# 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; throws `ROOM_FULL` once the daemon hits `maxRooms`. - `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 in `IdempotencyGuard.execute`, fires the `presence: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 ```ts import { createRoom, joinRoom } from "@suaveplan/websocket-gateway"; createRoom(deps, { id: "general" }); await joinRoom(deps, "general", "alice"); ``` ## Usage: Advanced Example ```ts 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): Room` - `deleteRoom(rooms, roomId): boolean` - `joinRoom(deps, roomId, clientId, idempotencyKey?): Promise` - `leaveRoom(deps, roomId, clientId): Promise` - `broadcastToRoom(deps, roomId, senderId, payload, idempotencyKey?): Promise<{message, recipients} | null>` - `unicast(deps, senderId, targetId, payload): Promise` - `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 `::` 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).