first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
# `bun-sse-engine`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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
```ts
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
```ts
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.