Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
# @suaveplan/api-plugin-event-bus - Typed event-bus plugin for @suaveplan/api. Wraps @suaveplan/events (pub/sub, async dispatch, interceptors, bounded-queue backpressure, idempotent listener wrapper) as a single definePlugin descriptor. Registers a typed eventBusService in the host ServiceRegistry, installs request middleware that exposes a tenant-scoped publish/subscribe handle on c.get("eventBus"), and (optionally) mounts 3 auth-gated OpenAPI routes for publish/list/delete. Per-tenant topic scoping isolates publishes across tenants by prefixing topics with tenant:<id>:. Server-tier — composes cleanly with @suaveplan/api-plugin-auth + @suaveplan/api-plugin-tenant.
|
||||
|
||||
Typed event-bus plugin for `@suaveplan/api`. Wraps `@suaveplan/events`
|
||||
(pub/sub, async dispatch, interceptors, bounded-queue backpressure,
|
||||
idempotent listener wrapper) as a single `definePlugin` consumer so
|
||||
apps composing `@suaveplan/api` get a working, instrumented,
|
||||
per-tenant scoped event bus by adding one entry to their plugins
|
||||
array.
|
||||
|
||||
Naming note: `enterprise/api-plugin-events.md` documents a *different*
|
||||
package (social calendar / RSVP / meetup feed) that is deferred to a
|
||||
later wave. This `event-bus` plugin is the in-process event-bus
|
||||
integration wrapper.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [error-codes](./src/error-codes/error-codes.md) — Declares the stable error-code registry consumed by every failure path in `@suaveplan/api-plugin-event-bus`. The regist…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — Declares the stable error-code registry consumed by every failure path in `@suaveplan/api-plugin-event-bus`. The regist…
|
||||
- [context-augmentation](./src/middleware/context-augmentation.md) — Adds a typed `eventBus` slot to Hono's `ContextVariableMap` so request handlers see `c.get("eventBus")` resolve to `Sco…
|
||||
- [request-bus](./src/middleware/request-bus.md) — Provides the Hono middleware that installs a `ScopedEventBusHandle` on every request via `c.set("eventBus", handle)`. T…
|
||||
- [otel](./src/otel/instruments.md) — Exposes the five OpenTelemetry instruments emitted from the plugin's production paths and a lazy singleton (`getEventBu…
|
||||
- [instruments](./src/otel/instruments.md) — Exposes the five OpenTelemetry instruments emitted from the plugin's production paths and a lazy singleton (`getEventBu…
|
||||
- [plugin](./src/plugin/plugin.md) — Top-level factory exported as the package's main entry point. Builds a typed `PluginDescriptor<EventBusPluginConfig>` p…
|
||||
- [plugin](./src/plugin/plugin.md) — Top-level factory exported as the package's main entry point. Builds a typed `PluginDescriptor<EventBusPluginConfig>` p…
|
||||
- [routes](./src/routes/build-routes.md) — Builds the three `PluginRouteEntry` objects the plugin mounts on the host Hono app + `OpenAPIRegistry` via `definePlugi…
|
||||
- [build-routes](./src/routes/build-routes.md) — Builds the three `PluginRouteEntry` objects the plugin mounts on the host Hono app + `OpenAPIRegistry` via `definePlugi…
|
||||
- [options](./src/schemas/options.md) — Boundary Zod schema validated by `createEventBusPlugin(options)` at the factory entry point. Invalid input produces an…
|
||||
- [routes](./src/schemas/routes.md) — Boundary Zod schemas validated by the three OpenAPI routes the plugin optionally mounts: `POST /event-bus/publish`, `GE…
|
||||
- [backpressure](./src/services/backpressure.md) — Wraps `@suaveplan/events`' `BoundedQueue` with a small adapter that treats the queue as a pure accounting primitive: a…
|
||||
- [event-bus-service](./src/services/event-bus-service.md) — Builds the canonical `EventBusService` registered in the host `ServiceRegistry` by `createEventBusPlugin`. The service…
|
||||
- [helpers](./src/services/helpers.md) — Holds the pure helpers extracted from the main service body so the service itself stays under the 300-line cap and each…
|
||||
- [idempotency-cache](./src/services/idempotency-cache.md) — Provides the default `IdempotencyCache` implementation used by `eventBusService` to suppress duplicate publishes. The c…
|
||||
- [registry-augmentation](./src/services/registry-augmentation.md) — Augments `@suaveplan/api-plugin/service-registry`'s `ServiceRegistry` interface to advertise the `eventBusService` slot…
|
||||
- [topic](./src/topic/topic.md) — Pure functions for validating event-bus topics and projecting them into their tenant-scoped form. Every `publish` and `…
|
||||
- [topic](./src/topic/topic.md) — Pure functions for validating event-bus topics and projecting them into their tenant-scoped form. Every `publish` and `…
|
||||
- [types](./src/types/types.md) — Declares the two top-level shapes consumed by external callers of the plugin: `EventBusPluginOptions` (what `createEven…
|
||||
- [types](./src/types/types.md) — Declares the two top-level shapes consumed by external callers of the plugin: `EventBusPluginOptions` (what `createEven…
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/api-plugin-event-bus
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
```ts
|
||||
import { defineApp } from "@suaveplan/api";
|
||||
import { createEventBusPlugin } from "@suaveplan/api-plugin-event-bus";
|
||||
|
||||
const app = await defineApp()
|
||||
.name("my-api")
|
||||
.use(createEventBusPlugin({}))
|
||||
.start();
|
||||
```
|
||||
|
||||
`createEventBusPlugin` returns a typed `PluginDescriptor`. The host's
|
||||
`PluginRegistry` calls `onInit(ctx)` which:
|
||||
|
||||
1. Resolves `app` and (optionally) `openAPIRegistry` from the host
|
||||
`ServiceRegistry`.
|
||||
2. Constructs an `EventBus` from `@suaveplan/events` and applies
|
||||
configured interceptors, bounded-queue backpressure, and idempotent
|
||||
listener wiring.
|
||||
3. Registers `eventBusService` in the typed `ServiceRegistry`.
|
||||
4. Auto-prefixes topics with `tenant:<id>:` when a tenant slot is
|
||||
present on the request (provided by `@suaveplan/api-plugin-tenant`).
|
||||
5. Installs request middleware so every handler sees a
|
||||
`ScopedEventBusHandle` under `c.get("eventBus")`.
|
||||
6. Optionally mounts 3 OpenAPI routes (`POST /event-bus/publish`,
|
||||
`GET /event-bus/subscriptions`, `DELETE /event-bus/subscriptions/:id`),
|
||||
auth-gated by `requireAuth()` when `requireAuthForRoutes: true`
|
||||
(the default).
|
||||
|
||||
## In-handler usage
|
||||
|
||||
```ts
|
||||
app.post("/checkout", async (c) => {
|
||||
const bus = c.get("eventBus");
|
||||
const result = await bus.publish("order.created", {
|
||||
id: "o1",
|
||||
total: 9.99,
|
||||
});
|
||||
return c.json({ delivered: result.delivered });
|
||||
});
|
||||
|
||||
app.get("/me/subs", (c) => {
|
||||
const bus = c.get("eventBus");
|
||||
return c.json({ subscriptions: bus.listSubscriptions() });
|
||||
});
|
||||
```
|
||||
|
||||
The handle is automatically scoped to the request's tenant — a
|
||||
publish on tenant A's request never reaches a subscriber registered
|
||||
through tenant B's handle.
|
||||
|
||||
## Per-tenant isolation
|
||||
|
||||
When `tenantIsolation: true` (the default) the service prefixes every
|
||||
publish and subscribe topic with `tenant:<id>:` extracted from
|
||||
`c.get("tenant")?.id`. The HTTP routes follow the same rule and
|
||||
unscope topics on the way out so wire responses never leak the
|
||||
internal prefix. Cross-tenant `unsubscribe(id)` attempts return HTTP
|
||||
404 to avoid leaking subscription-id ownership.
|
||||
|
||||
Set `tenantIsolation: false` for single-tenant deployments — every
|
||||
publish flows on the bare topic.
|
||||
|
||||
## Backpressure
|
||||
|
||||
Every publish flows through a `BoundedQueue` from `@suaveplan/events`
|
||||
configured by `backpressure: { capacity, policy }`. The four
|
||||
`QueuePolicy` values (`drop-oldest`, `drop-newest`, `block`, `throw`)
|
||||
are forwarded verbatim. Under `drop-oldest`/`drop-newest`, the
|
||||
`PublishResult.dropped` field reports overflow events alongside an
|
||||
`event_bus.dropped_total{policy}` counter increment. Under
|
||||
`policy: "throw"`, overflow rejects the publish promise with
|
||||
`EventBusError(EVENT_BUS.BACKPRESSURE_DROPPED)`.
|
||||
|
||||
## Idempotency
|
||||
|
||||
A subscription registered with `idempotencyKey: (payload) => string`
|
||||
fires its handler at most once per derived key. The plugin-level
|
||||
`idempotency.enabled: true` flag activates the same wrapper for every
|
||||
subscriber that does not supply its own key function — the default
|
||||
derivation is `JSON.stringify(payload)`. Duplicates increment
|
||||
`event_bus.duplicates_total`.
|
||||
|
||||
## OTel metrics
|
||||
|
||||
| Metric | Type | Labels |
|
||||
|---|---|---|
|
||||
| `event_bus.published_total` | Counter | `outcome`, `topic_root` |
|
||||
| `event_bus.dropped_total` | Counter | `policy` |
|
||||
| `event_bus.duplicates_total` | Counter | — |
|
||||
| `event_bus.subscriptions_active` | UpDownCounter | — |
|
||||
| `event_bus.publish_duration_ms` | Histogram | — |
|
||||
|
||||
## Error codes
|
||||
|
||||
`EVENT_BUS_CODES` exports 8 stable codes: `INVALID_INPUT`,
|
||||
`INVALID_TOPIC`, `SERVICE_NOT_AVAILABLE`, `ROUTE_PREFIX_COLLISION`,
|
||||
`SUBSCRIPTION_NOT_FOUND`, `BACKPRESSURE_DROPPED`, `TENANT_MISMATCH`,
|
||||
`PUBLISH_FAILED`. See `src/error-codes/error-codes.md` for the full
|
||||
table and `context.statusCode` mapping.
|
||||
|
||||
## Modules
|
||||
|
||||
- [`src/error-codes/error-codes.md`](./src/error-codes/error-codes.md)
|
||||
— `EVENT_BUS_CODES`, `EventBusError`, `EventBusErrorCode`.
|
||||
- [`src/middleware/context-augmentation.md`](./src/middleware/context-augmentation.md)
|
||||
— Hono `ContextVariableMap.eventBus` declaration.
|
||||
- [`src/middleware/request-bus.md`](./src/middleware/request-bus.md)
|
||||
— `requestBusMiddleware`, `bindScopedHandle`, `extractTenantId`.
|
||||
- [`src/otel/instruments.md`](./src/otel/instruments.md) — 5-metric
|
||||
bundle + lazy singleton + reset helper.
|
||||
- [`src/plugin/plugin.md`](./src/plugin/plugin.md) — `createEventBusPlugin`
|
||||
factory + 6-step `onInit` lifecycle.
|
||||
- [`src/routes/build-routes.md`](./src/routes/build-routes.md) — 3
|
||||
OpenAPI route entries (publish / list / delete).
|
||||
- [`src/schemas/options.md`](./src/schemas/options.md) — boundary
|
||||
Zod schema for `EventBusPluginOptions`.
|
||||
- [`src/schemas/routes.md`](./src/schemas/routes.md) — Zod schemas
|
||||
for the HTTP route surface.
|
||||
- [`src/services/backpressure.md`](./src/services/backpressure.md) —
|
||||
bounded-queue accounting primitive.
|
||||
- [`src/services/event-bus-service.md`](./src/services/event-bus-service.md)
|
||||
— `EventBusService` factory + per-tenant scoping + idempotency.
|
||||
- [`src/services/helpers.md`](./src/services/helpers.md) — id
|
||||
generation, error projection, OTel emission helpers.
|
||||
- [`src/services/idempotency-cache.md`](./src/services/idempotency-cache.md)
|
||||
— bounded set-based `IdempotencyCache` implementation.
|
||||
- [`src/services/registry-augmentation.md`](./src/services/registry-augmentation.md)
|
||||
— `ServiceRegistry` slot declaration for `eventBusService`.
|
||||
- [`src/topic/topic.md`](./src/topic/topic.md) — `EVENT_TOPIC_PATTERN`
|
||||
- `scopeTopic` / `unscopeTopic` / `assertValidTopic`.
|
||||
- [`src/types/types.md`](./src/types/types.md) — public option /
|
||||
config interfaces.
|
||||
|
||||
## Subpath exports
|
||||
|
||||
`.`, `./plugin/plugin`, `./middleware/request-bus`,
|
||||
`./middleware/context-augmentation`, `./services/event-bus-service`,
|
||||
`./services/registry-augmentation`, `./schemas/options`,
|
||||
`./schemas/routes`, `./routes/build-routes`, `./topic/topic`,
|
||||
`./error-codes/error-codes`, `./otel/instruments`, `./types/types`.
|
||||
|
||||
## Peer deps
|
||||
|
||||
- `hono`, `@hono/zod-openapi`, `zod` — supplied by the host
|
||||
application.
|
||||
- `@suaveplan/api-plugin-auth` — used by `requireAuth()` route gate
|
||||
when `requireAuthForRoutes: true` (default).
|
||||
|
||||
Reference in New Issue
Block a user