Skip to content

worker: add support for Web Workers - #64894

Open
avivkeller wants to merge 5 commits into
nodejs:mainfrom
avivkeller:web-workers
Open

worker: add support for Web Workers#64894
avivkeller wants to merge 5 commits into
nodejs:mainfrom
avivkeller:web-workers

Conversation

@avivkeller

@avivkeller avivkeller commented Aug 1, 2026

Copy link
Copy Markdown
Member

Closes: #43583

Adds support for the Web Worker API as defined by the HTML Standard:
https://html.spec.whatwg.org/multipage/workers.html

The implementation trys to follow the specification as close as Node.js allows, so note the following differences:

  • SharedWorker is not implemented. Its lifetime and sharing model depend on origins and browsing contexts, concepts that do not exist in Node.js.

  • Worker scripts are loaded synchronously from the local filesystem rather than fetched over the network. As a result:

    • new Worker() and importScripts() accept only file:, data:, and blob: URLs.
    • Any other scheme throws a NotSupportedError.
    • An unreadable script throws a NetworkError (per the spec, this is emitted as an error event).
    • Redirects, nosniff, and HTTP MIME type validation are not applicable. MIME type validation is performed only for data: and blob: URLs.
    • WorkerOptions.credentials is validated for API compatibility but otherwise has no effect, since no network request is made.
  • Node.js has no origin model. Consequently, concepts such as same-origin and cross-origin do not exist, and location.origin is null for file: workers.

  • close() terminates the worker immediately instead of following the specification's "closing flag" algorithm. Code remaining in the current task after close() is therefore not executed.

  • The worker global is the normal Node.js global object with DedicatedWorkerGlobalScope inserted into its prototype chain rather than the inverse (a fresh global created from the interface). Additionally, classic file: workers are executed through the CommonJS/ESM loaders rather than as classic scripts, so top-level declarations do not become global properties. Classic data: and blob: workers continue to execute as classic scripts.

  • ErrorEvents dispatched to Worker instances include message and error, but not filename, lineno, or colno. Unhandled worker errors are also not propagated further. These are a result of the worker_threads implementation that is underneath the web workers implemantion.

  • The following WorkerGlobalScope events are never dispatched:

    • languagechange, online, and offline, since these concepts do not exist in Node.js.
    • rejectionhandled and unhandledrejection, since Node.js exposes equivalent process-level events but does not implement the PromiseRejectionEvent interface or the per-rejection preventDefault() behavior required by the HTML Standard.
  • On the main thread, relative worker script URLs are resolved against the current working directory because there is no document base URL. Within a worker, relative URLs resolve against the worker's own URL, matching the specification.

AI Disclaimer: I used slight AI help to resolve issues that came up during me validating the WPT tests.

TODO before merge:

  • test WPT Report upload against staging.wpt.fyi with @panva

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/loaders
  • @nodejs/startup
  • @nodejs/web-standards

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Aug 1, 2026
@avivkeller

avivkeller commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

I'm not sure if I should break the WPT additions into their own commit / PR for ease of reviewing?

You should be able to collapse the test folder to view the lib impl changes

Finally, I haven't added dedicated tests for this outside of the WPT, which should cover it.

@avivkeller avivkeller added worker Issues and PRs related to Worker support. web-standards Issues and PRs related to Web APIs semver-minor PRs that contain new features and should be released in the next minor version. labels Aug 1, 2026
Comment thread lib/internal/webworker.js
Comment thread test/common/wpt/webworker.js
@avivkeller avivkeller added commit-queue-squash Add this label to instruct the Commit Queue to squash all the PR commits into the first one. experimental Issues and PRs related to experimental features. labels Aug 1, 2026
@jasnell

jasnell commented Aug 1, 2026

Copy link
Copy Markdown
Member

The web platform tests in this PR really ought to be separated out into a separate commit to make reviewing this easier. 1300+ files changes with 54k+ lines changed is very difficult to review in a single commit.

Comment thread doc/api/globals.md Outdated
@avivkeller

Copy link
Copy Markdown
Member Author

The web platform tests in this PR really ought to be separated out into a separate commit to make reviewing this easier. 1300+ files changes with 54k+ lines changed is very difficult to review in a single commit.

Broken up! c73f2fe has the actual changes

@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.63799% with 71 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.13%. Comparing base (91a99c5) to head (056b9bf).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
lib/internal/webworker.js 92.70% 71 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #64894      +/-   ##
==========================================
- Coverage   90.32%   90.13%   -0.19%     
==========================================
  Files         751      752       +1     
  Lines      250000   251094    +1094     
  Branches    47231    47157      -74     
