node: v26 compat fixes — tls/net/dns/crypto, errors/whatwg/promise, worker/util/URLPattern, heap-prof/readline/GCProfiler, cli eval (+48 tests) - #35483
Draft
cirospaciari wants to merge 303 commits into
Draft
Conversation
…l tests)
Add node's internal/util test surface to the exposedInternals map:
normalizeEncoding (node-semantics wrapper over the Rust binding),
assertCrypto, getCIDR, isError, assignFunctionName, kEnumerableProperty,
kEmptyObject (reused from internal/shared), and WeakReference. Add
internalBinding('util').isInsideNodeModules to the test binding via
Error.prepareStackTrace CallSites.
In test/common, the --expose-internals branch of the Flags loop now
`continue`s instead of `break`ing so a later --expose-gc on the same
Flags line still installs the gc shim.
Vendors 7 node v26.3.0 tests verbatim:
test-internal-util-{normalizeencoding,assertCrypto,getCIDR,helpers,
objects,weakreference,isinsidenodemodules}.js
Keeps the strong-pin member from being write-only (lint) with identical semantics. [allow size]
… fallout it unmasks process.config.variables spelled the key v8_enable_i8n_support, so every vendored node test gated on common.hasIntl read undefined and skipped its Intl half. Fixing the key un-skips the Intl halves of 46 vendored files; 11 of them exposed real gaps, all fixed here: - buffer: implement transcode() — port of node's lib/buffer.js wrapper and src/node_i18n.cc converters (simdutf fast paths, U+FFFD decode / '?' encode substitution, node's exact error contract) - url: rework domainToASCII/domainToUnicode to node's semantics (WHATWG host parse on a ws:// base, ERR_MISSING_ARGS, string coercion, punycode label validation); expose the standalone UTS #46 toASCII for url.parse - url.parse: adopt node's current host-section scan (tab/CR/LF stripping, last-@ auth split), keep IPv6 literals as written, reject forbiddenHostCharsIpv6, throw ERR_INVALID_URL on hostname-spoofing routes - url.format: node's slashes/file:// rules, auth escaping via node's noEscapeAuth table, and the WHATWG URL branch with {auth,fragment,search,unicode} options - URL: reject invalid punycode (xn--) hosts for special schemes like ada does (constructor, href/host/hostname setters, parse/canParse); parse an empty base string (and fail) instead of ignoring it; the error is now node's exact plain "TypeError: Invalid URL" with the ERR_INVALID_URL code and node's `input` property - TextDecoder: node's ERR_ENCODING_INVALID_ENCODED_DATA message format ("The encoded data was not valid for encoding <label>") - util.inspect: node-format URL and URLSearchParams output, including the showHidden Symbol(context) URLContext reconstruction - USVString conversions: V8's "Cannot convert a Symbol value to a string" - internal/test/binding: add the icu binding (toASCII/toUnicode/ hasConverter) that node removed in nodejs/node#55156, for test-icu-punycode - vendor test/fixtures/wpt/url/resources (toascii.json, urltestdata.json from node v24.3.0) and icu-punycode-toascii.json; restore node's original Symbol message in six vendored searchparams tests All 46 hasIntl-gated vendored files pass on release and debug builds (the 35 previously green plus the 11 this exposed), along with the url, encoding, buffer, and util regression suites.
Four no-duplicate-conditional-property-access sites from the Intl fallout commit (inspect constructor lookup, URL context fields, url format/resolve). [allow size]
Single conflict in internal/shared.ts: the branch's guardCallback/ reportUncaughtException block and main's new resistStopPropagation were added at the same location; kept both, exports unioned. [allow size]
…drain Async fs completions post into concurrent_tasks straight from the work pool, with no ScriptExecutionContext gate. A worker terminated mid-flight (or a process exiting with fs ops pending) could run its final queue drain before the pool thread's post landed, so the ConcurrentTask shell and its AsyncFSTask payload sat in the dealloc'd-without-Drop VM's queue forever — LeakSanitizer aborts the asan lanes on both worker-fshandles termination tests. Count posters on the JS thread before the pool hand-off, decrement on the pool thread right after the post, and have both shutdown paths (worker shutdown and global exit) spin for zero before draining. The wait is bounded by syscall latency; nothing can schedule new posters past either wait point because no user JS runs after it. The cp and recursive-readdir pool tasks post through the same queue and are not yet counted; they need the same begin/end pairing as a follow-up. [allow size]
util: implement getSystemErrorMap, getSystemErrorMessage and
_errnoException on top of a new native uv_e ENTRIES table (the same
table getSystemErrorName reads, so names can never diverge), with
libuv's uv_strerror messages keyed by name.
constants: require('constants') gains the binding-parity keys it was
missing (EXTENSIONLESS_FORMAT_*, obsolete SSL_OP_* zeros, ENGINE_METHOD_*,
RSA padding fallbacks) and is now frozen, matching node's lib/constants.js.
process.binding('uv'): add the missing ENOEXEC entry and drop the
non-node colon from the "Unknown system error" fallback in errname().
internal/test/binding: internalBinding('constants') now returns the real
process.binding('constants') object; internalBinding('uv') exposes the
full UV_* code set plus errname()/getErrorMessage(); new credentials
(safeGetenv) and buffer (kMaxLength/kStringMaxLength) bindings; unknown
bindings now throw with code ERR_INVALID_MODULE like node.
bun:internal-for-testing: expose internal/child_process (the real
ChildProcess class plus a port of node's getValidStdio).
test/common: serve byte-identical node v26.3.0 copies of internal/webidl,
internal/socket_list, internal/fs/utils and internal/crypto/{util,webidl,
hashnames} through a small loader (emulated primordials + node-exact
error formatters), and teach the internal/options shim about
--expose-internals.
The 20 added vendored tests pass 3x against a release build, fail under
a tampered shim, and pass on node v26.3.0.
…ygiene - deepEquals (checkPrototypes): DataViews compare bytes, ArrayBuffers and DataViews fall through to own-property comparison, and non-index property names must be own on both sides (a prototype-chain lookup could match an inherited key). - assert: the Map identity fast path now consumes the matched entry so one actual entry cannot satisfy two expected entries; DataView/ArrayBuffer partial comparison also checks own enumerable properties; URL comparison uses a slot-backed brand check instead of instanceof. - require.resolve: builtin ids resolve before options.paths validation, like node. - ResolveMessage: String(err) routes through the same node_message() as err.message; BuildMessage gets Error.prototype in its chain, matching ResolveMessage. - process: DEP0111 stack walk stops once the latch is set; the self-kill signal-handler map read is main-thread-gated. - inspect: URLSearchParams formatting iterates via captured prototype methods; debuglog captures Math.floor/Number. - pathToFileURL: empty extended-UNC servername check uses the prefix length. - tests: drain stderr pipes (fs runScript, heap-prof), exercise the worker DEP0119 latch twice, drop a stray fixture file. - comments: correct stale rustdoc/SAFETY notes (hot reloader kill-signal invariant, compile cache allocator, rustdoc attachment). [allow size]
Matches the documented short_aliases contract: a mapping can no longer touch the -/-- sentinels or a positional. No behavior change for the in-tree tables (all froms are flag-shaped). [allow size]
Port of node's getPathFromURLWin32/getPathFromURLPosix for explicit
{windows} callers (drive letters, UNC hosts via domainToUnicode, encoded
separator rejection, node's exact error messages); the host-platform path
stays on the native Bun.fileURLToPath fast path.
[allow size]
The is_emitting_watch_kill_signal early return unwound back into the emitting listener, so code after process.exit() ran and remaining signal listeners fired. Replace the process there instead (the 'exit' event has already been dispatched by the caller), restoring the noreturn contract the BunProcess.cpp comment documents. Regression test proves the repro (the stray should-not-write.txt this branch once carried was this bug's artifact). Also gate the new DataView byte comparison to the node-parity deepEquals instantiations: Bun.deepEquals/bun:test keep their released plain-object semantics for DataViews. [allow size]
Replace the PR-added arrow functions in runtime modules with hoisted named functions (async_hooks hook wrappers and GC-destroy callback, crypto's node-style .then delivery via bound helpers, the test-binding stack/env shims, the stdio reducer), and add node v26.3.0 source citations to the createHook and formatTime ports. [allow size]
…, stray binary - Remove the accidentally-committed garbage-env ELF, compile the fixture into a temp dir, and gitignore the root path. - Flush the compile cache in the self-kill at-exit path, matching node's RunAtExit ordering (env.cc AtExit(FlushCompileCache)). - Wire --watch-kill-signal on Windows: store the signal so the platform-agnostic pre-reload emit runs JS handlers; sigaction stays unix. - Wrap util.isDeepStrictEqual in node's 2-arg forwarder (lib/util.js) so the internal skipPrototype argument no longer leaks to the public API. [allow size]
Node v26 follows Unicode 16 through ada::idna (the ICU path was removed in nodejs/node#55156); the ICU bundled with WebKit carries Unicode 15.1 data, so domainToASCII returned '' for inputs node maps successfully: U+180E and U+206A..U+206F became ignored, U+04C0 and U+2183 gained lowercase mappings, and five CJK compatibility ideographs got corrected mappings. Pre-apply the delta in the node:url IDNA entry points; WPT toascii cases 66/74/81/82/83 pin every branch of it. The lane split on CI followed each runner's ICU data version, not the architecture. [allow size]
Union resolution in test/cli/watch/watch.test.ts: both sides added a test at the same location (the branch's watch kill-signal process.exit guard and main's FileWatcher thread-spawn failure propagation test); kept both. [allow size]
…in paths bypass
- partialDeepStrictEqual's identity fast path now reserves the matched
entry's index so the index loop and the fast path cannot both consume
one actual entry (node throws for the double-match shape); the
skipPrototype map comparison gets the same reservation for consistency
(its observable divergence traces to the Bun.deepEquals short-circuit,
tracked separately).
- require.resolve bypasses paths validation only for real builtins via
Module.isBuiltin, matching node's normalizeRequirableId gate:
require.resolve("node:nope", {paths:[0]}) now throws
ERR_INVALID_ARG_TYPE instead of MODULE_NOT_FOUND.
- The work-pool poster counter now also covers AsyncReaddirRecursiveTask
and AsyncCpTask (the two remaining enqueue_task_concurrent posters), so
shutdown's wait_for_concurrent_posters covers every fs completion post.
[allow size]
…f-kill process.kill(pid-self, sig) flushed the compile cache through persist_at_exit(), whose process-global DONE latch assumed the signal was fatal. When it isn't — a worker self-kill with the handler on the main thread, or a default-ignored signal like SIGWINCH — the process survives with the latch set, so the real exit's persist became a no-op and modules loaded after the kill never reached the cache. The self-kill path now uses a non-latching persist_now(); the exit latch stays where exits happen. Regression test: self-kill with SIGWINCH, then require a module — both modules must be cached at exit (pre-fix only the first was). [allow size]
…in node The 2-arg forwarder added while addressing review assumed node hides the internal skipPrototype argument, but node v26.3.0 exposes it: util.isDeepStrictEqual.length === 3 and isDeepStrictEqual(a, b, true) skips prototype identity — asserted by the vendored upstream test-util-isDeepStrictEqual.js, which the forwarder broke (both aarch64 lanes; x64 lanes skipped the file). Restore the direct export of the 3-arg comparator; the comment now cites the verified behavior. [allow size]
Drop the sleep-and-retouch fallback (pipe output written before the reader attaches waits in the kernel buffer, so no start can be missed), reassemble lines across chunk boundaries before matching, bound the test with the file's standard 10s timeout, and fail loudly if the child's stdout ends early instead of letting the absent-file expects pass vacuously. [allow size]
…rding The branch's ResolveMessage node_message() now emits node's exact 'Cannot find module' text (with Require stack) for require/require.resolve failures, so the fixture's old Bun-adapted /Cannot find package/ regexes went stale. Restore upstream v26.3.0 wording; the only remaining deviation (dropping upstream's /^Error: / anchor, since the error class stringifies as ResolveMessage) is commented inline. [allow size]
The line editor redrew the prompt per keystroke (CR + erase-line + reprint + cursor move) even when stdin/stdout were pipes. Besides being noise no pipe reader wants, each redraw is several tiny writes, and unix socketpair send buffers account per-skb overhead — so a spawn(..., stdio: 'pipe') parent that doesn't read promptly wedged the whole REPL inside a blocking write(2) after a few hundred keystrokes (test-repl-dynamic-import timed out on every Linux release lane this way). Non-TTY mode now mirrors node's terminal:false repl: no echo, no redraw, prompt printed once per line. Losing the echo unmasked two directive bugs the old output hid: a lone 'use strict' prologue was consumed into scope state and dropped before the repl transform ran (evaluating to undefined instead of the string, and with zero parts the transformed IIFE was silently discarded), and 'use asm' was blanked to an empty statement. Reinject the consumed directive like the CJS wrapper does, create a part when none exist, and keep 'use asm' a string statement in repl mode — all three now match node v26.3.0. Also gitignore the compiled-bundler test outputs (entry, entry.js.map, nosourcemap_entry) that land in the repo root when tests run from there. [allow size]
The JS requireResolve wrapper now only extracts options.paths; the
builtin bypass (node resolves builtin ids before validating paths, so
require.resolve('node:fs', {paths:[0]}) must not throw) and the paths
validation both live in functionImportMeta__resolveSyncPrivate. The
native error contract now matches node v26.3.0 exactly: non-array paths
report the 'options.paths' property, elements throw per-index
ERR_INVALID_ARG_TYPE (paths[0]), and a non-string id names 'request'.
Verified case-by-case against node.
[allow size]
isURL is now a C++ host function doing inherits<JSDOMURL>() — immune to prototype and Symbol.hasInstance tampering — replacing the try/catch href-getter probe. The partialDeepStrictEqual typed-array, DataView, and ArrayBuffer branches call a native ordered-with-gaps containment scan instead of allocating Uint8Array copies and looping in JS. Element equality follows Object.is at storage width (all NaNs equal, +0/-0 distinct, per-dtype element alignment including Float16 and BigInt64), and detached buffers throw node's exact TypeError. Verified identical to node v26.3.0 across gap/order/NaN/-0/bigint/DataView/ArrayBuffer probes. [allow size]
The NODE_CHANNEL_FD unset_cloexec and caught-signal SIG_DFL reset were inside on_before_reload_process_linux under an OS(LINUX) || OS(FREEBSD) gate, so macOS skipped the hook entirely: usockets CLOEXEC'd the IPC fd and execve closed it, and the reloaded image attached to a dead fd (the "IPC to the parent survives a --watch reload" test failed on darwin). Renamed to on_before_reload_process_posix under !OS(WINDOWS); the close_range sweep stays Linux/FreeBSD-only, and reload_process calls the hook on all unix.
formatWhatwgURL rebuilt the href from .search/.hash, which both return ""
for a null and an empty-string component, so url.format(new URL("http://x/?"),
{unicode: true}) dropped the marker where node keeps it. Derive presence
from the href (the only bare # is the fragment delimiter; the char before
it or end-of-href is ? iff the query is present-and-empty).
CachedBytecode::__bun_jsc_generate_cached_bytecode and RuntimeTranspilerCache::OutputCode::byte_slice are called from NodeCompileCache.rs / jsc_hooks.rs (different crate than the pub(crate) sweep narrowed them to); node_process get_cwd uses bun_sys::getcwd directly (Syscall alias removed); repl editor_mode bool renamed to input_mode enum.
…g does not trip hasInlineStorage
{...process.binding("uv")} (internal/test/binding.ts) asserts
hasInlineStorage() in JSObject::inlineStorage on a constructEmptyObject(...,
0) object under the merged WebKit. Use the default-capacity overload; the
binding carries ~85 properties anyway so zero inline was a non-optimization.
…ered Error.* in isInsideNodeModules isInsideNodeModules read Error.captureStackTrace/stackTraceLimit/ prepareStackTrace at call time inside a try/finally with no catch, so a deleted captureStackTrace or a non-writable stackTraceLimit made url.parse throw. Capture captureStackTrace at module scope (matching assertion_error.ts /inspect.js/quic.ts) and swallow any throw from the body and the restore so callers never observe it.
The hoisted deliverCallbackResult/Error + .bind(cb) pattern allocated one bound-function object per .bind call (same count as the arrows it replaced), so the 'no closure allocates per invocation' comment was wrong; and .bind resolves through user-mutable Function.prototype.bind where arrows do not.
…-uncaught # Conflicts: # src/runtime/dispatch.rs # src/runtime/node/node_fs.rs
…SIGPWR on Linux) in the pre-execve disposition reset ipc.rs: the 276e176 merge moved the cpp bridge out of crate:: scope; the two advanced-buffer wrappers now resolve through bun_jsc::cpp like their ipc_serialize/ipc_parse siblings. c-bindings.cpp: on Linux g_wtfConfig.sigThreadSuspendResume is SIGPWR (30), not an RT signal, so the s >= SIGRTMIN break did not skip it; resetting SIGPWR to SIG_DFL while the SamplingProfiler/concurrent GC are still pthread_kill'ing the JS thread with it terminates the process instead of reloading. Skip it explicitly under OS(LINUX), mirroring BunProcess.cpp's process.on guard.
…into claude/node-v26-fix-tls # Conflicts: # src/cares_sys/c_ares.rs # src/clap/lib.rs # src/clap/streaming.rs # src/js/internal/test/binding.ts # src/js/internal/util/inspect.js # src/js/internal/validators.ts # src/js/node/readline.ts # src/js/node/tls.ts # src/jsc/JSGlobalObject.rs # src/jsc/VirtualMachine.rs # src/jsc/bindings/BunHeapProfiler.cpp # src/jsc/bindings/BunHeapProfiler.h # src/jsc/bindings/BunProcess.cpp # src/jsc/bindings/ErrorCode.cpp # src/jsc/bindings/ErrorCode.ts # src/jsc/bindings/JSDOMExceptionHandling.h # src/jsc/bindings/webcore/SerializedScriptValue.cpp # src/options_types/context.rs # src/runtime/cli/Arguments.rs # src/runtime/cli/mod.rs # src/runtime/cli/run_command.rs # src/runtime/node/node_process.rs # src/runtime/node/path.rs # test/js/node/test/common/index.mjs
… the try so a throwing getter is swallowed too
…soned CallSite.prototype.getFileName degrades to false instead of escaping to url.parse
…into claude/node-v26-fix-tls # Conflicts: # src/jsc/bindings/BunProcess.cpp
…llout
- add src/jsc/bindings/BunHeapProfiler.h: GeneratedJS2Native.h #includes it
for the $newCppFunction("BunHeapProfiler.cpp", ...) calls in v8.ts
- web_worker.rs: parent_ref binding was removed by #36571; deref (*parent)
like the neighbouring fields
- permission.rs: switch std::sync::RwLock -> bun_threading::RwLock and
std::env::var -> bun_core::env_var::NODE_OPTIONS (disallowed_types/methods);
contains() instead of iter().any()
- path.rs: re-expose resolve_{posix,windows}_t as pub(crate) for permission.rs
- clap lib.rs: Diagnostic fields pub (read by bun_runtime Arguments.rs)
- run_command.rs: exec_check pub -> pub(crate) (unreachable_pub)
- BunHeapProfiler.rs: then(|| ..) -> then_some (unnecessary_lazy_evaluations)
- Timer.rs: move SAFETY comment onto the unsafe block it documents
…into claude/node-v26-fix-tls
…syncHook emitImmediateAsyncHook does a raw JSC::call with no local scope. Two back-to-back calls without a check between them trip the JSC exception-scope verifier (executeCallImpl -> executeCallImpl) under validateExceptionChecks. Surfaces in bake/dev tests on x64-asan since the dev server runs inside AsyncLocalStorage.run and react-dom schedules via setImmediate.
…l.iterator cannot hide Buffers from the envelope
…descriptor value check so a polluted Object.prototype.value does not make accessor descriptors walk onto the stack
…p describe, JSEvent inspect exception check, fs options.recursive name, oxlint + dead-symbols - Arguments.rs: drop the pre-permission-model '--permission is not supported' rejection block; the implementation landed earlier in the file and the rejection overrode it (unblocks ~20 vendored permission tests). - util.test.js: close the first util.debuglog describe so the two merged debuglog test blocks do not overlap (was a parse error). - JSEvent.cpp: RETURN_IF_EXCEPTION after utilInspectFunction() in the Event inspect.custom, matching the other three call sites. - node_fs.rs: name options-bag boolean members 'options.<key>' so ERR_INVALID_ARG_TYPE says 'property', matching Node. - crypto.ts: drop the placeholder arity params (length is overridden via defineProperty); domain.ts: read stack.length once. - validators.ts: remove the never-referenced validateInternalField export. - transpiler.test.js: prefix the deeply-nested-unary '-e' payload with a space now that Bun matches Node's 'requires an argument' for a dash-leading value. - node-dns.test.js: compare shape against localhost instead of racing two google.com lookups (google.com now has multiple A records). - error-event.test.ts: refresh Bun.inspect(ErrorEvent) snapshots for the new Node-compat Event inspect.custom output.
…nScope; restore --allow-* ⇒ --permission gate; restore merge-lost fs.write/read arg messages - permission.rs publish_permission_event called a raw extern-C requireId wrapper then global.has_exception(), tripping JSC exception-scope validation on ~17 --permission tests. Wrap the call in top_scope! and read the exception through it. - Arguments.rs: re-add the ERR_MISSING_OPTION '--permission is required' rejection for --allow-* without --permission (the rejection block was removed wholesale along with the obsolete 'not supported' message). run-eval.test.ts updated: the model is now implemented, so assert it enables process.permission instead of being rejected. - node_fs.rs: restore the two-stage validateOffsetLengthWrite/validateInt32 length message and the empty-read-buffer 'Received <inspected>' suffix from ac5866b — both hunks were dropped by a later merge. - test-c-ares.js: sync the null-hostname case with node v26.3.0, which removed DEP0118 and rejects an empty hostname.
…warning; refresh Event inspect snapshots
- events.on()'s closeHandler now resets paused=false like node, so next()
stops calling emitter.resume() after a close event drained the queue —
readline's resume() throws ERR_USE_AFTER_CLOSE otherwise
(test-readline-async-iterators.js with the restored 10k-line case).
- http.ts: the ported-lazy debuglog only emits the sensitive-data warning
on first call; Bun's client never calls it. Call it once when enabled
so NODE_DEBUG=http still warns (test-http-debug.js).
- inspect.test.js / test-eventtarget.js: Event subclasses now install
node's [util.inspect.custom] on the prototype. Refresh Bun.inspect
snapshots for the {type, defaultPrevented, cancelable, timeStamp}
shape, assert inspecting the bare prototype throws (brand check, like
node), and drop the Bun-only '[Event]' depth:-1 special-case.
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.
Combines three same-base compat PRs into one review unit (#35487 and #35491 are closed pointing here). +31 upstream Node v26.3.0 tests vendored byte-verbatim, ~20 runtime fixes. All 31 pass CI-style one-process-per-file; preflight clean (2 justified skips below).
tls / net / dns / crypto / timers (+14)
privateKeyEngine/privateKeyIdentifiervalidation with Node's ordering invalidateSecureContextOptionskSetKeepAliveInitialDelaystores seconds (Node/libuv convention); call sites convert to ms at the native handleSocket[kReinitializeHandle]defaults the replacement handle like Node's TLSSocket overrideguardCallbackenters the captured domain;randomBytes/randomFill/randomIntbridge native callbacks through a portedDomain._errorHandlerdns.Resolver({ maxTimeout })wired through toARES_OPT_MAXTIMEOUTMSclearTimeout/clearIntervalset_idleTimeout = -1(Node's unenroll) in the Rust timerserver.listenon non-IP hosts resolves viadns.lookup({all:true}), filtering fe80::/10 link-local, guarded by_listeningIdsetMaxSendFragmentreturns BoringSSL's verdict (clamp + success) instead of an OpenSSL-style range gate; the existing bun test that codified the divergence is updatedJustified skips:
test-tls-enable-trace{,-cli}.jsskip with1..0because BoringSSL does not compileSSL_trace()— identical behavior to a Node built against BoringSSL (HAVE_SSL_TRACE: false).errors / whatwg / promise (+11)
process.emit; native-context CallSite arrays were empty and crashed Node'scommon.mustNotCall)"<name>: <message>"stack headerPromiseRejectionHandledWarningonly when norejectionHandledlistener (Node's ordering)[nodejs.util.inspect.custom]function name on URL/URLSearchParams/webstream prototypesbreak→continue(--expose-gc --expose-internalscombinations never installed the interceptor)internal/errors.js+internal/encoding*with the binding entries they needworker_threads / util / url / URLPattern (+6)
ERR_WORKER_UNSERIALIZABLE_ERROR, Node-style crash on unknown control types,internalBinding('worker').getEnvMessagePortSymbol(x)/function foo() {} could not be cloned., MessageChannel→needs-transfer)receiveMessageOnPort(broadcastChannel)implemented; fixed a real hang:bc.ref()afterclose()pinned the event loop foreverutil.debuglogport (callback arg,enabledaccessor, Node output format)this; nowIllegal invocationtest/js/node/test/common/index.jsdrift is intentional and documented: the flag-scan fix above plus a worker-gc shim.Each fix's tests fail on the unfixed build (verified per-fix during development). Full disposition logs for the three slices:
errors/whatwgPR body history in #35487,workerin #35491, both closed pointing here.heap-prof / readline / GCProfiler / internal shims (+16, folded from #35486)
--heap-profwrote a heap snapshot graph into.heapprofile; now synthesizes the correct V8 sampling heap profile ({head, samples}) from JSC's SamplingProfiler, incl. workerexecArgvsupport. Bun's owntest/cli/heap-prof.test.tsasserted the old wrong shape and is updated in the same change (13/13 on the fix, 5/13 fail on the unfixed base).Promise.$reject,ERR_USE_AFTER_CLOSEguards, promisesquestion()rejects instead of throwing, ctrl+C/D AbortError, constructible promisescreateInterfacev8.GCProfilerimplemented onJSC::HeapObserver(was a stub)internal/assert,internal/fs/sync_write_stream,internal/net,internal/event_target(NodeEventTarget)cli eval / --print (+1, folded from #35488)
--printsemantics: print once at drain orprocess.exit(), promises rendered verbatim via the node util.inspect port;-p -e/--print --eval=-42exit-9 parsing; barecryptoresolves to node:crypto in-e/-p(also fixed an infinite-recursion hazard in node:crypto's webcrypto export). Two suite assertions pinning the old output format updated (child_process env quoting, install snapshot).Cross-branch interaction fixed on the combined branch
require('internal/net'): the vendored verbatim shim (pure helpers likeisLoopback) exports freshly-minted look-alike symbols; the harness require hook now merges it key-for-key with Bun's real module-private socket symbols recovered from a probe socket. The superseded plugin-shim interceptor was removed.