- name
- kickjs-add-plugin
- description
- Use when scaffolding a feature that bundles modules + DI + middleware + adapters together (auth, monitoring suite, multi-tenant scaffolding).
<!-- Generated by `kick g agents` for settings-api. Edits are overwritten on the next refresh; keep customisation in a SKILL.local.md alongside. -->
**When plugin > adapter**: a plugin is the right answer when the integration ships **more than one** of: a module, a DI binding, middleware, or another adapter. If you have a single hook (`beforeStart`) and no other contributions, use `defineAdapter` instead.
**Canonical shape**:
```ts
import { definePlugin } from '@forinda/kickjs'
export const AuthPlugin = definePlugin({
name: 'AuthPlugin',
defaults: { tokenTtl: '1h' },
build: (config, { name }) => ({
modules: () => [AuthModule()],
adapters: () => [JwtAdapter({ ttl: config.tokenTtl })],
middleware: () => [requestIdMiddleware()],
register(container) {
container.registerFactory(TOKEN_SIGNER, () => createSigner(config))
},
contributors() {
return [LoadCurrentUser.registration]
},
onReady({ server }) {
log.info(`AuthPlugin listening on port ${server.address().port}`)
},
}),
})
// In bootstrap:
bootstrap({ plugins: [AuthPlugin({ tokenTtl: env.TOKEN_TTL })] }) // <-- parens
```
**Inline plugin literal** — the canonical answer for one-off DI bindings. There's no top-level `register:` on `bootstrap` itself:
```ts
bootstrap({
plugins: [{ name: 'vector-store', register(c) { c.registerInstance(VECTOR_STORE, store) } }],
})
```
**Execution order** (memorize):
plugin `register()` → plugin `middleware()` → plugin `modules()` + user modules → plugin `adapters()` + user adapters → server listens → plugin `onReady()`.
**Static vs dynamic modules**: `modules()` returning an array is introspectable (Swagger, DevTools see it). `setup(registry)` is imperative — pick the latter when the module set depends on resolved config.
**Multi-instance** — `.scoped('users', { url })`; derive unique DI tokens from `ctx.name` inside `build`:
```ts
build: (config, { name }) => ({
register(c) {
c.registerInstance(createToken(`cache/${name}`), client)
},
})
```
**Precedence**: plugin contributors land at `'adapter'` precedence — beat global, lose to module/class/method same-key.
**Red flags**:
- `bootstrap({ plugins: [AuthPlugin] })` — passed factory. Call it: `AuthPlugin()`.
- Reaching for a plugin when an adapter would do (no modules, no DI bindings, no contributors) — overkill; use `defineAdapter()`.
- `.async()` plugin that depends on `modules()` / `middleware()` / `adapters()` / `contributors()` — those are dropped. `.async()` only resolves `register()` + `onReady()`.
- Confusing CLI plugins (`defineCliPlugin` from `@forinda/kickjs-cli`) with runtime plugins (`definePlugin` from `@forinda/kickjs`) — different surfaces, different registration sites.
- `dependsOn: ['SomePlugin']` referring to a plugin not in the boot list — throws `MissingMountDepError` at boot.
**Nuances**:
- `definition` is `Object.freeze`'d metadata; useful for version checks (`compare(AuthPlugin.definition.version, '1.2.0')`) — not mountable.
View on GitHub