# sse-handler _(no submodules)_ ## Purpose Hono handler factory that turns a subscription on `SseService` into a streaming HTTP response framed in the `text/event-stream` wire format. Handles per-connection heartbeats, abort-on-disconnect via the request's `AbortSignal`, `Last-Event-ID` resume from the ring buffer, and backpressure honoring the configured `queueHighWaterMark` and `overflowPolicy`. Returns a standard `Response` whose body is a `ReadableStream` so any modern runtime (Bun, Node 18+, Cloudflare Workers, Deno) streams without an adapter shim. ## Features - Reads `:channel` from the route path; missing throws `SseError(SSE_CHANNEL_REQUIRED)` for the host's error handler to translate to 400. - Reads `Last-Event-ID` from request headers and forwards to `service.subscribe` for replay. - Optional `resolvePrincipalId(c)` lets consumers wire any auth stack (e.g. `c.get("user")?.id`) without this package depending on `@suaveplan/api-plugin-auth`. - Optional `requirePrincipal: true` raises `SseError(SSE_UNAUTHENTICATED)` (HTTP 401) when the resolver returns `undefined`. - Heartbeat scheduler writes a `: heartbeat\n\n` comment frame every `heartbeatIntervalMs` to keep idle connections alive through proxies; the timer is injectable (`setIntervalImpl`/`clearIntervalImpl`) for deterministic tests. - Backpressure handling: when the writable controller's `desiredSize` falls to zero or the in-memory backlog exceeds `queueHighWaterMark`, applies `overflowPolicy` and increments `sse.queue_overflow_total{policy, code}`. - Sets canonical response headers: `Content-Type: text/event-stream; charset=utf-8`, `Cache-Control: no-cache, no-transform`, `X-Accel-Buffering: no`, `Connection: keep-alive`. ## Usage: Basic ```ts import { Hono } from "hono"; import { createSseRoute } from "@suaveplan/api-plugin-sse/middleware/sse-handler"; const app = new Hono(); app.get( "/sse/:channel", createSseRoute(sseService, { heartbeatIntervalMs: 30_000, queueHighWaterMark: 256, overflowPolicy: "drop-oldest", }) ); ``` ## Usage: Advanced ```ts import { Hono } from "hono"; import { createSseRoute } from "@suaveplan/api-plugin-sse/middleware/sse-handler"; import { requireAuth } from "@suaveplan/api-plugin-auth/middleware/require-auth"; const app = new Hono(); // Compose with the auth plugin: require an authenticated principal, // scope the principalId to the subscription cap. app.get( "/sse/:channel", requireAuth(), createSseRoute(sseService, { heartbeatIntervalMs: 15_000, queueHighWaterMark: 128, overflowPolicy: "drop-newest", resolvePrincipalId: (c) => c.get("user")?.id, requirePrincipal: true, }) ); ``` ## API Reference - `interface CreateSseRouteOptions` — `{ heartbeatIntervalMs, queueHighWaterMark, overflowPolicy, resolvePrincipalId?, requirePrincipal?, setIntervalImpl?, clearIntervalImpl? }`. - `createSseRoute(service: SseService, options: CreateSseRouteOptions): Handler` — returns a Hono handler. ## Implementation Notes - The response body is constructed as `new ReadableStream` with an async producer running inside `start(controller)`. The producer drains `handle.events` via `for await`, frames each event with `formatSseFrame`, and pushes the encoded bytes through the controller. - Cleanup is centralised in a single async closure that clears the heartbeat interval, calls `handle.close()`, and records the `sse.connection_duration_ms` histogram. It is invoked from both the iterator's `finally` branch and the stream's `cancel` callback so abort and natural end converge on the same code path. - Backpressure is detected by checking `controller.desiredSize`; values of `null` (no high-water mark) and positive integers are treated as "not full". Zero or negative trigger the overflow policy. - Heartbeat interval uses native `setInterval` by default; tests inject a fake to drive the timer deterministically (matching the FakeClock pattern used elsewhere ).