first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
# 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.