From 9b911b3fa856568b2a7c21ff669b7b7ad7d8df0f Mon Sep 17 00:00:00 2001 From: SudhansuBandha Date: Tue, 7 Jul 2026 07:43:55 +0530 Subject: [PATCH 1/2] worker: populate BroadcastChannel MessageEvent source Populate the MessageEvent source property with the sender's worker thread ID for BroadcastChannel messages originating from worker threads. Fixes: https://github.com/nodejs/node/issues/59053 Signed-off-by: Sudhansu Bandha --- lib/internal/worker/io.js | 9 ++++-- test/parallel/test-worker-broadcastchannel.js | 31 ++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/lib/internal/worker/io.js b/lib/internal/worker/io.js index 786ee36c1927fa..4182538561fb9e 100644 --- a/lib/internal/worker/io.js +++ b/lib/internal/worker/io.js @@ -48,6 +48,7 @@ const { } = internalBinding('messaging'); const { getEnvMessagePort, + threadId, } = internalBinding('worker'); const { Readable, Writable } = require('stream'); @@ -346,7 +347,7 @@ function receiveMessageOnPort(port) { } function onMessageEvent(type, data) { - this.dispatchEvent(lazyMessageEvent(type, { data })); + this.dispatchEvent(lazyMessageEvent(type, { data: data.value, source: data.source })); } function isBroadcastChannel(value) { @@ -419,13 +420,17 @@ class BroadcastChannel extends EventTarget { * @returns {void} */ postMessage(message) { + const broadcastMessage = { + source: threadId, + value: message, + }; if (!isBroadcastChannel(this)) throw new ERR_INVALID_THIS('BroadcastChannel'); if (arguments.length === 0) throw new ERR_MISSING_ARGS('message'); if (this[kHandle] === undefined) throw new DOMException('BroadcastChannel is closed.', 'InvalidStateError'); - if (this[kHandle].postMessage(message) === undefined) + if (this[kHandle].postMessage(broadcastMessage) === undefined) throw new DOMException('Message could not be posted.'); } diff --git a/test/parallel/test-worker-broadcastchannel.js b/test/parallel/test-worker-broadcastchannel.js index 12535271c15596..89bccd1fbddf19 100644 --- a/test/parallel/test-worker-broadcastchannel.js +++ b/test/parallel/test-worker-broadcastchannel.js @@ -147,7 +147,7 @@ assert.throws(() => new BroadcastChannel(), { const bc1 = new BroadcastChannel('channel4'); const bc2 = new BroadcastChannel('channel4'); bc1.postMessage('some data'); - assert.strictEqual(receiveMessageOnPort(bc2).message, 'some data'); + assert.strictEqual(receiveMessageOnPort(bc2).message.value, 'some data'); assert.strictEqual(receiveMessageOnPort(bc2), undefined); bc1.close(); bc2.close(); @@ -183,3 +183,32 @@ assert.throws(() => new BroadcastChannel(), { "BroadcastChannel { name: 'channel5', active: false }" ); } + +{ + const bc = new BroadcastChannel('worker-source'); + + new Worker(` + const assert = require('assert'); + const { BroadcastChannel, threadId } = require('worker_threads'); + + const bc = new BroadcastChannel('worker-source'); + + bc.onmessage = (evt) => { + assert.strictEqual(evt.data, 'from-main'); + assert.strictEqual(evt.source, 0); + + bc.close(); + }; + + bc.postMessage('from-worker'); + `, { eval: true }); + + bc.onmessage = common.mustCall((evt) => { + assert.strictEqual(evt.data, 'from-worker'); + + assert.ok(evt.source > 0); + + bc.postMessage('from-main'); + bc.close(); + }); +} From e392e56d17279725646687f0b88f3e9460c56231 Mon Sep 17 00:00:00 2001 From: SudhansuBandha Date: Sat, 11 Jul 2026 08:38:21 +0530 Subject: [PATCH 2/2] worker: expose sender on BroadcastChannel MessageEvent --- doc/api/worker_threads.md | 60 +++++++++++++++++++ lib/internal/worker/io.js | 12 +++- test/parallel/test-worker-broadcastchannel.js | 4 +- 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/doc/api/worker_threads.md b/doc/api/worker_threads.md index 525825ade82621..a353b25ccce595 100644 --- a/doc/api/worker_threads.md +++ b/doc/api/worker_threads.md @@ -995,6 +995,66 @@ added: v15.4.0 * `message` {any} Any cloneable JavaScript value. +### `messageEvent.sender` + + + +> Stability: 1 - Experimental + +Type: {number} + +A Node.js-specific extension that identifies the sender of a +BroadcastChannel message. + +When a message originates, sender is the threadId of the +worker that posted the message. + +```mjs +import { + isMainThread, + BroadcastChannel, + Worker, +} from 'node:worker_threads'; + +const bc = new BroadcastChannel('hello'); + +if (isMainThread) { + let c = 0; + bc.onmessage = ({ data, sender }) => { + console.log(`Received "${data}" from worker ${sender}`); + if (++c === 10) bc.close(); + }; + for (let n = 0; n < 10; n++) + new Worker(new URL(import.meta.url)); +} else { + bc.postMessage('hello from every worker'); + bc.close(); +} +``` + +```cjs +const { + isMainThread, + BroadcastChannel, + Worker, +} = require('node:worker_threads'); + +const bc = new BroadcastChannel('hello'); + +if (isMainThread) { + let c = 0; + bc.onmessage = ({ data, sender }) => { + console.log(`Received "${data}" from worker ${sender}`); + if (++c === 10) bc.close(); + }; + for (let n = 0; n < 10; n++) + new Worker(__filename); +} else { + bc.postMessage('hello from every worker'); + bc.close(); +} +``` + ### `broadcastChannel.ref()`