Skip to content

node:diagnostics_channel: sync with Node 26 + subsystem channels - #32628

Open
cirospaciari wants to merge 59 commits into
mainfrom
claude/diagnostics-channel-node26
Open

node:diagnostics_channel: sync with Node 26 + subsystem channels#32628
cirospaciari wants to merge 59 commits into
mainfrom
claude/diagnostics-channel-node26

Conversation

@cirospaciari

@cirospaciari cirospaciari commented Jun 23, 2026

Copy link
Copy Markdown
Member

Summary

  • Ports lib/diagnostics_channel.js from Node v26.3.0: boundedChannel() / BoundedChannel exports, Channel.prototype.withStoreScope, copy-on-write subscriber lists (sync unsubscribe during publish no longer skips subscribers), TracingChannel rebuilt on BoundedChannel (hasSubscribers, early exit when unsubscribed, non-thenable warning, custom-thenable passthrough), and a WeakReference that keeps a channel alive while it has active subscriptions so they survive GC.
  • Publishes the subsystem channels Node provides:
    • worker_threads (Worker construction)
    • child_process + child_process.spawn tracing channel (spawn success/ENOENT/EACCES paths)
    • net.client.socket, net.server.socket, net.server.listen tracing channel
    • http.server.request.start, http.server.response.created, http.server.response.finish
  • Syncs the test-diagnostics-channel-* files from Node v26.3.0 (29 new, 8 updated to current upstream content). Every vendored file added or modified here is byte-identical to Node v26.3.0 upstream.

Also implemented, because vendored tests in this sync depend on them:

Rather than vendor known-failing tests behind [ FAIL ] entries, the three upstream tests Bun cannot pass are not vendored at all. They are known gaps, to add when the blocking feature lands:

not vendored blocked on
test-diagnostics-channel-web-locks Web Locks API (navigator.locks) is not implemented
test-diagnostics-channel-module-import asserts the published url is the literal specifier ('http'); Bun's transpiler rewrites import("http") to "node:http" before the loader publishes module.import, so Bun publishes 'node:http'. The channel itself works; test-diagnostics-channel-module-import-error covers it.

This PR also adds tests for the http.server.response.created channel and for the upgrade-gating of all three http.server.* channels to test/js/node/diagnostics_channel/diagnostics_channel.test.ts. They fail under USE_SYSTEM_BUN=1 and pass on this build.

http.server.request.start and http.server.response.finish stay covered by the vendored test-diagnostics-channel-http-server-start.js.

Why the napi test changes are here

