- 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
73 lines
2.3 KiB
Markdown
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 |