Fix critical publish() bug, address review findings
CRITICAL: createPubSub.publish() was dispatching CustomEvent with
just the event type (e.g. 'call.responded') instead of the composite
topic string ('call.responded:uuid-123'). This broke all adapters
that rely on topic-scoped dispatch — Redis subscribe/publish
channels didn't match, and WS server fan-out routing would fail.
Fixed to dispatch with the full type:id composite.
Other fixes:
- Add __ prefix runtime guard in publish() (reserved for control)
- Add Redis barrel re-export to src/index.ts (ADR-002 compliance)
- Clarify WS server: adapter's onclose calls removeConnection
internally; user doesn't need to
- WS client: document null callback no-op, removeEventListener
edge cases (unregistered callback, null callback)
- WS server: document dispatchEvent always returns true
- Redis spec: document in-flight message edge case after unsubscribe
- Worker adapter: rename createMainThreadEventTarget to
createWorkerThreadEventTarget, createWorkerEventTarget to
createWorkerHostEventTarget (fix inverted naming)
- api-surface.md: add PubSub.publish() section documenting the
type:id composite and __ guard
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-05-07
|
||||
last_updated: 2026-05-08
|
||||
---
|
||||
|
||||
# Worker Event Target
|
||||
@@ -14,15 +14,17 @@ Enables `createPubSub` to work across Worker boundaries. Two factory functions:
|
||||
## API
|
||||
|
||||
```ts
|
||||
// Main thread — wraps a Worker instance
|
||||
function createWorkerEventTarget<TEvent extends TypedEvent>(
|
||||
// Main thread side — wraps a Worker instance
|
||||
function createWorkerHostEventTarget<TEvent extends TypedEvent>(
|
||||
worker: Worker,
|
||||
): TypedEventTarget<TEvent>;
|
||||
|
||||
// Worker thread — wraps parent message port
|
||||
function createMainThreadEventTarget<TEvent extends TypedEvent>(): TypedEventTarget<TEvent>;
|
||||
// Worker thread side — wraps parent message port
|
||||
function createWorkerThreadEventTarget<TEvent extends TypedEvent>(): TypedEventTarget<TEvent>;
|
||||
```
|
||||
|
||||
The naming convention: `Host` is the side that owns the `Worker` object (typically the main thread). `Thread` is the side that runs inside the worker (accessing `self.onmessage` / `parentPort`).
|
||||
|
||||
## Protocol
|
||||
|
||||
Worker messages use the `EventEnvelope` format over `postMessage`:
|
||||
@@ -31,9 +33,10 @@ Worker messages use the `EventEnvelope` format over `postMessage`:
|
||||
{ "type": "call.responded", "id": "uuid-123", "payload": { "output": 42 } }
|
||||
```
|
||||
|
||||
### Main Thread → Worker
|
||||
### Host → Worker Thread
|
||||
|
||||
```ts
|
||||
// Host side (createWorkerHostEventTarget)
|
||||
dispatchEvent(event) {
|
||||
this.worker.postMessage(event.detail);
|
||||
// event.detail is the EventEnvelope
|
||||
@@ -41,19 +44,20 @@ dispatchEvent(event) {
|
||||
}
|
||||
```
|
||||
|
||||
### Worker → Main Thread
|
||||
### Worker Thread → Host
|
||||
|
||||
```ts
|
||||
// Worker thread side (createWorkerThreadEventTarget)
|
||||
dispatchEvent(event) {
|
||||
globalThis.postMessage(event.detail);
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
### Receiving
|
||||
### Receiving on Host Side
|
||||
|
||||
```ts
|
||||
// Main thread side
|
||||
// Host side (createWorkerHostEventTarget)
|
||||
this.worker.onmessage = (msg) => {
|
||||
const envelope = msg.data;
|
||||
const topic = `${envelope.type}:${envelope.id}`;
|
||||
@@ -61,7 +65,10 @@ this.worker.onmessage = (msg) => {
|
||||
// dispatch to listeners
|
||||
};
|
||||
|
||||
// Worker thread side
|
||||
### Receiving on Worker Thread Side
|
||||
|
||||
```ts
|
||||
// Worker thread side (createWorkerThreadEventTarget)
|
||||
globalThis.onmessage = (msg) => {
|
||||
const envelope = msg.data;
|
||||
const topic = `${envelope.type}:${envelope.id}`;
|
||||
|
||||
Reference in New Issue
Block a user