# @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::. 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. ## 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` p… - [plugin](./src/plugin/plugin.md) — Top-level factory exported as the package's main entry point. Builds a typed `PluginDescriptor` 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… ## 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::` 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::` 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).