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