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
This commit is contained in:
73
docs/architecture/event-targets/websocket-client.md
Normal file
73
docs/architecture/event-targets/websocket-client.md
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
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
|
||||
Reference in New Issue
Block a user