110 lines
7.5 KiB
Markdown
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
|
|
|