Files
2026-05-27 23:29:53 +01:00
..
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00

@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 — Declares the stable error-code registry consumed by every failure path in @suaveplan/api-plugin-event-bus. The regist…
    • error-codes — Declares the stable error-code registry consumed by every failure path in @suaveplan/api-plugin-event-bus. The regist…
  • context-augmentation — Adds a typed eventBus slot to Hono's ContextVariableMap so request handlers see c.get("eventBus") resolve to `Sco…
  • request-bus — Provides the Hono middleware that installs a ScopedEventBusHandle on every request via c.set("eventBus", handle). T…
  • otel — Exposes the five OpenTelemetry instruments emitted from the plugin's production paths and a lazy singleton (`getEventBu…
    • instruments — Exposes the five OpenTelemetry instruments emitted from the plugin's production paths and a lazy singleton (`getEventBu…
  • plugin — Top-level factory exported as the package's main entry point. Builds a typed PluginDescriptor<EventBusPluginConfig> p…
    • plugin — Top-level factory exported as the package's main entry point. Builds a typed PluginDescriptor<EventBusPluginConfig> p…
  • routes — Builds the three PluginRouteEntry objects the plugin mounts on the host Hono app + OpenAPIRegistry via `definePlugi…
    • build-routes — Builds the three PluginRouteEntry objects the plugin mounts on the host Hono app + OpenAPIRegistry via `definePlugi…
  • options — Boundary Zod schema validated by createEventBusPlugin(options) at the factory entry point. Invalid input produces an…
  • routes — Boundary Zod schemas validated by the three OpenAPI routes the plugin optionally mounts: POST /event-bus/publish, `GE…
  • backpressure — Wraps @suaveplan/events' BoundedQueue with a small adapter that treats the queue as a pure accounting primitive: a…
  • event-bus-service — Builds the canonical EventBusService registered in the host ServiceRegistry by createEventBusPlugin. The service…
  • helpers — Holds the pure helpers extracted from the main service body so the service itself stays under the 300-line cap and each…
  • idempotency-cache — Provides the default IdempotencyCache implementation used by eventBusService to suppress duplicate publishes. The c…
  • registry-augmentation — Augments @suaveplan/api-plugin/service-registry's ServiceRegistry interface to advertise the eventBusService slot…
  • topic — Pure functions for validating event-bus topics and projecting them into their tenant-scoped form. Every publish and `…
    • topic — Pure functions for validating event-bus topics and projecting them into their tenant-scoped form. Every publish and `…
  • types — Declares the two top-level shapes consumed by external callers of the plugin: EventBusPluginOptions (what `createEven…
    • types — Declares the two top-level shapes consumed by external callers of the plugin: EventBusPluginOptions (what `createEven…

Install

bun add @suaveplan/api-plugin-event-bus

Quick start

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

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

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).