# `limits` _(no submodules)_ ## Purpose Provides the per-tenant connection-count enforcement function that the handshake pipeline calls before admitting a new connection. The cap defends every host process against a single tenant exhausting socket capacity — a noisy-neighbour mitigation explicitly called out in design §11.4 (default `maxConnectionsPerTenant=1000`). Enforcement is a function rather than a class because the only state needed is the registry itself; threading another stateful module through the handshake path would obscure the actual policy decision. ## Features - O(1) cap check — leans on `registry.countByTenant` which is index-backed. - Throws `WS_BUN.TENANT_CAP_EXCEEDED` with the offending tenant id + configured cap in the message for debuggable telemetry. - Read-only — does not mutate the registry, so the handshake path can run additional checks after the cap test without rollback. ## Usage: Basic ```ts import { enforceTenantCap, BunConnectionRegistry } from "@suaveplan/api-ws-bun"; const registry = new BunConnectionRegistry(); enforceTenantCap(registry, tenantId, 1000); // throws if >= 1000. ``` ## Usage: Advanced ```ts import { enforceTenantCap, BunConnectionRegistry, WS_BUN_CODES, } from "@suaveplan/api-ws-bun"; function tryAccept(reg: BunConnectionRegistry, tenant, cap) { try { enforceTenantCap(reg, tenant, cap); return { accepted: true as const }; } catch (err) { if ((err as { code?: string }).code === WS_BUN_CODES.TENANT_CAP_EXCEEDED) { return { accepted: false as const, httpStatus: 429 }; } throw err; } } ``` ## API Reference - `enforceTenantCap(registry, tenant, cap): void` — throws on cap breach; otherwise no-op. ## Implementation Notes The check uses `>=` rather than `>` so a cap of 1000 admits exactly 1000 concurrent connections; the 1001st gets the 429. This matches the spec scenario verbatim and avoids the off-by-one trap of "999 admitted, 1000 rejected". The implementation does not reserve a registry slot before the cap test. The window between the check and `registry.add(conn)` is bounded by a synchronous function call in the engine, so racing two handshakes through that microsecond gap would only be possible if Bun.serve fired two upgrade callbacks on the same JavaScript turn — which it does not. If a future engine extends the gap, this function should be replaced with an atomic `reserveSlot` / `releaseSlot` pair.