2.6 KiB
Gateway room operations
Purpose
Free-function module containing every room-level mutation the gateway exposes — create / delete / join / leave / broadcast / unicast — extracted from gateway.ts so the daemon file stays under the 300-line cap. The functions consume a structurally-typed RoomOpsDeps and have no class dependency, which makes them straightforward to unit-test with hand-rolled fakes.
Features
createRoom(deps, config)— capacity check + idempotent registration; throwsROOM_FULLonce the daemon hitsmaxRooms.deleteRoom(rooms, id)— clears members, drops the entry, decrements the OTel rooms-active gauge.joinRoom(deps, roomId, clientId, idempotencyKey?)— gates through rate-limit + authz, wraps the join inIdempotencyGuard.execute, fires thepresence:update.leaveRoom(deps, roomId, clientId)— symmetric leave + presence fire.broadcastToRoom(deps, roomId, senderId, payload, idempotencyKey?)— gated + idempotent message dispatch returning{message, recipients}.unicast(deps, senderId, targetId, payload)— gated unicast.
Usage: Basic Example
import { createRoom, joinRoom } from "@suaveplan/websocket-gateway";
createRoom(deps, { id: "general" });
await joinRoom(deps, "general", "alice");
Usage: Advanced Example
import { broadcastToRoom } from "@suaveplan/websocket-gateway";
const result = await broadcastToRoom(
deps,
"general",
"alice",
{ text: "hi" },
"client-supplied-idempotency-key"
);
// result.recipients excludes alice; result.message is the dispatched envelope.
API Reference
createRoom(deps, config): RoomdeleteRoom(rooms, roomId): booleanjoinRoom(deps, roomId, clientId, idempotencyKey?): Promise<boolean>leaveRoom(deps, roomId, clientId): Promise<boolean>broadcastToRoom(deps, roomId, senderId, payload, idempotencyKey?): Promise<{message, recipients} | null>unicast(deps, senderId, targetId, payload): Promise<Message | null>RoomOpsDeps— structurally-typed dependency bundle.
Implementation Notes
The dependency bundle uses Pick<> / Omit<> per function so tests can construct minimal fakes — createRoom and deleteRoom deliberately avoid pulling in the gate/idempotency/presence-fire dependencies that aren't relevant to their pure path. Idempotency keys default to a deterministic <verb>:<roomId>:<clientId> shape; per-broadcast they incorporate the daemon's clock so two broadcastToRoom calls in the same logical operation are NOT deduplicated unless the caller passes an explicit key (the upstream behaviour).