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

3.1 KiB

connection-registry — multi-index lookup for active connections

(no submodules)

Purpose

Every plugin endpoint that opens a WebSocket connection registers it with a host-owned ConnectionRegistry. The registry is the lookup surface every other piece of the engine consumes: the subscription bus iterates connections by (topic, tenant), the per-tenant cap counts by tenant, the heartbeat loop walks the registry to find stale connections. Keeping the contract here lets future broker-backed registries (Redis, SurrealDB) swap in without touching plugin code.

This module ships the abstract ConnectionRegistry interface AND a single-host in-memory implementation. The in-memory registry doubles as the default for the conformance harness and is what every test in the Stage-1 contract depends on.

Features

  • ConnectionRegistry interface — add, remove, lookupByTenant, lookupByTopic, countByTenant.
  • InMemoryConnectionRegistry — O(1) add / remove / lookups via three internal maps; idempotent add / remove; topic bindings dropped automatically when a connection is removed.
  • recordSubscription(conn, topic, tenant) and dropSubscription(...) hooks the subscription bus uses to keep the (topic, tenant) index in sync.

Usage: Basic

import { InMemoryConnectionRegistry } from "@suaveplan/api-ws/connection-registry";

const registry = new InMemoryConnectionRegistry();
registry.add(connection);
const peersForTenant = registry.lookupByTenant(connection.tenantId);
registry.remove(connection);

Usage: Advanced

import {
    InMemoryConnectionRegistry,
    type ConnectionRegistry,
} from "@suaveplan/api-ws/connection-registry";

class TenantCapGate {
    constructor(
        private readonly registry: ConnectionRegistry,
        private readonly maxPerTenant: number,
    ) {}

    accept(conn: { tenantId: TenantId }): boolean {
        return this.registry.countByTenant(conn.tenantId) < this.maxPerTenant;
    }
}

const registry: ConnectionRegistry = new InMemoryConnectionRegistry();
const gate = new TenantCapGate(registry, 1000);

API Reference

  • interface ConnectionRegistry { add, remove, lookupByTenant, lookupByTopic, countByTenant }.
  • class InMemoryConnectionRegistry implements ConnectionRegistry.
  • recordSubscription(connection, topic, tenant) — wired by SubscriptionBus.subscribe.
  • dropSubscription(connection, topic, tenant) — wired by unsubscribe.

Implementation Notes

  • The three indices (by id, by tenant, by (topic, tenant)) stay in sync through the add / remove / recordSubscription / dropSubscription quartet. remove walks the topic index because in practice a connection's full subscription set isn't carried on the WsConnection contract — engines may keep that state themselves.
  • recordSubscription silently ignores unknown connections so the subscription bus can call it without re-checking membership.
  • The (topic, tenant) key uses :: as a delimiter; tenant ids that contain :: are still distinguishable because the encoding is prefix-based, not lossy.