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

3.0 KiB

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<conn> and (topic, tenantId) → Set<conn> 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

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

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).