# Capability Spec Delta: game-server (game-server-network) **Package**: `@suaveplan/game-server` **Status**: PROPOSED ## ADDED Requirements ### Requirement: Unified network adapter contract The package SHALL expose a `NetworkAdapter` class that wraps a structural `Transport` interface and provides single-initialisation, multi-subscriber fan-out, Zod-validated outbound + inbound frames, and OpenTelemetry counters for messages sent / received / errors. #### Scenario: single-initialisation guard - **WHEN** `initialize(...)` is called twice without an intervening `dispose()` / `disconnect()` - **THEN** the second call throws `GameServerError` with code `GAME_SERVER.TRANSPORT_ALREADY_INITIALIZED`. #### Scenario: send before initialise - **WHEN** `send(...)` is called before `initialize(...)` - **THEN** it rejects with code `GAME_SERVER.TRANSPORT_NOT_INITIALIZED`. #### Scenario: send while transport disconnected - **WHEN** the underlying transport never fires `onConnect` and `send` is invoked - **THEN** it rejects with code `GAME_SERVER.TRANSPORT_NOT_CONNECTED`. #### Scenario: outbound message validation - **WHEN** a frame with empty `type` is passed to `send` - **THEN** it rejects with code `GAME_SERVER.INVALID_MESSAGE_FORMAT`. #### Scenario: fan-out and handler isolation - **WHEN** multiple subscribers are attached via `onMessage` and one of them throws - **THEN** every other subscriber still receives the frame and the failure is logged through the injected logger. **Verified by:** `src/network/network-adapter/network-adapter.test.ts::"initialises a transport and connects"` AND `src/network/network-adapter/network-adapter.test.ts::"rejects double-initialisation"` AND `src/network/network-adapter/network-adapter.test.ts::"rejects send before initialize"` AND `src/network/network-adapter/network-adapter.test.ts::"rejects send when not connected"` AND `src/network/network-adapter/network-adapter.test.ts::"rejects malformed outgoing message"` AND `src/network/network-adapter/network-adapter.test.ts::"fans inbound messages to every subscriber"` AND `src/network/network-adapter/network-adapter.test.ts::"isolates handler errors and still delivers to others"` AND `src/network/network-adapter/network-adapter.test.ts::"drops malformed inbound frames"` AND `src/network/network-adapter/network-adapter.test.ts::"exposes stats counts"`. --- ### Requirement: Reconnection with exponential backoff The package SHALL expose a `ReconnectionManager` that schedules reconnection attempts through an injectable `TimersLike`, delegates backoff calculation to `@suaveplan/retry`'s `createExponentialBackoff`, and reports outcomes through OTel counter + histogram. #### Scenario: success on first attempt - **WHEN** the supplied `reconnect` callback resolves on the first try - **THEN** the manager resets to `{ attempts: 0, isReconnecting: false }` and stops scheduling. #### Scenario: exponential retry until success - **WHEN** `reconnect` fails twice and then succeeds - **THEN** the manager invokes the callback exactly three times, doubling the envelope between attempts. #### Scenario: exhaustion - **WHEN** `maxAttempts` is `n` and `reconnect` always rejects - **THEN** the manager emits `network:reconnect-exhausted` after `n` failures and stops scheduling further attempts. #### Scenario: double-start guard - **WHEN** `start()` is invoked while a reconnection is already in progress - **THEN** it throws `GameServerError` with code `GAME_SERVER.RECONNECT_IN_PROGRESS`. #### Scenario: use after dispose - **WHEN** `start()` or `forceReconnect()` is invoked after `dispose()` - **THEN** it throws `GameServerError` with code `GAME_SERVER.RECONNECT_EXHAUSTED`. **Verified by:** `src/network/reconnection-manager/reconnection-manager.test.ts::"succeeds on first attempt"` AND `src/network/reconnection-manager/reconnection-manager.test.ts::"retries with exponential backoff on failure"` AND `src/network/reconnection-manager/reconnection-manager.test.ts::"exhausts after maxAttempts and emits exhausted event"` AND `src/network/reconnection-manager/reconnection-manager.test.ts::"rejects start when already reconnecting"` AND `src/network/reconnection-manager/reconnection-manager.test.ts::"start throws after dispose"` AND `src/network/reconnection-manager/reconnection-manager.test.ts::"forceReconnect throws after dispose"`. --- ### Requirement: WebSocket transport wrapper The package SHALL expose a `WebSocketTransport` class implementing the unified `Transport` contract by wrapping a structural `UnderlyingWebSocket` client. JSON framing SHALL be applied to outgoing messages and inbound parse failures SHALL surface through `onError` rather than throwing. #### Scenario: connect forwards protocols - **WHEN** `connect(url, { protocols: ["v1"] })` is invoked - **THEN** the underlying client receives the URL and protocols. #### Scenario: send rejects when not connected - **WHEN** `send(...)` is invoked before `connect(...)` - **THEN** it rejects with code `GAME_SERVER.TRANSPORT_NOT_CONNECTED`. #### Scenario: parse error surfaces through onError - **WHEN** the underlying client delivers a non-JSON frame - **THEN** the registered `onError` handler receives a typed `GameServerError` with code `GAME_SERVER.INVALID_MESSAGE_FORMAT`. **Verified by:** `src/network/websocket-transport/websocket-transport.test.ts::"connects with protocols"` AND `src/network/websocket-transport/websocket-transport.test.ts::"send serialises message as JSON"` AND `src/network/websocket-transport/websocket-transport.test.ts::"send rejects when disconnected"` AND `src/network/websocket-transport/websocket-transport.test.ts::"reports parse errors through onError"` AND `src/network/websocket-transport/websocket-transport.test.ts::"propagates connect/disconnect callbacks"`. --- ### Requirement: WebRTC transport wrapper The package SHALL expose a `WebRTCTransport` class implementing the unified `Transport` contract by wrapping a structural `UnderlyingWebRTC` client. The wrapper SHALL provide pass-through signalling helpers (`addIceCandidate`, `applyAnswer`) and SHALL throw a typed error when the underlying client does not implement them. #### Scenario: connect with options bag - **WHEN** `connect(url, { iceServers: [...] })` is invoked - **THEN** the options bag is forwarded verbatim to the underlying client's `open(...)`. #### Scenario: signalling pass-through - **WHEN** `addIceCandidate(c)` and `applyAnswer(a)` are invoked on a client that implements them - **THEN** the candidate / answer is delivered verbatim to the client. #### Scenario: signalling unsupported - **WHEN** the underlying client omits `addIceCandidate` / `applyAnswer` - **THEN** the wrapper throws `GameServerError` with code `GAME_SERVER.TRANSPORT_NOT_INITIALIZED`. **Verified by:** `src/network/webrtc-transport/webrtc-transport.test.ts::"connects with options bag"` AND `src/network/webrtc-transport/webrtc-transport.test.ts::"send serialises message as JSON"` AND `src/network/webrtc-transport/webrtc-transport.test.ts::"addIceCandidate forwards to underlying"` AND `src/network/webrtc-transport/webrtc-transport.test.ts::"applyAnswer forwards to underlying"` AND `src/network/webrtc-transport/webrtc-transport.test.ts::"addIceCandidate throws when unsupported"` AND `src/network/webrtc-transport/webrtc-transport.test.ts::"applyAnswer throws when unsupported"`. --- ### Requirement: Extended error code registry The package SHALL register six additional `GAME_SERVER.*` codes introduced by the network slice and SHALL expose them on the `GAME_SERVER_CODES` constant alongside the existing core codes. #### Scenario: registry shape - **WHEN** the test suite inspects `GAME_SERVER_CODES` - **THEN** it contains `TRANSPORT_ALREADY_INITIALIZED`, `TRANSPORT_NOT_INITIALIZED`, `TRANSPORT_NOT_CONNECTED`, `INVALID_MESSAGE_FORMAT`, `RECONNECT_EXHAUSTED`, `RECONNECT_IN_PROGRESS`, each fully-qualified as `GAME_SERVER.`. **Verified by:** `src/errors/game-server-error.test.ts::"registers all expected codes under GAME_SERVER namespace"`.