--- status: draft last_updated: 2026-05-07 --- # WebSocket Server Event Target **Import**: `@alkdev/pubsub/event-target-websocket-server` **Peer dep**: none (WebSocket is a web standard, but a server framework like Hono may be needed for upgrade handling) **Status**: Not yet implemented. Manages multiple WebSocket connections for the server (hub) side. Handles fan-out: `dispatchEvent` sends to all connected spokes; `addEventListener` aggregates subscriptions across all connections. ## `createWebSocketServerEventTarget` ```ts function createWebSocketServerEventTarget( options: CreateWebSocketServerEventTargetArgs, ): TypedEventTarget; ``` ### `CreateWebSocketServerEventTargetArgs` | Field | Type | Required | Description | |-------|------|----------|-------------| | `onConnection` | callback | No | Called when a new spoke connects. Receives the spoke's event target for per-connection customization. | | `onDisconnection` | callback | No | Called when a spoke disconnects. Receives the spoke's event target for cleanup. | ## How It Works Unlike the client adapter, the server adapter manages a `Set` of active connections: - `dispatchEvent` → iterates all connected WebSockets, sends JSON envelope to each - `addEventListener` → registers local listeners. The server doesn't subscribe to individual spokes — it listens for events from any spoke - `removeEventListener` → removes local listeners ### Incoming Messages Each connected spoke sends JSON envelopes. The server listens on `ws.onmessage` for each connection: ```ts for (const ws of this.connections) { ws.onmessage = (msg) => { const envelope = JSON.parse(msg.data); const topic = `${envelope.type}:${envelope.id}`; const event = new CustomEvent(topic, { detail: envelope }); this.dispatchEvent(event); // dispatches to local listeners }; } ``` ### Outgoing Messages (Fan-out) ```ts dispatchEvent(event) { const message = JSON.stringify(event.detail); for (const ws of this.connections) { ws.send(message); } return true; } ``` ## Per-Connection Spoke Targets The server adapter creates a `WebSocketClientEventTarget` for each incoming connection. This allows the hub to target specific spokes if needed (e.g., responding to a specific request). ## Key Properties - **Fan-out** — dispatchEvent sends to all connected spokes - **Aggregate subscription** — addEventListener listens for events from any spoke - **Connection lifecycle** — manages add/remove of WebSocket connections - **No native deps** — works with any WebSocket server (Node ws, Bun, Deno, Hono) ## Open Questions 1. **Server framework coupling** — Should this adapter take a raw `WebSocketServer` or just handle `WebSocket` instances? Raw `WebSocket` instances keeps it framework-agnostic. The caller (hub code) handles the HTTP upgrade and passes connected `WebSocket`s to the adapter. 2. **Backpressure** — What happens when `ws.send()` blocks or buffers? Should there be a max buffer size per connection? 3. **Selective fan-out** — Should `dispatchEvent` always send to all connections, or should there be a way to target a specific spoke? ## Test Plan 1. **Fan-out** — dispatchEvent sends to all connected WebSockets 2. **Incoming aggregation** — messages from any spoke dispatch to local listeners 3. **Connection add/remove** — new connections are tracked, disconnections are cleaned up 4. **Mixed topology** — server adapter and client adapters can communicate bidirectionally