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

3.7 KiB

bun-sse-engine

(no submodules)

Purpose

The bun-sse-engine module exports createBunSseEngine, the factory the host calls to mount Server-Sent Events on top of Bun.serve. The factory wires the seven collaborators (options, subscription bus, tenant cap, event buffer, instruments, handshake pipeline, active connection registry) into one cohesive engine value. Plugin authors never touch the internals — they hand the engine to the host's router which calls engine.acceptRequest(...) on every matching GET request.

Features

  • Single factory: validated options, broker- or in-memory event buffer selected automatically, shared subscription bus exposed for the WS engine to plug into.
  • acceptRequest({ request, path, topic }) runs the full handshake pipeline and returns the 200 streaming Response.
  • publish(envelope) shortcut: append to the buffer + broadcast on the bus in one call — used by plugins that publish in-process.
  • activeConnections map for observability dashboards (size, tenant distribution, oldest open).
  • buildReplayBuffer exposed for the conformance harness so the engine's ring-buffer semantics are pinned by the same test path every other engine uses.

Usage: Basic

import { createBunSseEngine } from "@suaveplan/api-sse-bun";

const engine = createBunSseEngine({
    authenticate: async () => ({
        tenantId: "acme" as never,
        principal: null,
    }),
});
// ...wire engine.acceptRequest into a Bun.serve handler...
engine.dispose();

Usage: Advanced

import { createBunSseEngine } from "@suaveplan/api-sse-bun";
import Bun from "bun";

const engine = createBunSseEngine({
    eventBufferSize: 512,
    eventBufferMs: 600_000,
    keepAliveCommentMs: 30_000,
    maxConnectionsPerTenant: 5_000,
    compressionEnabled: true,
    retryMs: 4_000,
    authenticate: async (req) => {
        const tenant = req.headers.get("x-tenant");
        if (tenant === null) return { tenantId: null, principal: null };
        return { tenantId: tenant as never, principal: null };
    },
});

Bun.serve({
    port: 0,
    async fetch(request) {
        const url = new URL(request.url);
        if (url.pathname === "/sse/audit") {
            return engine.acceptRequest({
                request: {
                    headers: request.headers,
                    url: request.url,
                    method: request.method,
                },
                path: url.pathname,
                topic: "audit",
            });
        }
        return new Response("not found", { status: 404 });
    },
});

API Reference

  • interface SseAcceptInputs — { request, path, topic }.
  • interface BunSseEngine — bus, cap, buffer, instruments, options, activeConnections, acceptRequest(inputs), publish(envelope), dispose(), buildReplayBuffer(opts?).
  • function createBunSseEngine(options: BunSseEngineOptions): BunSseEngine.
  • function aggregateConnectionSnapshot(active, plugin): readonly { plugin, path, tenant, value }[] — exported so OTel snapshots can be exercised in tests independent of the global meter provider.

Implementation Notes

The factory does not start any background timers — every long-lived resource (keep-alive scheduler, gzip stream) is owned by the handshake pipeline and bound to a single connection. dispose() is a synchronous best-effort: it cancels the bus and clears the active map but does not eagerly close every open stream (the HTTP transport handles that when the underlying socket closes). The connectionSnapshot for the OTel gauge aggregates active connections per (path, tenantId) so dashboards can plot capacity per route per tenant without re-walking the connection set inside the SDK callback.