[7.x] fix: support Node 26 and legacy global dispatcher#5319
Open
mcollina wants to merge 8 commits into
Open
Conversation
Signed-off-by: Matteo Collina <hello@matteocollina.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## v7.x #5319 +/- ##
==========================================
- Coverage 92.89% 92.89% -0.01%
==========================================
Files 112 112
Lines 35828 35837 +9
==========================================
+ Hits 33283 33291 +8
- Misses 2545 2546 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Member
Author
|
cc @domenic |
Contributor
|
Thanks. My plan is to move entirely off of Undici v7 for jsdom though (jsdom/jsdom#4170) so hopefully I just won't need to worry about this :) |
ronag
approved these changes
May 29, 2026
metcoder95
reviewed
May 29, 2026
Member
metcoder95
left a comment
There was a problem hiding this comment.
lgtm, just tests are not happy
mcollina
added a commit
to platformatic/platformatic
that referenced
this pull request
May 29, 2026
Node.js >= 26 bundles undici >= 8, whose built-in fetch() reads the global
dispatcher from Symbol.for('undici.globalDispatcher.2'). Our bundled undici
(v7) setGlobalDispatcher() only writes Symbol.for('undici.globalDispatcher.1'),
so the application's global fetch() bypassed the runtime mesh interceptor and
internal *.plt.local calls failed with ENOTFOUND (composer/gateway returned 500
in development mode for nest/vite/remix/tanstack/react-router).
Add mirrorGlobalDispatcherForBuiltinFetch() to @platformatic/foundation and
invoke it wherever the mesh dispatcher is installed so both undici versions
observe the same dispatcher. Can be dropped once undici aligns the symbols
across versions: nodejs/undici#5319
Assisted-by: Claude Code:claude-opus-4-8[1m]
Signed-off-by: Matteo Collina <hello@matteocollina.com>
mcollina
added a commit
to platformatic/platformatic
that referenced
this pull request
May 29, 2026
* ci: test packages on Node.js 26
Assisted-by: OpenAI:gpt-5
Signed-off-by: Matteo Collina <hello@matteocollina.com>
* fix(sql-mapper): update sqlite-pool for Node.js 26
Assisted-by: OpenAI:gpt-5
Signed-off-by: Matteo Collina <hello@matteocollina.com>
* fix(runtime): mirror global dispatcher for Node 26 built-in fetch
Node.js >= 26 bundles undici >= 8, whose built-in fetch() reads the global
dispatcher from Symbol.for('undici.globalDispatcher.2'). Our bundled undici
(v7) setGlobalDispatcher() only writes Symbol.for('undici.globalDispatcher.1'),
so the application's global fetch() bypassed the runtime mesh interceptor and
internal *.plt.local calls failed with ENOTFOUND (composer/gateway returned 500
in development mode for nest/vite/remix/tanstack/react-router).
Add mirrorGlobalDispatcherForBuiltinFetch() to @platformatic/foundation and
invoke it wherever the mesh dispatcher is installed so both undici versions
observe the same dispatcher. Can be dropped once undici aligns the symbols
across versions: nodejs/undici#5319
Assisted-by: Claude Code:claude-opus-4-8[1m]
Signed-off-by: Matteo Collina <hello@matteocollina.com>
* test(wattpm): mirror MockAgent dispatcher for Node 26 built-in fetch
The update-dependencies test fixtures mock the npm registry via undici
MockAgent + setGlobalDispatcher, which writes Symbol.for('undici.globalDispatcher.1').
On Node.js >= 26 (bundled undici >= 8) the built-in fetch() used by the
`update` command reads Symbol.for('undici.globalDispatcher.2'), so the mock
was bypassed and the command hit the real registry, breaking the
wattpm-utils dependency update tests.
Mirror the mock onto the symbol fetch() reads so the registry mock is
honored on Node 26.
Assisted-by: Claude Code:claude-opus-4-8[1m]
Signed-off-by: Matteo Collina <hello@matteocollina.com>
* fix(runtime): grant --allow-net under the permission model on Node 25+
Node.js 25 added --allow-net to the Permission Model, gating dns.lookup(),
server.listen(), outbound connections and fetch() behind it. When an
application enables fs permissions, its worker runs with --permission, so on
Node >= 25 it could no longer bind its HTTP server (getaddrinfo
ERR_ACCESS_DENIED localhost) nor reach other applications through the mesh.
Add a features.node.permission.network capability flag (detected via
process.allowedNodeEnvironmentFlags so the flag is never passed on older
versions where it does not exist) and append --allow-net to the worker
permission flags when supported.
Assisted-by: Claude Code:claude-opus-4-8[1m]
Signed-off-by: Matteo Collina <hello@matteocollina.com>
---------
Signed-off-by: Matteo Collina <hello@matteocollina.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Backport Node 26 coverage to the v7.x branch and keep
setGlobalDispatcher()compatible with Node.js' built-infetch().The package now stores its dispatcher under
Symbol.for('undici.globalDispatcher.2'), while Node's bundledfetch()still readsSymbol.for('undici.globalDispatcher.1').setGlobalDispatcher()now writes the same dispatcher to both symbols so package APIs and Node's built-infetch()observe the same global dispatcher.Changes
build-ffi-testslib/global.jsto use the v2 dispatcher symbol while mirroring the configured dispatcher to the legacy v1 symbolsetGlobalDispatcher(new Agent())does not break Node.js globalfetch()fetch()uses the dispatcher mirrored under the v1 symbolclient-errors.jsreconnect tests withmainso the stream/async-iterator POST error cases remain stable on Node 26Testing
node --test --test-reporter=spec --test-timeout=180000 test/node-test/client-errors.jsnode --test --test-reporter=spec --test-timeout=180000 test/content-length.jsnpx borp --timeout 180000 -p "test/client.js"npm run test:node-testnpm run lintvia the pre-commit hook