Size / Priority
Rationale
WebSocketActor (src/io/broker/) is client-side — connects out to a WS server. Server-side WebSocket support exists but is backend-specific (Fastify's @fastify/websocket, Hono's WS, Express's express-ws) — no uniform actor-ts surface.
A typed WebSocket route + handler abstraction:
- Lets users define a typed bidirectional protocol (
In from client, Out to client).
- Works across all three HTTP backends.
- Integrates with route DSL (path matching, auth middleware).
Design sketch
// src/http/WebSocket.ts (new)
export interface WebSocketSession<In, Out> {
readonly clientHost: string;
send(msg: Out): void;
close(code?: number, reason?: string): void;
readonly closed: Promise<void>;
}
export interface WebSocketHandler<In, Out> {
onOpen?(session: WebSocketSession<In, Out>): void | Promise<void>;
onMessage(session: WebSocketSession<In, Out>, msg: In): void | Promise<void>;
onClose?(session: WebSocketSession<In, Out>, code: number, reason: string): void | Promise<void>;
onError?(session: WebSocketSession<In, Out>, err: Error): void;
}
/** Route DSL integration. */
export function webSocketRoute<In = unknown, Out = unknown>(
pattern: string,
handler: WebSocketHandler<In, Out>,
options?: {
readonly serializer?: 'json' | 'cbor';
readonly maxMessageBytes?: number;
},
): Route;
Backend integration via the existing HttpServerBackend interface — extend with an optional registerWebSocketRoute(pattern, handler, options) method.
Out of scope / non-goals
- Subprotocol negotiation — phase 1: pass-through; user-defined.
- Compression — defer.
- Streaming binary — phase 1: discrete messages; binary supported as Uint8Array.
Open design questions
- Serializer: shared with HTTP marshalling? Recommend: yes (same JSON/CBOR codecs).
- Maximum-message size enforcement: backend-level (Fastify's
maxPayload) vs framework-level. Recommend: framework-level (consistent across backends).
- Session lifecycle as actor: should each WS session spawn its own actor? Powerful but heavy. Recommend: handler-based (sketch) for simplicity; user can spawn per-session actors if needed.
Test plan
- WS server with echo handler; client connects, sends "hi", receives "hi".
- Typed protocol — In/Out typed; type errors caught.
- Close from server side.
- Close from client side (onClose fires).
- Backend parity — same handler works on Fastify, Hono, Express.
- Max-message-size enforced.
- JSON / CBOR serialization.
Acceptance criteria
Size / Priority
Rationale
WebSocketActor(src/io/broker/) is client-side — connects out to a WS server. Server-side WebSocket support exists but is backend-specific (Fastify's@fastify/websocket, Hono's WS, Express'sexpress-ws) — no uniform actor-ts surface.A typed WebSocket route + handler abstraction:
Infrom client,Outto client).Design sketch
Backend integration via the existing
HttpServerBackendinterface — extend with an optionalregisterWebSocketRoute(pattern, handler, options)method.Out of scope / non-goals
Open design questions
maxPayload) vs framework-level. Recommend: framework-level (consistent across backends).Test plan
Acceptance criteria
WebSocketHandler<In, Out>+WebSocketSession<In, Out>types.webSocketRoute(pattern, handler, options?).