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,67 @@
# `SseConnectionRegistry` + `createConnectionRegistry`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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
```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).