import type { TenantId } from "@suaveplan/api-types/tenant"; import { SSE_BUN_CODES, SseBunError } from "../error-codes/error-codes.js"; /** * Per-tenant connection counter the engine consults on every handshake. * * Implementations expose `tryAcquire(tenantId)` (returns `true` and * increments on success, throws {@link SSE_BUN_CODES.TENANT_CAP_EXCEEDED} * on cap breach) plus `release(tenantId)` called on graceful close. */ export interface TenantConnectionCap { tryAcquire(tenantId: TenantId): true; release(tenantId: TenantId): void; count(tenantId: TenantId): number; readonly total: number; } /** * Build a tenant cap with the given per-tenant ceiling. */ export function createTenantConnectionCap( maxConnectionsPerTenant: number ): TenantConnectionCap { const counts = new Map(); let total = 0; return { tryAcquire(tenantId): true { const current = counts.get(tenantId) ?? 0; if (current >= maxConnectionsPerTenant) { throw new SseBunError( SSE_BUN_CODES.TENANT_CAP_EXCEEDED, "error.sse_bun.tenant_cap_exceeded", { context: { tenantId, limit: maxConnectionsPerTenant, current, }, } ); } counts.set(tenantId, current + 1); total += 1; return true; }, release(tenantId): void { const current = counts.get(tenantId) ?? 0; if (current <= 1) { counts.delete(tenantId); } else { counts.set(tenantId, current - 1); } total = Math.max(0, total - 1); }, count(tenantId): number { return counts.get(tenantId) ?? 0; }, get total() { return total; }, }; }