==========================================
+ Hits       225816   226336     +520     
- Misses      15566    16131     +565     
- Partials     8618     8627       +9     
Files with missing lines Coverage Δ
lib/internal/blob.js 89.40% <100.00%> (-0.46%) ⬇️
...internal/bootstrap/web/exposed-window-or-worker.js 91.60% <100.00%> (-2.20%) ⬇️
lib/internal/encoding.js 96.66% <100.00%> (+0.04%) ⬆️
lib/internal/event_target.js 98.78% <100.00%> (-0.40%) ⬇️
lib/internal/main/worker_thread.js 94.91% <100.00%> (+0.55%) ⬆️
lib/internal/modules/helpers.js 98.92% <100.00%> (-0.01%) ⬇️
lib/internal/navigator.js 98.78% <100.00%> (+<0.01%) ⬆️
lib/internal/process/pre_execution.js 97.26% <100.00%> (-0.46%) ⬇️
lib/internal/webidl.js 99.12% <100.00%> (-0.34%) ⬇️
lib/internal/worker.js 96.76% <100.00%> (+0.01%) ⬆️
... and 5 more

... and 48 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread test/fixtures/wpt/README.md
@mcollina

mcollina commented Aug 1, 2026

Copy link
Copy Markdown
Member

This is gargantuan. Can you add a review guide and/or split into chunks?

Comment thread test/wpt/status/workers.json Outdated
Comment thread doc/api/globals.md
@panva

panva commented Aug 1, 2026

Copy link
Copy Markdown
Member

I have:

  • added a commit to accomodate multi-global WPT tests in the runner and reporter
  • added a TODO to the description when this stabilizes to test the daily WPT Report upload against staging.wpt.fyi

@avivkeller

Copy link
Copy Markdown
Member Author

This is gargantuan. Can you add a review guide and/or split into chunks?

It's only extremely large due to the added WPT tests. You can make it easier to review by

  1. Not reviewing the WPT test commit (the second commit in the PR), or
  2. Collapsing the test/fixtures folder in the GitHub UI

Comment thread test/parallel/test-worker-spec-differences.js Outdated
@avivkeller
avivkeller requested review from jasnell and mcollina August 1, 2026 16:41
Comment thread lib/internal/webworker.js Outdated
@avivkeller

avivkeller commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

I think I'm getting really really unlucky on the MacOS CI. It's failing over several attempts, but each time with a different test flake 😭

@avivkeller avivkeller added the review wanted PRs that need reviews. label Aug 7, 2026
@nikelborm

Copy link
Copy Markdown

For easier review of the PR
https://diffshub.com/nodejs/node/pull/64894

@avivkeller

Copy link
Copy Markdown
Member Author

@nodejs/web-standards @nodejs/workers PTAL :-)

@panva

panva commented Aug 10, 2026

Copy link
Copy Markdown
Member

I simulated the daily Node.js WPT workflow against epochs/daily revision 54f8f933629e7c010ae98a246729af01f8abcda5 and fixed two issues exposed by the generated report:

  • Test discovery was treating support scripts as tests. Upstream WPT classifies files below resources, support, and tools as support files through its MANIFEST generator. Node’s discovery now mirrors that rule while leaving those files available to ResourceLoader. This removed the status-file workaround for worker resources and two bogus wasm ERROR results.
  • META: title was not propagated into the actual Web Worker executing .any.worker.js tests. The parsed title is now passed as structured worker data and installed before loading the harness, keeping worker and window subtest names aligned.

Regression coverage was added for both discovery filtering and end-to-end window/worker title parity.

