- 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
86 lines
3.5 KiB
Markdown
86 lines
3.5 KiB
Markdown
---
|
|
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<TEvent extends TypedEvent>(
|
|
options: CreateWebSocketServerEventTargetArgs,
|
|
): TypedEventTarget<TEvent>;
|
|
```
|
|
|
|
### `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<WebSocket>` 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 |