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.
