Files
pubsub/docs/architecture/event-targets/websocket-client.md
glm-5.1 371dabc20d Add per-adapter architecture docs in event-targets/ directory
- Create docs/architecture/event-targets/ with individual specs:
  in-process, redis, websocket-client, websocket-server,
  worker, iroh-spoke, iroh-hub
- Update event-targets.md to serve as index with topology model
  (symmetric vs fan-out) and adapter status table
- Update architecture.md index to reference new directory
2026-05-07 14:49:50 +00:00

73 lines
2.3 KiB
Markdown

---
status: draft
last_updated: 2026-05-07
---
# WebSocket Client Event Target
**Import**: `@alkdev/pubsub/event-target-websocket-client`
**Peer dep**: none (WebSocket is a web standard)
**Status**: Not yet implemented.
Wraps a single `WebSocket` connection for the client (spoke) side. Bidirectional — can both send and receive events.
## `createWebSocketClientEventTarget`
```ts
function createWebSocketClientEventTarget<TEvent extends TypedEvent>(
ws: WebSocket,
): TypedEventTarget<TEvent>;
```
Takes an already-connected `WebSocket`. The caller is responsible for connection lifecycle (including reconnection — see below).
## Protocol
WebSocket provides native message boundaries (no length-prefix needed). Each message is a JSON-serialized `EventEnvelope`:
```json
{ "type": "call.responded", "id": "uuid-123", "payload": { "output": 42 } }
```
### Sending (dispatchEvent)
```ts
dispatchEvent(event) {
this.ws.send(JSON.stringify(event.detail));
// event.detail is the EventEnvelope { type, id, payload }
return true;
}
```
### Receiving (addEventListener)
```ts
ws.onmessage = (msg) => {
const envelope = JSON.parse(msg.data);
const topic = `${envelope.type}:${envelope.id}`;
const event = new CustomEvent(topic, { detail: envelope });
// dispatch to local listeners
};
```
## Key Properties
- **Bidirectional** — `dispatchEvent` sends over WS, `addEventListener` receives from WS
- **Per-connection** — one event target per WebSocket connection
- **JSON framing** — WebSocket provides native message boundaries
- **No native deps** — works in browsers and Node
- **Envelope serialization** — sends/receives the full `EventEnvelope` JSON
## Reconnection
WebSocket connections drop. On reconnect, the spoke must create a new `WebSocket` and a new `WebSocketClientEventTarget`. Reconnection logic belongs to the spoke lifecycle, not the event target.
The event target itself is per-connection. A new connection means a new instance.
## Test Plan
1. **Send path** — dispatchEvent serializes envelope and calls ws.send
2. **Receive path** — ws.onmessage parses envelope, creates CustomEvent, dispatches to listeners
3. **Topic scoping** — type:id topics correctly formed from envelope
4. **Connection close** — ws.onclose propagates to listeners (error event?)
5. **Multiple listeners** — multiple addEventListener on same topic