/** * WebSocket framing — one JSON message per text frame (PROTOCOL §2.2). * * The WS frame boundary *is* the record boundary, so no newline delimiting is needed (or * allowed). Binary frames are unused in v0.1 and MUST be rejected. Keepalive is WS * ping/pong (server pings every 30s; client must pong or be considered dead) — this is a * transport concern, not encoded here. */ import { FramingError } from '@/protocol/framing/jsonl'; /** Re-exported for callers that import the ws module directly. */ export { FramingError }; /** * Encode a message for a WS text frame: `JSON.stringify(msg)`. No newline — the frame * boundary is the record boundary (§2.2). */ export function encodeWsFrame(msg: unknown): string { // No \n: the WS frame itself delimits the record. return JSON.stringify(msg); } /** * Decode a received WS text frame into a parsed message. * * @param data the text frame payload (string) * @returns the parsed message * @throws {FramingError} with code `INVALID_REQUEST` if the payload is not valid JSON, or * with code `PROTOCOL_ERROR` if it is not a non-null object. */ export function decodeWsFrame(data: string): unknown { let value: unknown; try { value = JSON.parse(data); } catch (err) { throw new FramingError( 'INVALID_REQUEST', `invalid JSON in WS text frame: ${err instanceof Error ? err.message : String(err)}`, ); } if (value === null || typeof value !== 'object' || Array.isArray(value)) { throw new FramingError( 'PROTOCOL_ERROR', 'WS frame payload must be a JSON object (message envelope), got ' + (Array.isArray(value) ? 'array' : value === null ? 'null' : typeof value), ); } return value; } /** * Assert that a frame is a text frame. Binary frames are unused in v0.1 (§2.2) and MUST be * rejected — accepting them would let a client smuggle a non-JSON payload past the parser. * * @param isBinary whether the received frame was binary * @throws {FramingError} with code `PROTOCOL_ERROR` if the frame was binary. */ export function assertTextFrame(isBinary: boolean): void { if (isBinary) { throw new FramingError( 'PROTOCOL_ERROR', 'binary WS frames are unused in protocol v0.1 (§2.2)', ); } }