# `SseConnectionRegistry` + `createConnectionRegistry` _(no submodules)_ ## Purpose `SseConnectionRegistry` is the multi-index registry for active SSE connections. Engines call `add` on handshake, `remove` on disconnect, and the host fans out broadcasts via `lookupByTenant` / `lookupByTopic`. Lookups are O(1) by design — the registry keeps a parallel `tenantId → Set` and `(topic, tenantId) → Set` index so broadcast paths avoid scanning the full connection set. `remove` is idempotent: engines can call it from both the graceful-close path and a finaliser without risk of throwing or double-decrementing tenant counts. This matches the shape of `WsConnectionRegistry` in `@suaveplan/api-types/ws` so engines that bridge both transports can share lifecycle code. ## Features - O(1) lookup by `tenantId` via `lookupByTenant`. - O(1) lookup by `(topic, tenantId)` via `lookupByTopic` — cross-tenant fan-out is impossible by construction. - `countByTenant` powers the per-principal cap check at handshake. - `add(conn, topics)` is mutation-only; topics MUST be supplied at `add` time (no `subscribe(conn, topic)` here — topic subscriptions live with the SSE manifest). ## Usage: Basic ```ts import { createConnectionRegistry } from "@suaveplan/api-sse/connection-registry/connection-registry"; const reg = createConnectionRegistry(); declare const connection: import("@suaveplan/api-sse/connection/connection").SseConnection; reg.add(connection, ["orders", "notifications"]); const audience = reg.lookupByTopic("orders", connection.tenantId); ``` ## Usage: Advanced ```ts import { createConnectionRegistry } from "@suaveplan/api-sse/connection-registry/connection-registry"; const reg = createConnectionRegistry(); function broadcast( tenantId: import("@suaveplan/api-types/tenant").TenantId, topic: string, event: import("@suaveplan/api-sse/event/event").SseEvent ) { return Promise.all( reg.lookupByTopic(topic, tenantId).map((conn) => conn.send(event)) ); } // idempotent close reg.remove(conn); reg.remove(conn); // does not throw ``` ## API Reference - `createConnectionRegistry(): SseConnectionRegistry` - `interface SseConnectionRegistry` - `add(conn: SseConnection, topics?: readonly string[]): void` - `remove(conn: SseConnection): void` (idempotent) - `lookupByTenant(tenantId: TenantId): readonly SseConnection[]` - `lookupByTopic(topic: string, tenantId: TenantId): readonly SseConnection[]` - `countByTenant(tenantId: TenantId): number` - `size: number` ## Implementation Notes The implementation uses a `Set` per tenant and per `(topic, tenantId)` key for O(1) `add` / `delete`. Topics-per-connection are stashed in a `WeakMap` so the registry never holds a strong reference past `remove` — the connection becomes eligible for GC as soon as the engine drops its own handle. The `(topic, tenantId)` composite key uses a space separator (`${tenantId} ${topic}`) because branded `TenantId` cannot contain spaces by construction (resolved at the trust boundary).