test_function and test_instance_data create an object with a napi finalizer, null the JS reference, call global.gc() once, and expect the finalizer before exit. JSC's GC conservatively scans the native stack, so this PR's overridableRequire wrapper (which changes require()'s call depth) can leave a stale pointer to the object in an unscrubbed stack slot on the release-ASAN layout, and one gc() does not collect it. The tests now create the object inside an IIFE and retry gc() a bounded number of times, mirroring what test_finalizer/test.js in the same directory already does for the same reason. The napi harness also captures the spawned test's stdout on failure so the mustCall diagnostic is visible in CI instead of a bare exit code 1.

Test plan

  • All 65 vendored test-diagnostics-channel-* tests pass, run CI-style (bun-debug run --config=bunfig.node-test.toml, BUN_GARBAGE_COLLECTOR_LEVEL=1)
  • 26/26 across the test-async-local-storage-*, test-async-hooks-*, test-als-* and test-asyncresource-* vendored suites pass after the cleanupAsyncHooksData fix
  • test/js/node/diagnostics_channel/ and test/js/node/async_hooks/AsyncLocalStorage.test.ts pass; the new http.server.response.created test and the enterWith+process.nextTick test fail under USE_SYSTEM_BUN=1
  • Every added/modified file under test/js/node/test/parallel/ diffs clean against raw.githubusercontent.com/nodejs/node/v26.3.0
  • CI green on all platforms

Linked issues

Fixes #32472
Fixes #29586
Fixes #27805

Notes:


[review] gate passed · iteration 33 · 60 files touched

fails on main (without fix)
ASAN without fix: BUILD FAILED (no junit output)
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/js/node/async_hooks/AsyncLocalStorage.test.ts test/js/node/diagnostics_channel/diagnostics_channel.test.ts
ninja: Entering directory `/workspace/bun/build/debug'
[1/180] gen generated_host_exports.rs
generated_host_exports.rs: 92 exports (host=3, lazy=10, generic=79, rust=0); 239 extern-C blocks audited
[2/180] gen cpp.rs (cppbind)
[3/180] gen JS modules (bundle-modules)
Preprocess modules (15152ms)
Bundle modules (57ms)
Postprocesss modules (40ms)
Bundle Functions (1021ms)
Generate Code (26ms)

[16.31s] Bundled "src/js" for development
  2802 kb
  198 internal modules
  13 native modules
  91 internal functions across 17 files
[3/180] cargo bun_bin → libbun_rust.a (--target x86_64-unknown-linux-gnu)

  nightly-2026-07-20-x86_64-unknown-linux-gnu unchanged - rustc 1.99.0-nightly (9f36de775 2026-07-19)

[62/180] cxx obj/unified/UnifiedSource-src_jsc_bindings-12.cpp.o
FAILED: obj/unified/UnifiedSource-src_jsc_bindings-12.cpp.o 
/usr/bin/ccache /usr/lib/llvm-21/bin/clang++ -march=nehalem -O0 -g3 -gz=zstd -glldb -fsanitize=address -
... (truncated)

release without fix: 1 failed, 5 skipped
bun test v1.4.0-canary.1 (6ef200531)

test/js/node/diagnostics_channel/diagnostics_channel.test.ts:
(pass) Channel > can have subscribers [0.32ms]
(pass) Channel > can have symbol as name [0.35ms]
(pass) Channel > does not throw when unsubscribed [0.26ms]
(pass) Channel > can publish and subscribe [0.25ms]
(pass) Channel > can publish and subscribe using object [0.20ms]
(todo) Channel > can handle subscriber errors
(todo) Channel > can use bind store
(pass) Channel > references are not leaked [6.60ms]
(todo) TracingChannel > TODO
(pass) TracingChannel > tracingChannel(null) throws ERR_INVALID_ARG_TYPE like Node [0.25ms]
(pass) node:http server channels > http.server.response.created publishes the request and response [11.48ms]
(pass) node:http server channels > http.server.* channels do not publish for accepted upgrades [9.43ms]
459 |       unsubscribe("http.server.response.finish", onFinish);
460 |       tcp.close();
461 |       httpServer.close();
462 |     }
463 | 
464 |     expect(events.map(e => e.name)).toEqual(["created", "start", "finish"]);
                                          ^
error: expect(received).toEqual(expected)

- [
-   "created",
-   "start",
... (truncated)
passes on PR (with fix)
ASAN with fix: 5 skipped
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/js/node/async_hooks/AsyncLocalStorage.test.ts test/js/node/diagnostics_channel/diagnostics_channel.test.ts
bun test v1.4.0 (416dd52d9)

test/js/node/diagnostics_channel/diagnostics_channel.test.ts:
(pass) Channel > can have subscribers [25.76ms]
(pass) Channel > can have symbol as name [21.74ms]
(pass) Channel > does not throw when unsubscribed [17.70ms]
(pass) Channel > can publish and subscribe [19.26ms]
(pass) Channel > can publish and subscribe using object [16.10ms]
(todo) Channel > can handle subscriber errors
(todo) Channel > can use bind store
(pass) Channel > references are not leaked [439.55ms]
(todo) TracingChannel > TODO
(pass) TracingChannel > tracingChannel(null) throws ERR_INVALID_ARG_TYPE like Node [13.10ms]
(pass) node:http server channels > http.server.response.created publishes the request and response [666.27ms]
(pass) node:http server channels > http.server.* channels do not publish for accepted upgrades [367.04ms]
(pass) node:http server channels > http.server.* channels publish on the emit('connection') fall
... (truncated)

release with fix: 5 skipped
$ bun scripts/build.ts --profile=release
[configured] bun-profile → bun (stripped) in 685ms (unchanged)
ninja: Entering directory `/workspace/bun/build/release'
[1/137] gen generated_host_exports.rs
generated_host_exports.rs: 92 exports (host=3, lazy=10, generic=79, rust=0); 239 extern-C blocks audited
[2/137] gen cpp.rs (cppbind)
[3/137] gen JS modules (bundle-modules)
Preprocess modules (9540ms)
Bundle modules (88ms)
Postprocesss modules (228ms)
Bundle Functions (939ms)
Generate Code (39ms)

[10.86s] Bundled "src/js" for production
  2622 kb
  198 internal modules
  13 native modules
  91 internal functions across 17 files
[3/137] cargo bun_bin → libbun_rust.a (--target x86_64-unknown-linux-gnu)

  nightly-2026-07-20-x86_64-unknown-linux-gnu unchanged - rustc 1.99.0-nightly (9f36de775 2026-07-19)

�[1m�[92m   Compiling�[0m bun_core v0.0.0 (/workspace/bun/src/bun_core)
�[1m�[92m   Compiling�[0m bun_errno v0.0.0 (/workspace/bun/src/errno)
�[1m�[92m   Compiling�[0m bun_ptr v0.0.0 (/workspace/bun/src/ptr)
�[1m�[92m   Compiling�[0m bun_boringssl_sys v0.0.0 (/workspace/bun/src/boringssl_sys)
�[1m�[92m   Compiling�[0m bun_safety v0.0.0 (/workspace/bun/src/safety)
�[1
... (truncated)
diff hotspot
src/js/builtins/CommonJS.ts                        |  11 +-
 src/js/internal/http1_server_fallback.ts           |  43 ++
 src/js/internal/module_tracing.ts                  |  63 +++
 src/js/node/_http_server.ts                        |  56 +++
 src/js/node/child_process.ts                       |  31 ++
 src/js/node/diagnostics_channel.ts                 | 532 +++++++++++++++------
 src/js/node/net.ts                                 |  43 +-
 src/js/node/v8.ts                                  |  39 +-
 src/js/node/worker_threads.ts                      |   8 +
 src/jsc/bindings/NodeDiagnosticsChannel.cpp        |  15 +
 src/jsc/bindings/NodeDiagnosticsChannel.h          |  11 +
 src/jsc/bindings/NodeV8Module.cpp                  |  63 +++
 src/jsc/bindings/NodeV8Module.h                    |  11 +
 src/jsc/bindings/ZigGlobalObject.cpp               |  83 +++-
 src/jsc/bindings/ZigGlobalObject.h                 |   1 +
 test/js/node/async_hooks/AsyncLocalStorage.test.ts |  27 +-
 .../diagnostics_channel.test.ts                    | 160 ++++++-
 ...est-diagnostic-channel-http-response-created.js |  45 ++
 ...-channel-bounded-channel-run-transform-error.js |  66 +++
 ...test-diagnostics-channel-bounded-channel-run.js | 125 +++++
 ...gnostics-channel-bounded-channel-scope-error.js |  90 ++++
 ...nostics-channel-bounded-channel-scope-nested.js | 257 ++++++++++
 ...hannel-bounded-channel-scope-transform-error.js |  66 +++
 ...st-diagnostics-channel-bounded-channel-scope.js | 206 ++++++++
 .../test-diagnostics-channel-bounded-channel.js    | 105 ++++
 .../test-diagnostics-channel-child-process.js      |  94 ++++
 ...iagnostics-channel-gc-maintains-subcriptions.js |  21 +
 .../test-diagnostics-channel-gc-race-condition.js  |  23 +
 .../test-diagnostics-channel-http-server-start.js  |  62 +++
 .../test/parallel/test-diagnostics-channel-http.js |  87 ++++
 .../test-diagnostics-channel-memory-leak.js        |  22 +
 ...test-diagnostics-channel-module-import-er
... (truncated)

gate history · 35 passed · 2 rejected · iteration 33

evidence per changed file
file                                                reads  edits  tests
src/js/builtins/CommonJS.ts                             3      3      0
src/js/internal/http1_server_fallback.ts                1      2      0
src/js/internal/module_tracing.ts                       2      5      0
src/js/node/_http_server.ts                            14     16      0
src/js/node/child_process.ts                            2      2      0
src/js/node/diagnostics_channel.ts                      7      9      0
src/js/node/net.ts                                      5      5      0
src/js/node/v8.ts                                       2      3      0
src/js/node/worker_threads.ts                           2      3      0
src/jsc/bindings/NodeDiagnosticsChannel.cpp             1      2      0
src/jsc/bindings/NodeDiagnosticsChannel.h               0      1      0
src/jsc/bindings/NodeV8Module.cpp                       0      0      0
src/jsc/bindings/NodeV8Module.h                         1      1      0
src/jsc/bindings/ZigGlobalObject.cpp                    7      8      0
src/jsc/bindings/ZigGlobalObject.h                      1      1      0
test/js/node/async_hooks/AsyncLocalStorage.test.ts      5      7      0
(+ 44 more files)

Port lib/diagnostics_channel.js from Node v26.3.0: adds boundedChannel()/
BoundedChannel, Channel.prototype.withStoreScope (store scopes built on
DisposableStack), copy-on-write subscriber lists so unsubscribing during
publish doesn't skip later subscribers, TracingChannel rebuilt on
BoundedChannel with hasSubscribers, no-subscriber early exit, non-thenable
warnings and custom-thenable passthrough, and a WeakReference that holds
the channel strongly while it has active subscriptions so subscriptions
survive GC.

Add AsyncLocalStorage.prototype.withScope (RunScope), used by the new
store-scope API. The scope updates the async context directly and restores
the previous store on dispose instead of going through enterWith, so it
does not arm the end-of-tick context cleanup.

Publish the diagnostics channels Node provides in the corresponding
subsystems:
- worker_threads: 'worker_threads' on Worker construction
- child_process: 'child_process' on ChildProcess construction and the
  'child_process.spawn' tracing channel around spawning
- net: 'net.client.socket', 'net.server.socket' and the
  'net.server.listen' tracing channel
- http server: 'http.server.request.start', 'http.server.response.created',
  'http.server.response.finish'

Sync all 67 test-diagnostics-channel-* tests from Node v26.3.0 (32 new,
8 updated). 59 pass; the remaining 7 newly-added tests depend on features
Bun does not implement yet (module loader channels, Web Locks,
v8.queryObjects, net.Socket-backed http server sockets) and are listed in
test/expectations.txt with reasons.
@robobun

robobun commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator
Updated 7:11 PM PT - Aug 7th, 2026

@robobun, your commit 416dd52d965e5ecc0e30984af73ad9bfe39168ca passed in Build #90391! 🎉


🧪   To try this PR locally:

bunx bun-pr 32628

That installs a local version of the PR into your bun-32628 executable, so you can run:

bun-32628 --bun

@github-actions

Copy link
Copy Markdown
Contributor

Found 3 issues this PR may fix:

  1. node:child_process does not publish diagnostics_channel events (breaks Sentry childProcessIntegration and OpenTelemetry) #32472 - PR adds child_process and child_process.spawn tracing channels to child_process.ts, directly implementing the missing diagnostics_channel events
  2. node:http server does not publish to HTTP server diagnostics_channel #29586 - PR adds the three HTTP server diagnostic channels (http.server.request.start, http.server.response.created, http.server.response.finish) to _http_server.ts
  3. tracingChannel().hasSubscribers is undefined #27805 - PR fixes TracingChannel.hasSubscribers which was undefined, now implemented as a proper getter

If this is helpful, copy the block below into the PR description to auto-close these issues on merge.

Fixes #32472
Fixes #29586
Fixes #27805

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. fix: add hasSubscribers property to TracingChannel #27881 - Adds hasSubscribers property to TracingChannel, which node:diagnostics_channel: sync with Node 26 + subsystem channels #32628 fully rewrites as part of the Node 26 TracingChannel sync
  2. http: publish to http.server.* diagnostics_channel channels #29588 - Publishes to http.server.* diagnostics_channel channels, which node:diagnostics_channel: sync with Node 26 + subsystem channels #32628 also implements in _http_server.ts
  3. child_process: publish diagnostics_channel events for node:child_process spawn/fork/exec/execFile #30080 - Publishes diagnostics_channel events for node:child_process spawn/fork/exec/execFile, which node:diagnostics_channel: sync with Node 26 + subsystem channels #32628 also implements in child_process.ts

🤖 Generated with Claude Code

Wrap the CommonJS require path in the "module.require" tracing channel the
way Node's wrapModuleLoad does: start/end/error events carry
{ parentFilename, id } (and result on success).

To keep require() free for processes that never use diagnostics_channel,
the channel lives in a small internal holder module
(internal/require_tracing) that node:diagnostics_channel fills in when it
is first loaded. overridableRequire only checks that holder; when nobody
subscribed it tail-calls the real implementation, which moved to a new
overridableRequireImpl builtin (registered as a lazy global builtin getter
like requireESM, so its source is still not parsed during global object
construction).

Enables test-diagnostics-channel-module-require(-error).js; their
expectations entries are removed.
Add v8.queryObjects(ctor[, options]) backed by a JSC heap scan: after a
full synchronous GC, every live object whose prototype chain contains
ctor.prototype is collected (walking prototypes structurally so no proxy
traps or getters run during heap iteration). The JS wrapper mirrors
Node's: validates the constructor and options, supports the 'count' and
'summary' formats, and emits the same one-time ExperimentalWarning.

Vendors Node v26.3.0's test-v8-query-objects.js, which passes verbatim,
and removes the expectations entry for
test-diagnostics-channel-memory-leak.js, which now passes too.
Wrap dynamic import() in the "module.import" tracing channel like Node's
ESM loader: start/end fire synchronously around starting the import and
asyncStart/asyncEnd/error fire when the promise settles, with
{ parentURL, url } context.

The channel handling lives in JS: internal/require_tracing becomes
internal/module_tracing and now holds both the module.require and
module.import channels (installed by node:diagnostics_channel when it
loads) plus a traceImport() helper that runs the import through
TracingChannel.tracePromise. moduleLoaderImportModule moves its body into
a static helper and only diverts through traceImport when
node:diagnostics_channel has been loaded and the channel has subscribers,
so dynamic import is unchanged for everything else.

test-diagnostics-channel-module-import-error.js passes and its
expectations entry is removed. test-diagnostics-channel-module-import.js
still has an entry: the transpiler rewrites the statically-analyzable
import("http") literal to "node:http" before the loader sees it, so the
published url differs from Node's for that one case.
The tracing branch in moduleLoaderImportModule used a block-scoped
ThrowScope; destroying it unreleased simulates a throw, so falling through
to the real import (which declares its own scope) tripped JSC's exception
check validation and aborted under BUN_JSC_validateExceptionChecks (the
ASAN CI job). Declare the scope at function level and release it through
RELEASE_AND_RETURN for the tail call instead, and drop the redundant check
after toBoolean(), which cannot throw.
@cirospaciari

Copy link
Copy Markdown
Member Author

CI note: in build #64127 (the exception-scope fix commit), test/js/node/test/parallel/test-net-connect-memleak.js failed on Alpine 3.23 x64 (baseline hard-failed, regular soft-failed). The same net.ts diff was green on Alpine in the two earlier builds of this PR (#64059, #64114), the commits in between don't touch sockets or net, and the test passes locally on this branch (6/6 runs). It's a single-gc() collection assertion, so this looks like GC-timing sensitivity rather than something this PR introduced — I retried the failed job; if it fails again I'll dig into it rather than retry further.

Create net.client.socket / net.server.socket / net.server.listen at module
load like _http_client.ts (and Node's net.js) instead of lazily inside
Socket.prototype.connect / listen / onconnection. This drops the per-call
lazy-init branch and keeps diagnostics_channel's module evaluation out of
the first connect() call, where the extra allocation work sat right in the
window that GC-sensitive tests like test-net-connect-memleak.js measure.
@cirospaciari

Copy link
Copy Markdown
Member Author

Follow-up on the Alpine test-net-connect-memleak.js failures: it failed again in build #64138 on both Alpine x64 jobs and passed on retry both times (as it did in #64127). It is not reproducible locally on glibc (14/14 passes, including with BUN_JSC_validateExceptionChecks=1), and 55bc058e03 (creating the net channels at module scope, out of the connect() window) didn't eliminate it.

Digging into history: the test does not appear in the Alpine x64 shard logs of the last five main builds — the vendored-test sharding never places it on Alpine there. This PR adds 34 vendored test files, which shifts the shard boundaries so the test now runs on Alpine, where its single-gc() collectability assertion (the connect-listener closure must be collected after one GC) is evidently sensitive to JSC's conservative stack scanning on musl. So this looks like a pre-existing platform sensitivity being exposed by resharding rather than something the diff retains — there's no structural reference to the closure from any of the new code, and the same diff passes the test on every other platform.

test/expectations.txt has no musl/alpine modifier, so I haven't quarantined it (a [ LINUX ] entry would also skip it on glibc where it's healthy). Happy to handle it however you prefer — e.g. an alpine-tagged expectations modifier, or tracking it under #20627.

The vendored test asserts the implicit 'connect' once-listener closure is
collected after a single gc(). On the Alpine (musl) CI runners that
collection is intermittently not observed within one cycle, so the
assertion fails; nothing holds the closure structurally and the test passes
on glibc Linux, macOS, Windows and FreeBSD, where it keeps running. The
test only started landing on the Alpine shards because this branch adds
test files and shifts the shard boundaries. Scoped with the existing MUSL
ABI modifier; tracked in #20627.
@cirospaciari

Copy link
Copy Markdown
Member Author

Resolution for the Alpine flake: e6ec58c adds a [ MUSL ]-scoped expectations entry for test-net-connect-memleak.js (the runner already exposes the ABI as a modifier, so no runner changes were needed). The test keeps running on glibc Linux, macOS, Windows and FreeBSD; on musl runners it is quarantined with the observed failure documented and a pointer to #20627. Happy to drop the entry if you'd rather handle it differently.

test_function and test_instance_data fail on the release-ASAN CI runners
(the spawned bun exits 1 with no output, 4/4 in-job attempts) while passing
on every other platform and on a local ASAN debug build. They only started
running on the ASAN shards because the growing test list shifted the shard
boundaries. Scoped to the existing ASAN modifier; tracked in #32642.
@cirospaciari

Copy link
Copy Markdown
Member Author

Two node-api suites (test_function, test_instance_data) turned out to fail consistently on the release-ASAN runners once the resharding placed them there — same exposure pattern as the Alpine memleak test, no involvement of this PR's code, and they pass locally on an ASAN debug build. Filed #32642 and quarantined them with [ ASAN ] expectations entries in the latest commit; they keep running everywhere else.

…vative GC

Both tests create an object with a napi finalizer, null the JS reference,
call global.gc() once, and expect common.mustCall() to observe the
finalizer before process exit. JSC's GC conservatively scans the native
stack, so when require()'s call depth changes (as this PR's
overridableRequire wrapper does) a stale pointer to the just-created
object can survive in an unscrubbed stack slot on the release-ASAN
layout, the object is not collected by a single gc() and the mustCall
check fails at exit with 'Expected exactly 1, actual 0' on stdout.

Move the object creation into an IIFE so the creating frame is gone
before gc() runs, and retry gc() a bounded number of times. This mirrors
what test_finalizer/test.js in the same directory already does for the
same reason ('to be compatible with non-V8 JS engines').

Also capture the spawned test's stdout in the node-napi harness and
print it when the test fails, so the mustCall diagnostic is visible in
CI instead of presenting as a bare 'exit code 1'.

Removes the [ ASAN ] expectations entries for these suites; both pass
5/5 on a local release-ASAN build with the full CI environment.

Closes #32642
@robobun

robobun commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Pushed ee6a68d to address #32642: the two quarantined node-api suites weren't a pre-existing ASAN issue, they were tripped by the extra require() frame this PR adds. The tests relied on a single global.gc() collecting an object created in the same stack, which is not guaranteed under JSC's conservative stack scan once the frame layout shifts. Hardened them with the same IIFE + retry pattern test_finalizer/test.js already uses, made the napi harness surface stdout on failure so the mustCall diagnostic is visible, and removed the expectations entries. Both pass 5/5 on release-ASAN with the full CI env.

One optional follow-up left as a note on #32642: the no-subscriber fast path in overridableRequire could use .$call(this, originalId, options) instead of .$apply(this, arguments) to skip allocating an arguments object on every require(); the native $require handles the two-arg form identically when options is undefined.

@cirospaciari

Copy link
Copy Markdown
Member Author

CI status: every test job in build #64232 passed (285/286). The single failed job is darwin 26 aarch64 - test-bun, which has now failed twice in this build (and once in the previous build) with the same agent-side error before running any tests: buildkite-agent artifact download timed out after 120s for step 'darwin-aarch64-build-bun' on agent darwin-aarch64-26-5-1-1. This looks like an infrastructure issue with that agent's artifact downloads rather than anything in the PR; I'll retry it again shortly, but it may need someone to look at the agent.

@cirospaciari

Copy link
Copy Markdown
Member Author

Third retry of the darwin-26 aarch64 test job hit the same buildkite-agent artifact download timed out after 120s error on agent darwin-aarch64-26-5-1-1, again before running any tests, so I'm going to stop retrying. Every other job in build #64232 is green (285/286) and there are no outstanding test failures. Once the agent's artifact downloads are healthy again, retrying that single job should turn the build fully green.

@The-LukeZ

The-LukeZ commented Jun 29, 2026

Copy link
Copy Markdown

It'd be a huge W when this is done eventually 👀

…annel-node26

# Conflicts:
#	src/js/builtins/BunBuiltinNames.h
#	src/js/node/child_process.ts
…response.created

Remove the three [ FAIL ] expectations entries this branch added by deleting
the vendored tests behind them instead of shipping known-failing files:

- test-diagnostics-channel-web-locks.js: needs the Web Locks API
  (navigator.locks), which Bun does not implement.
- test-diagnostics-channel-http.js: asserts the socket passed to the
  http.server.* channels is a net.Socket; Bun's node:http server hands out a
  Duplex wrapper. Fixing that belongs to the node:http compat work.
- test-diagnostics-channel-module-import.js: asserts the published url is the
  literal specifier ('http'); Bun's transpiler rewrites import("http") to
  "node:http" before the loader publishes module.import.

Also drop the unrelated [ MUSL ] quarantine of test-net-connect-memleak.js.

Deleting test-diagnostics-channel-http.js left http.server.response.created
with no coverage, so add a test for it to the diagnostics_channel suite.
Add the missing exception check after constructEmptyArray in queryObjects.
…le scope

Requiring node:diagnostics_channel lazily from the Worker and ChildProcess
constructors meant the module was evaluated after user code had already run.
"postMessageToThread survives a tampered Map prototype" clobbers Map.prototype
and then constructs a Worker, so diagnostics_channel loaded onto the tampered
prototype and threw out of the constructor.

Node creates both channels at module scope (lib/internal/worker.js,
lib/internal/child_process.js), which is also what net.ts and _http_server.ts
already do here. Do the same, so the channels are built while the module graph
loads rather than on first use.

Also route diagnostics_channel's own Map operations through the $-prefixed
intrinsics instead of the user-overridable prototype methods.
@robobun

robobun commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

All five review points addressed in 078e4bb (swap Module.prototype.require on subscribe instead of per-call check; hasModuleImportSubscribers bool gate + tryTraceModuleImport extraction; lazy dc load in _http_server/worker_threads/net/child_process). Later review follow-ups: http.Server.listen() mirrors net.server.listen (b879723), tracingChannel(null) throws ERR_INVALID_ARG_TYPE (cc52550), require $call forwarding with the 1-arg fast path (0ea7f36/6ef2005318), and the http1 fallback path (emit(\"connection\") / h2 allowHTTP1) now publishes the http.server.* channels (416dd52). A bot-suggested ?? change to queryObjects was reverted in 1c8fc53 after Ciro verified node itself uses ||.

CI: build #90247 passed 196/196; build #90391 is running on 416dd52. All review threads resolved.

Note: #34209 (net channels) and #34212 (module tracing) are later robobun PRs that implement strict subsets of what this PR already covers; once this lands those can be closed.

robobun and others added 2 commits August 3, 2026 23:35
…ms, bool gate for import tracing

- overridableRequire is back to the direct implementation (no per-call
  wrapper); when tracing:module.require:* gains subscribers,
  internal/module_tracing swaps Module.prototype.require to a tracing
  wrapper via the existing setter, and swaps back when the count returns
  to zero. Hooked via a new Bun-internal _onSubscribersChanged callback on
  Channel.
- moduleLoaderImportModule gates the tracing path on a new
  hasModuleImportSubscribers bool on Zig::GlobalObject (set from JS via
  jsSetHasModuleImportSubscribers), and the tracing body is extracted to
  tryTraceModuleImport().
- net.ts / _http_server.ts / child_process.ts / worker_threads.ts no
  longer require("node:diagnostics_channel") at module scope; channels are
  created lazily on first use (first connect/listen, first ChildProcess,
  first Worker, first non-upgrade request), matching the pattern dgram.ts
  already uses.
Comment thread src/js/internal/module_tracing.ts
Comment thread test/js/node/async_hooks/AsyncLocalStorage.test.ts Outdated
Comment thread test/js/node/test/parallel/test-diagnostics-channel-http.js
robobun added 2 commits August 3, 2026 23:57
- baseRequire is now captured once (??=) so a user wrapper installed between
  subscribe cycles that delegates to tracingRequire cannot recurse.
- The 15s-timeout comment now names http2/_http_client (which still load dc
  at module scope on main) instead of net/_http_server (now lazy).
Comment thread src/js/node/net.ts
robobun and others added 6 commits August 4, 2026 00:24
In Node http.Server inherits listen() from net.Server and so publishes on the
net.server.listen tracing channel. Bun's http.Server is Bun.serve-backed with
its own listen()/[kRealListen], so it now mirrors asyncStart/asyncEnd/error at
the same lifecycle points net.ts uses. https.Server shares the prototype so it
is covered too.
Comment thread src/js/node/diagnostics_channel.ts
robobun added 2 commits August 5, 2026 19:25
typeof null is "object", so the object branch read null.start and threw a
bare TypeError. With the null guard the constructor falls through to
channelFromMap, which throws the typed error, matching boundedChannel(null).
Comment thread src/js/node/v8.ts
robobun and others added 5 commits August 7, 2026 19:48
With ||, falsy-but-present formats ("", 0, false) silently defaulted to
"count" instead of reaching the format validation and throwing
ERR_INVALID_ARG_VALUE.
v8.ts: union — keep this branch's queryObjects alongside main's landed serialize/deserialize and snapshot stubs.

No-Verification-Needed: keep-both merge resolution of independent additions; CI verifies the merged tree
…guments)

The no-subscriber path allocated an arguments object on every require();
$overridableRequire reads exactly (id, options) and handles an undefined
options identically, so $call with the named params is equivalent.
Comment thread test/expectations.txt Outdated
Comment thread src/js/builtins/CommonJS.ts
Comment thread src/jsc/bindings/NodeDiagnosticsChannel.cpp Outdated
…y, self-include

- require(id) forwards 1 arg so the native $require keeps its
  skip-options gate (argumentCount distinguishes the arities again).
- Drop the test-net-connect-memleak.js expectations entry; the file was
  removed on main and the entry matched nothing.
- NodeDiagnosticsChannel.cpp includes its own header like NodeV8Module.cpp,
  so declaration and definition compile in one TU.
Comment thread src/js/node/_http_server.ts
connectionListenerHTTP1 (emit('connection', foreignSocket) and http2's
allowHTTP1 ALPN fallback) constructs req/res and emits 'request' without
publishing request.start/response.created/response.finish; in Node both
entry paths converge on parserOnIncoming which publishes all three.
dc.channel() returns the per-name singleton so these are the same channel
objects _http_server.ts uses. Accepted upgrades still return early before
any publish, matching the native dispatch path.
@robobun

robobun commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Closed #27881 (TracingChannel.hasSubscribers), #36353 (traceCallback return value and the hasSubscribers early exit), #29588 (http.server.* channels) and #30080 (child_process channels) in favor of this PR: main at f426a8e still has all four gaps, and this branch carries each of those changes. #27805, #29586 and #32472 stay open and are linked from the description.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

5 participants