Files
skills/docs/api-stack/api/api-plugin-sse/README.md
2026-05-29 08:20:56 +01:00

110 lines
7.5 KiB
Markdown

# @suaveplan/api-plugin-sse - Server-Sent Events plugin for @suaveplan/api. Registers a typed sseService in the host ServiceRegistry, mounts a Hono streamSSE handler with per-connection heartbeat, AbortSignal threading, backpressure, and Last-Event-ID resume from a bounded per-channel ring buffer. Server-tier.
Server-Sent Events plugin for `@suaveplan/api`. Registers a typed `sseService` in the host `ServiceRegistry`, mounts a Hono streaming handler at `GET /sse/:channel`, and ships with per-connection heartbeat, `AbortSignal` threading on client disconnect, `Last-Event-ID` resume from a bounded per-channel ring buffer, and backpressure handling with `drop-oldest` / `drop-newest` overflow policies.
<!-- begin:auto-docs -->
## Documentation tree
- [buffer](./src/buffer/ring-buffer.md) — Bounded per-channel ring buffer that powers `Last-Event-ID` resume. The SSE service appends every broadcast to the buff…
- [ring-buffer](./src/buffer/ring-buffer.md) — Bounded per-channel ring buffer that powers `Last-Event-ID` resume. The SSE service appends every broadcast to the buff…
- [error-codes](./src/error-codes/error-codes.md) — Stable error-code registry and `SseError` class for `@suaveplan/api-plugin-sse`. Every failure path in the plugin raise…
- [error-codes](./src/error-codes/error-codes.md) — Stable error-code registry and `SseError` class for `@suaveplan/api-plugin-sse`. Every failure path in the plugin raise…
- [fanout](./src/fanout/in-process.md) — Single-process publish/subscribe bus consumed by the SSE service to deliver every broadcast to every active subscriber…
- [in-process](./src/fanout/in-process.md) — Single-process publish/subscribe bus consumed by the SSE service to deliver every broadcast to every active subscriber…
- [format](./src/formatter/format.md) — Serialises `SseEvent` objects into the `text/event-stream` wire format defined by the WHATWG HTML Standard so the SSE h…
- [parse](./src/formatter/parse.md) — Async streaming parser for `text/event-stream`. Decodes a `ReadableStreamDefaultReader<Uint8Array>` into a sequence of…
- [middleware](./src/middleware/sse-handler.md) — Hono handler factory that turns a subscription on `SseService` into a streaming HTTP response framed in the `text/event…
- [sse-handler](./src/middleware/sse-handler.md) — Hono handler factory that turns a subscription on `SseService` into a streaming HTTP response framed in the `text/event…
- [otel](./src/otel/instruments.md) — Provides the canonical OpenTelemetry instrument bundle emitted by the SSE plugin. Counters and a histogram cover the fo…
- [instruments](./src/otel/instruments.md) — Provides the canonical OpenTelemetry instrument bundle emitted by the SSE plugin. Counters and a histogram cover the fo…
- [plugin](./src/plugin/plugin.md) — Factory producing a typed `PluginDescriptor` for the SSE plugin. Wraps the `@suaveplan/api-plugin` `definePlugin` found…
- [plugin](./src/plugin/plugin.md) — Factory producing a typed `PluginDescriptor` for the SSE plugin. Wraps the `@suaveplan/api-plugin` `definePlugin` found…
- [registry](./src/registry/service-registry.md) — TypeScript module augmentation declaring the `sseService` slot on the `@suaveplan/api-plugin/service-registry` `Service…
- [service-registry](./src/registry/service-registry.md) — TypeScript module augmentation declaring the `sseService` slot on the `@suaveplan/api-plugin/service-registry` `Service…
- [event](./src/schemas/event.md) — Zod schema validating the `SseEvent` shape accepted by `SseService.broadcast`. Ensures application payloads cannot bypa…
- [options](./src/schemas/options.md) — Zod schema validating `SsePluginOptions` at the factory boundary. `createSsePlugin` calls `SsePluginOptionsSchema.safeP…
- [services](./src/services/service.md) — Core SSE service registered under the `sseService` slot in the host `ServiceRegistry`. Owns subscription lifecycle (id…
- [service](./src/services/service.md) — Core SSE service registered under the `sseService` slot in the host `ServiceRegistry`. Owns subscription lifecycle (id…
<!-- end:auto-docs -->
## Install
```bash
bun add @suaveplan/api-plugin-sse
```
## Quick start
```ts
import { Hono } from "hono";
import { createPluginRegistry } from "@suaveplan/api-plugin";
import { createSsePlugin } from "@suaveplan/api-plugin-sse";
const app = new Hono();
const registry = createPluginRegistry({ seedServices: { app } });
await registry.register(createSsePlugin({ heartbeatIntervalMs: 15_000 }));
await registry.init();
const sse = registry.getService("sseService");
setInterval(() => {
sse?.broadcast("notifications", { data: { ts: Date.now() } });
}, 1_000);
export default app;
```
A browser client connects with the native `EventSource`:
```ts
const source = new EventSource("/sse/notifications");
source.addEventListener("message", (evt) => console.log(evt.data));
```
## The 3-way plug-and-play contract
This package follows the canonical API-plugin pattern pioneered by `@suaveplan/api-plugin-auth`: a plugin is a `definePlugin` consumer that owns its (1) Zod schemas at the boundary, (2) optional default route registration, and (3) typed `ServiceRegistry` slot. Mount or compose with any other `@suaveplan/api-plugin-*` package by passing them to the same plugin registry.
## Modules
- [`src/error-codes/error-codes.md`](./src/error-codes/error-codes.md) — stable `SSE_*` codes + `SseError`
- [`src/buffer/ring-buffer.md`](./src/buffer/ring-buffer.md) — bounded per-channel ring buffer for `Last-Event-ID` resume
- [`src/fanout/in-process.md`](./src/fanout/in-process.md) — single-process pub/sub bus
- [`src/formatter/format.md`](./src/formatter/format.md) — SSE wire-format serializer
- [`src/formatter/parse.md`](./src/formatter/parse.md) — streaming parser for SSE clients
- [`src/schemas/options.md`](./src/schemas/options.md) — Zod schema for plugin options
- [`src/schemas/event.md`](./src/schemas/event.md) — Zod schema for events
- [`src/otel/instruments.md`](./src/otel/instruments.md) — OpenTelemetry instruments
- [`src/services/service.md`](./src/services/service.md) — `SseService` registered under `sseService`
- [`src/middleware/sse-handler.md`](./src/middleware/sse-handler.md) — Hono streaming handler factory
- [`src/plugin/plugin.md`](./src/plugin/plugin.md) — `createSsePlugin` factory
- [`src/registry/service-registry.md`](./src/registry/service-registry.md) — `ServiceRegistry` augmentation
## Configuration
| Option | Default | Description |
|---|---|---|
| `heartbeatIntervalMs` | `30_000` | Heartbeat comment-frame cadence per connection |
| `routePrefix` | `"/sse"` | Mount path for the default route (`:channel` appended) |
| `attachDefaultRoute` | `true` | Skip route registration to mount manually |
| `retentionPerChannel` | `100` | Ring-buffer size per channel for resume |
| `maxConnectionsPerPrincipal` | `16` | Per-principal connection cap |
| `maxTotalConnections` | `10_000` | Global connection cap |
| `queueHighWaterMark` | `256` | Per-connection backlog threshold |
| `overflowPolicy` | `"drop-oldest"` | `drop-oldest` or `drop-newest` |
| `idGenerator` | `crypto.randomUUID()` | Event id source |
## Observability
- `sse.active_connections` — current open connections (UpDownCounter, `channel` attribute)
- `sse.broadcasts_total{channel}` — broadcasts per channel
- `sse.heartbeats_total` — heartbeat frames written
- `sse.message_bytes_total` — bytes written to clients
- `sse.queue_overflow_total{policy,code}` — events dropped under backpressure
- `sse.connection_duration_ms` — per-connection lifetime histogram
## License
MIT