Commit f53f438a3b5 for nodejs

commit f53f438a3b5a5392656150060951d72d27030e70
Author: Filip Skokan <panva.ip@gmail.com>
Date:   Sun Sep 27 19:02:12 2026 +0200

    worker: preserve blob module URLs

    Evaluate blob module sources under their original URL instead of
    rewrapping them in a data URL. This preserves import.meta.url and module
    identity while retaining the captured source after URL revocation.

    Signed-off-by: Filip Skokan <panva.ip@gmail.com>
    Assisted-by: Codex
    PR-URL: https://github.com/nodejs/node/pull/66354
    Reviewed-By: Anna Henningsen <anna@addaleax.net>
    Reviewed-By: James M Snell <jasnell@gmail.com>
    Reviewed-By: Aviv Keller <me@aviv.sh>

diff --git a/lib/internal/webworker.js b/lib/internal/webworker.js
index e798c814f28..cf5f1df6372 100644
--- a/lib/internal/webworker.js
+++ b/lib/internal/webworker.js
@@ -20,8 +20,6 @@ const {
   SymbolFor,
   SymbolIterator,
   SymbolToStringTag,
-  TypedArrayPrototypeGetLength,
-  Uint8Array,
   globalThis,
 } = primordials;

@@ -77,10 +75,6 @@ const {
   vm_dynamic_import_default_internal,
 } = internalBinding('symbols');

-const {
-  base64Slice,
-} = internalBinding('buffer');
-
 const {
   hasOpenSSL,
 } = internalBinding('config');
@@ -757,10 +751,9 @@ const convertWorkerOptions = createDictionaryConverter(

 /**
  * @param {URL} workerURL The parsed URL of the worker script.
- * @param {'classic'|'module'} type The worker's type.
  * @returns {{ value?: URL, source?: string }|null}
  */
-function resolveWorkerEntry(workerURL, type) {
+function resolveWorkerEntry(workerURL) {
   switch (workerURL.protocol) {
     case 'file:':
       return { value: workerURL };
@@ -779,17 +772,6 @@ function resolveWorkerEntry(workerURL, type) {
       if (data === undefined) {
         return null;
       }
-      if (type === 'module') {
-        // The blob's contents are re-wrapped as a data: URL so that the
-        // module loader evaluates them as a module script.
-        // TODO(@avivkeller): What if we update the ESM loader to accept blob:
-        // urls?
-        const bytes = new Uint8Array(data);
-        return {
-          value: new URL('data:text/javascript;base64,' + base64Slice(
-            bytes, 0, TypedArrayPrototypeGetLength(bytes))),
-        };
-      }
       return { source: utf8Decode(data) };
     }
     default:
@@ -824,7 +806,7 @@ class Worker extends EventTarget {
     }

     this[kWorker] = null;
-    const entry = resolveWorkerEntry(workerURL, options.type);
+    const entry = resolveWorkerEntry(workerURL);
     if (entry === null) {
       // "If the algorithm asynchronously completes with null or with a
       // script whose error to rethrow is non-null, then: Queue a global
diff --git a/test/parallel/test-webworker-blob-module-url.js b/test/parallel/test-webworker-blob-module-url.js
new file mode 100644
index 00000000000..7e51b727446
--- /dev/null
+++ b/test/parallel/test-webworker-blob-module-url.js
@@ -0,0 +1,19 @@
+// Flags: --experimental-web-worker
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto) common.skip('missing crypto');
+
+const assert = require('node:assert');
+const source = `
+  import value from 'data:text/javascript,export default 42';
+  postMessage([import.meta.url, location.href, value]);
+`;
+const url = URL.createObjectURL(new Blob([source], { type: 'text/javascript' }));
+const worker = new Worker(url, { type: 'module' });
+URL.revokeObjectURL(url);
+worker.onerror = common.mustNotCall('worker failed');
+worker.onmessage = common.mustCall(({ data }) => {
+  worker.terminate();
+  assert.deepStrictEqual(data, [url, url, 42]);
+});