@avivkeller avivkeller added the commit-queue Add this label to land a pull request using GitHub Actions. label Aug 13, 2026
@nodejs-github-bot nodejs-github-bot added commit-queue-failed An error occurred while landing this pull request using GitHub Actions. and removed commit-queue Add this label to land a pull request using GitHub Actions. labels Aug 13, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator
Commit Queue failed
- Loading data for nodejs/node/pull/64894
✔  Done loading data for nodejs/node/pull/64894
----------------------------------- PR info ------------------------------------
Title      worker: add support for Web Workers (#64894)
Author     Aviv Keller <me@aviv.sh> (@avivkeller)
Branch     avivkeller:web-workers -> nodejs:main
Labels     semver-minor, lib / src, experimental, author ready, worker, needs-ci, review wanted, commit-queue, commit-queue-rebase, web-standards
Commits    9
 - test: accomodate multi-global tests in WPT{Runner,TestSpec,Report}
 - test: add opt-in process WPT runner
 - worker: add support for Web Workers
 - test: enable multi-global WPTs
 - worker: add wpt tests for Web Workers
 - fixup! worker: add support for Web Workers
 - fixup! test: enable multi-global WPTs
 - fixup! worker: add wpt tests for Web Workers
 - fixup! test: enable multi-global WPTs
Committers 2
 - Filip Skokan <panva.ip@gmail.com>
 - avivkeller <me@aviv.sh>
PR-URL: https://github.com/nodejs/node/pull/64894
Fixes: https://github.com/nodejs/node/issues/43583
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
------------------------------ Generated metadata ------------------------------
PR-URL: https://github.com/nodejs/node/pull/64894
Fixes: https://github.com/nodejs/node/issues/43583
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
--------------------------------------------------------------------------------
   ℹ  This PR was created on Sat, 01 Aug 2026 04:18:11 GMT
   ✔  Approvals: 2
   ✔  - Filip Skokan (@panva) (TSC): https://github.com/nodejs/node/pull/64894#pullrequestreview-4868941830
   ✔  - James M Snell (@jasnell) (TSC): https://github.com/nodejs/node/pull/64894#pullrequestreview-4926739457
   ✔  Last GitHub CI successful
   ℹ  Last Full PR CI on 2026-08-05T20:04:47Z: https://ci.nodejs.org/job/node-test-pull-request/75538/
   ⚠  Commits were pushed after the last Full PR CI run:
   ⚠  - fixup! worker: add wpt tests for Web Workers
   ⚠  - fixup! test: enable multi-global WPTs
- Querying data for job/node-test-pull-request/75538/
✔  Build data downloaded
   ✔  Last Jenkins CI successful
--------------------------------------------------------------------------------
   ✔  Aborted `git node land` session in /home/runner/work/node/node/.ncu
https://github.com/nodejs/node/actions/runs/31701830064

@panva panva added the request-ci Add this label to start a Jenkins CI on a PR. label Aug 13, 2026
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 13, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Comment thread lib/internal/bootstrap/web/exposed-window-or-worker.js
Comment on lines +365 to +369
function setupWebWorkers() {
if (!getOptionValue('--experimental-web-worker')) {
delete globalThis.Worker;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming is deceiving, it's not setting up anything

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was following how setupEventsource and how setupWebsocket worked, is that wrong?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can change it to behave more like setupWebStorage?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe there's a good reason for doing it this way (e.g. if getOptionValue is not available, or something like that), following setupWebStorage is not a good enough reason

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so this can be resolved since I'm following the similar APIs, or is there something else to discuss?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm saying the opposite (i.e. setupWebStorage is not an example to follow), though I guess it's not a blocking concern

@avivkeller avivkeller Aug 14, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, I see. I'll get this updated

Comment thread eslint.config.mjs
WritableStreamDefaultWriter: 'readonly',
WritableStreamDefaultController: 'readonly',
WebSocket: 'readonly',
Worker: 'readonly',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to also forbid its use in lib/ like we do for e.g.

{
name: 'WritableStream',
message: "Use `const { WritableStream } = require('internal/webstreams/writablestream')` instead of the global.",
},

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't apply this logic to everything, so I wasn't sure (e.g. WebSocket, URLPattern). Are those intentional exceptions or just oversights?

@aduh95 aduh95 Aug 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just oversight, I've opened #65281 to fix that. We don't want to rely on the user-mutable global object unless we don't have a choice

panva and others added 5 commits August 14, 2026 10:40
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Co-authored-by: Aviv Keller <me@aviv.sh>
Signed-off-by: Aviv Keller <me@aviv.sh>
To hopefully get to the bottom of WPT crashes that have no traces.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Co-authored-by: Aviv Keller <me@aviv.sh>
Signed-off-by: Aviv Keller <me@aviv.sh>
Signed-off-by: Aviv Keller <me@aviv.sh>
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Co-authored-by: Aviv Keller <me@aviv.sh>
Signed-off-by: Aviv Keller <me@aviv.sh>
Signed-off-by: Aviv Keller <me@aviv.sh>
@panva

panva commented Aug 14, 2026

Copy link
Copy Markdown
Member

rebased and squashed, i made no functional changes

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@panva panva removed the commit-queue-failed An error occurred while landing this pull request using GitHub Actions. label Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author ready PRs that have at least one approval, no pending requests for changes, and a CI started. commit-queue-rebase Add this label to allow the Commit Queue to land a PR in several commits. experimental Issues and PRs related to experimental features. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. review wanted PRs that need reviews. semver-minor PRs that contain new features and should be released in the next minor version. web-standards Issues and PRs related to Web APIs worker Issues and PRs related to Worker support.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Web Workers

9 participants