113 lines
3.9 KiB
Markdown
113 lines
3.9 KiB
Markdown
# plugin
|
|
|
|
<!-- begin:auto-docs -->
|
|
_(no submodules)_
|
|
<!-- end:auto-docs -->
|
|
|
|
## Purpose
|
|
|
|
Defines `createEventsPlugin`, the factory that returns the
|
|
`PluginDescriptor<EventsPluginConfig>` consumed by `@suaveplan/api`'s
|
|
plugin registry. The factory Zod-validates options at the boundary,
|
|
constructs the runtime closure, and returns the four lifecycle hooks
|
|
(`onInit`, `onReady`, `onClose`) that the registry's topological
|
|
sorter wires up after the `database` dependency has booted.
|
|
|
|
The lifecycle is intentionally split: `onInit` reads the `db` slot
|
|
and registers the two repositories so downstream plugins (search,
|
|
audit, notifications) can resolve `eventRepository` /
|
|
`rsvpRepository` during their own `onInit`; `onReady` is the latest
|
|
phase the registry runs, so the `app` slot is guaranteed-populated
|
|
and the route mount is the last side effect before the host starts
|
|
listening.
|
|
|
|
## Features
|
|
|
|
- Zod-validated config at construction time — invalid options raise
|
|
`EventsPluginError(INVALID_CONFIG)` before the descriptor leaves
|
|
the factory.
|
|
- Lifecycle dedup — `onReady` refuses to mount when the configured
|
|
`basePath` already has routes registered, raising
|
|
`EventsPluginError(ROUTE_PREFIX_COLLISION)` instead of silently
|
|
shadowing the first mount.
|
|
- Forwards typed route events to the host's shared
|
|
`ctx.eventBus` (fixes the upstream "local emitter" smell flagged in
|
|
`enterprise/api-plugin-events.md`).
|
|
- `onClose` clears repository references so the GC can collect them.
|
|
|
|
## Usage: Basic Example
|
|
|
|
```ts
|
|
import { createEventsPlugin } from "@suaveplan/api-plugin-events";
|
|
import { quickstart } from "@suaveplan/api";
|
|
import { createDatabasePlugin } from "@suaveplan/api-plugin-database";
|
|
|
|
const app = await quickstart({
|
|
plugins: [
|
|
createDatabasePlugin({
|
|
/* ... */
|
|
}),
|
|
createEventsPlugin(), // mounts under /events by default
|
|
],
|
|
});
|
|
```
|
|
|
|
## Usage: Advanced Example
|
|
|
|
```ts
|
|
import { createEventsPlugin } from "@suaveplan/api-plugin-events";
|
|
|
|
const plugin = createEventsPlugin({
|
|
basePath: "/v1/events",
|
|
enableNotifications: false,
|
|
maxInvitesPerRequest: 25,
|
|
});
|
|
|
|
// Inspect the descriptor before handing it to the registry
|
|
console.log(plugin.name); // "events"
|
|
console.log(plugin.dependencies); // ["database"]
|
|
console.log(plugin.defaults.maxInvitesPerRequest); // 25
|
|
|
|
// Subscribe to lifecycle and domain events on the shared bus:
|
|
host.eventBus.on("events:ready", () => {
|
|
/* routes are now mounted */
|
|
});
|
|
host.eventBus.on("event:created", ({ eventId, creatorId }) => {
|
|
/* fan out to notifications, search index, audit log */
|
|
});
|
|
```
|
|
|
|
## API Reference
|
|
|
|
### `createEventsPlugin(options?: EventsPluginConfigInput): PluginDescriptor<EventsPluginConfig>`
|
|
|
|
Builds the plugin descriptor.
|
|
|
|
- **options** — Partial `EventsPluginConfig`. Each field has a
|
|
default; passing `{}` (or no argument) produces the default
|
|
config (`/events`, `enableNotifications: false`,
|
|
`maxInvitesPerRequest: 50`).
|
|
- **throws** `EventsPluginError(INVALID_CONFIG)` if `options` fails
|
|
`EventsPluginOptionsSchema`.
|
|
|
|
### Lifecycle hooks (attached to the returned descriptor)
|
|
|
|
- `onInit(ctx)` — pulls `db` from the registry, constructs the
|
|
repositories, registers `eventRepository` and `rsvpRepository`,
|
|
emits `events:initialized`.
|
|
- `onReady(ctx)` — pulls `app`, builds routes via
|
|
`createEventRoutes`, mounts under `config.basePath`, emits
|
|
`events:ready`.
|
|
- `onClose()` — clears repository refs.
|
|
|
|
## Implementation Notes
|
|
|
|
The plugin's `dependencies: ["database"]` declaration makes the
|
|
suaveplan registry topo-sort the events plugin strictly after the
|
|
database plugin. Without that ordering, `onInit` would fail because
|
|
`ctx.getService("db")` would resolve to `undefined`. The `app` slot
|
|
does not need to be declared as a dependency because every host
|
|
seeds it during registry construction (per the foundation patch
|
|
shipped on `2026-05-17`); the runtime check inside `onReady` is
|
|
defensive against custom hosts that don't pre-seed.
|