CORE CONCEPTS
Module Plugins
ModulePlugin is the generic extension point that lets ecosystem packages — @rasenganjs/ws, @rasenganjs/queue — add their own field to defineModule() (gateways, queues) without rasengan-server core knowing what either concept is.
bootstrap((app) => { app.registerPlugin(createWsPlugin()); // claims the "gateways" key app.registerPlugin(createQueuePlugin()); // claims the "queues" key app.registerModule(appModule); // may declare gateways/queues on any sub-module });
Order between registerPlugin() and registerModule() calls doesn't matter — both just need to happen before compile() runs (which bootstrap() triggers internally).
Why This Exists
Core only knows about name, prefix, middlewares, imports, controllers, and providers on a ModuleConfig. Any other key is opaque to core and must be claimed by a registered ModulePlugin, or compile() throws:
[rasengan-server] Module declares unknown key "gateways" with no matching
plugin. If this is meant to be handled by an ecosystem package (e.g.
`@rasenganjs/ws` for "gateways"), call `app.registerPlugin(...)` into
`bootstrap()`.
This is deliberate — a forgotten registerPlugin() call fails loudly instead of silently doing nothing.
ModulePlugin Interface
interface ModulePlugin { key: string; // the ModuleConfig field this plugin claims, e.g. 'gateways' register( app: ServerApp, container: ContainerView, mod: ModuleConfig, value: unknown ): void; asProviders?(value: unknown): ProviderLike[]; }
register()— called once per flattened module that declares a non-undefinedvalue atkey, after providers are registered. Useapp's public methods (e.g.app.websocket()) to wire in whatever the key represents.asProviders()(optional) — extracts real DI-provider tokens hiding insidevalue. When present,compile()merges those tokens into the module'sprovidersbefore registration, visibility computation, and eager resolution — so a plugin-declared class (aGateway, aQueue) becomes a real, exportable, eagerly-resolved singleton exactly like a hand-declared provider.
Writing Your Own Plugin
import type { ModulePlugin } from '@rasenganjs/server'; function createSchedulerPlugin(): ModulePlugin { return { key: 'schedulers', asProviders: (value) => value as ProviderLike[], // schedulers extend Provider register(app, container, mod, value) { const schedulerClasses = value as SchedulerClass[]; for (const SchedulerClass of schedulerClasses) { const scheduler = container.resolve(SchedulerClass); scheduler.start(); } }, }; }
bootstrap((app) => { app.registerPlugin(createSchedulerPlugin()); app.registerModule( defineModule({ schedulers: [ReportScheduler], // an unrecognized key without the plugin above }) ); });
Only one plugin may claim a given key — registering a second plugin for the same key throws immediately.
