- 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
4.6 KiB
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-05-07 |
Worker Event Target
Import: @alkdev/pubsub/event-target-worker
Peer dep: none (Web Worker / Node worker_threads are standard)
Status: Not yet implemented. Needs R&D on Node vs Web Worker API differences.
Enables createPubSub to work across Worker boundaries. Two factory functions: one for the main thread side, one for the worker thread side.
API
// Main thread — wraps a Worker instance
function createWorkerEventTarget<TEvent extends TypedEvent>(
worker: Worker,
): TypedEventTarget<TEvent>;
// Worker thread — wraps parent message port
function createMainThreadEventTarget<TEvent extends TypedEvent>(): TypedEventTarget<TEvent>;
Protocol
Worker messages use the EventEnvelope format over postMessage:
{ "type": "call.responded", "id": "uuid-123", "payload": { "output": 42 } }
Main Thread → Worker
dispatchEvent(event) {
this.worker.postMessage(event.detail);
// event.detail is the EventEnvelope
return true;
}
Worker → Main Thread
dispatchEvent(event) {
globalThis.postMessage(event.detail);
return true;
}
Receiving
// Main thread side
this.worker.onmessage = (msg) => {
const envelope = msg.data;
const topic = `${envelope.type}:${envelope.id}`;
const event = new CustomEvent(topic, { detail: envelope });
// dispatch to listeners
};
// Worker thread side
globalThis.onmessage = (msg) => {
const envelope = msg.data;
const topic = `${envelope.type}:${envelope.id}`;
const event = new CustomEvent(topic, { detail: envelope });
// dispatch to listeners
};
Key Properties
- Bidirectional — both sides can publish and subscribe
- Per-worker — each worker gets its own event target on the main thread side
- Structured clone — Web Workers use structured clone for serialization, but the JSON-serializable
EventEnvelopeensures cross-platform compatibility - No native deps — works in any environment with Worker support
Open Questions / R&D Needed
Node vs Web Worker API
The APIs differ significantly:
| Feature | Web Worker | Node worker_threads |
|---|---|---|
| Create | new Worker(url) |
new Worker(path) |
| Send | worker.postMessage(msg) |
worker.postMessage(msg) |
| Receive | worker.onmessage |
worker.on('message') |
| Worker send | self.postMessage(msg) |
parentPort.postMessage(msg) |
| Worker receive | self.onmessage |
parentPort.on('message') |
| Transfer | postMessage(msg, [transfer]) |
postMessage(msg, [transferList]) |
MessagePort |
No built-in | Yes — MessageChannel for direct ports |
Options:
- Two adapters —
event-target-web-workerandevent-target-node-worker - One adapter with runtime detection — detect environment and use appropriate API
- One adapter abstracting both — wrap the differences behind a common interface
Recommendation: Start with a single adapter that targets Web Workers (browser + Deno + Bun all support this API). Add Node worker_threads support later if needed, potentially with a MessagePort-based approach for direct channels.
Worker Pool Pattern
The original sandbox implementation used a worker pool pattern. A WorkerPoolManager would:
- Maintain a pool of workers
- Assign tasks to available workers
- Collect results and fan out to subscribers
This is not part of the WorkerEventTarget — it's a downstream concern for @alkdev/operations. The event target just wraps a single postMessage/onmessage channel. Pool management belongs higher.
Transferable Objects
Web Workers support Transferable objects (ArrayBuffers, etc.) for zero-copy transfer. The current EventEnvelope is JSON, which gets structured-cloned. If large payloads need zero-copy transfer, the envelope could support a Transferable field, but this adds complexity and is not needed for the initial implementation.
Relationship to Downstream
Workers can subscribe to events and publish results through the event target, allowing @alkdev/operations to dispatch work to worker threads via the same pubsub transport. The correlation (id field in the envelope) connects request to response.
Test Plan
- Main → Worker send — dispatchEvent from main posts message to worker
- Worker → Main send — dispatchEvent from worker posts message to main
- Bidirectional — both sides can subscribe and publish
- Topic scoping — type:id topics correctly formed
- Envelope round-trip — full envelope survives serialization
- Worker termination — cleanup when worker exits