Plugin development example

Try it
This example runs inside the Setu-TS repository. To start a similar project:
npm create @setu-ts my-app -- --template rest
cd my-app && npm install
npm run start:dev
The commands further down run inside a clone of the repository.

Shows a small but complete plugin: a custom capability token, an internal service, a route that resolves the service, and a test using @setu-ts/testing.

pnpm --dir apps/plugin-development run start
pnpm --dir apps/plugin-development run smoke
pnpm --dir apps/plugin-development run test

Read the example deep dive for the concept and source walkthrough.

Read, in order

src/plugins/greeting.plugin.ts

import { createCapabilityToken } from '@setu-ts/common';
import type { IPlugin } from '@setu-ts/common';

/** The capability token the plugin registers its service under. */
export const GREETING = createCapabilityToken('example.greeting');

/** The service the plugin provides. */
export interface IGreetingService {
  greet(name: string): string;
}

class GreetingService implements IGreetingService {
  greet(name: string): string {
    return `Hello, ${name}!`;
  }
}

/** A complete custom plugin: it provides a capability and a route that resolves it. */
export function GreetingPlugin(): IPlugin {
  return {
    name: 'example-greeting-plugin',
    version: '0.1.0',
    provides: [GREETING],
    register(plugin): void {
      plugin.services.register<IGreetingService>(GREETING, new GreetingService());
      plugin.router.get('/greet/:name', (ctx) => {
        const greetings = ctx.services.get<IGreetingService>(GREETING);
        return ctx.response.json({ message: greetings.greet(ctx.params.name) });
      });
    },
  };
}

smoke.ts

import assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import type { IKernelApplication } from '@setu-ts/kernel';
import { createApp } from './setu.config.ts';

describe('plugin-development example', () => {
  let app: IKernelApplication;

  before(async () => {
    app = createApp();
    await app.start();
  });

  after(async () => {
    await app.stop();
  });

  it("serves the plugin's route through the capability it registered", async () => {
    const response = await app.inject({ method: 'GET', url: '/greet/Ada' });

    assert.equal(response.statusCode, 200);
    assert.deepEqual(response.json(), { message: 'Hello, Ada!' });
  });
});