feat(compat): integrate upstream Bun PRs for OpenClaw - #1
Merged
Merged
Conversation
Ports five Node.js v26.3.0 test/parallel worker tests that are absent from the suite and pass against current main, byte-identical to upstream: test-worker-dns-terminate.js terminate() with an in-flight dns.lookup test-worker-http2-stream-terminate.js terminate() with in-flight http2 streams test-worker-memory.js RSS does not grow across worker churn test-worker-unsupported-eval-on-url.mjs eval:true rejected for a URL filename test-worker-cleanup-handles.js handles are cleaned up on worker exit Verified on a debug build of main (3/3 runs each), and again with BUN_JSC_validateExceptionChecks=1 to match the asan shard. No source changes: these cover behaviour Bun already implements, so they guard against regressions rather than fix anything. Note test-worker-unsupported-eval-on-url.mjs fails on 1.3.14 and passes on main - the ERR_INVALID_ARG_VALUE message now matches Node exactly. No-Verification-Needed: test-only change, no runtime surface to drive
…dy pass parallel/test-worker-dispose.mjs await using / Symbol.asyncDispose sequential/test-worker-fshandles-error-on-termination.js terminate() with open FileHandles sequential/test-worker-fshandles-open-close-on-termination.js These are the first test-worker-* files under test/js/node/test/sequential/; discovery is glob-based so no manifest change is needed, and the runner already sets BUN_FEATURE_FLAG_NO_ORPHANS=1 for that directory. Verified 5/5 each under the runner's own env (bun run --config bunfig.node-test.toml, NO_ORPHANS, BUN_JSC_validateExceptionChecks=1) and on a release binary. Both fshandles tests are self-contained: no ports, no chdir, no shared files, so they cannot interfere with other sequential tests. Slowest is ~4.5s on a debug build against a 20s budget. No-Verification-Needed: test-only change, no runtime surface to drive
… from LeakSan The x64-asan shard surfaced two pre-existing bugs in these new tests: test-worker-dns-terminate.js hits a heap-use-after-free (READ of size 4, thread T6) when a worker is terminated with a dns.lookup in flight: GlobalData::drop tears the Resolver down by value while in-flight DNSLookups still hold an IntrusiveRc, so the later deref reads the freed RefCount (Cell<u32>). Dropping the test - this needs a real fix in the DNS teardown, and it cannot be suppressed: worker VMs are destroyed on exit regardless of BUN_DESTRUCT_VM_ON_EXIT (VirtualMachine.rs), and ASAN_OPTIONS cannot hide a UAF. Linux-only; macOS uses lib_info rather than lib_c. test-worker-fshandles-open-close-on-termination.js is a genuine leak (ConcurrentTask boxed for a VM being torn down) and is delisted from LeakSanitizer, which is what no-validate-leaksan.txt actually controls. No-Verification-Needed: test-only change, no runtime surface to drive
The asan shard flagged the sibling too, on a different allocation site: an in-flight AsyncFSTask<Open> rather than JSC deferred work. Same class - terminating a worker with work still in flight leaks the ConcurrentTask boxed for it, because the VM is torn down before the task runs. Both tests pass; only the exit-time leak check fails.
…g them Reverts the no-validate-leaksan.txt entries and removes both sequential/test-worker-fshandles-*-on-termination.js. Needing a LeakSanitizer exemption means the test is not passing: terminating a worker with work still in flight leaks the ConcurrentTask boxed for it (an AsyncFSTask<Open>, or JSC deferred work via DeferredWorkTimer) because the VM is torn down before the task runs. That is a real leak and should be fixed in the teardown rather than hidden, so these two tests stay out until it is. What remains is 5 parallel tests that pass with no exemptions.
Node publishes the newly-constructed Worker on the 'worker_threads'
diagnostics channel at the end of the Worker constructor
(lib/internal/worker.js). Bun never did, so dc.subscribe('worker_threads')
was silently dead.
Resolve the channel at module load rather than lazily in the constructor:
diagnostics_channel keys its registry off a Map, so a lazy require would
build the channel out of whatever user code had tampered with by then
(this broke worker_threads.test.ts's tampered-Map-prototype test), and a
module-load strong ref also pins the channel against its WeakRefMap
registry so subscribe() cannot race a GC. http2.ts and _http_client.ts
already require diagnostics_channel at module scope.
Verified against the node v26.3.0 binary: identical payload ({ worker }),
no publish without subscribers, no fire when subscribing after
construction, one publish per Worker in order, unsubscribe stops it, and
a nested worker publishes on its own thread's channel. All 36 node
test-diagnostics-channel-*.js pass; vendored test-worker* is 105/2 vs
104/3 before.
…d timeOrigin Three independent Node compat fixes, each with its upstream v26.3.0 test. All three verified against the node v26.3.0 binary. async_hooks WORKER init (test-worker-hasref.js) Bun delivered `init` for TickObject only, so a hook watching for WORKER resources never fired. Emit one from the Worker constructor into the same tickInitHooks array, exposing hasRef(): it follows ref()/unref() and reads back undefined once the thread has exited, as node's handle does. ref() and unref() no-op after exit rather than resurrecting it (lib/internal/worker.js nulls kHandle before emitting 'exit'). A throwing init hook is fatal, as in node, mirroring the TickObject site in ProcessObjectInternals. createHook also invoked `init` bare, so `this` was undefined inside it. Node calls init as a method on the AsyncHook instance (lib/internal/async_hooks.js), which is why `this.disable()` inside init works there and threw here. Worker error clone (test-worker-error-stack-getter-throws.js) Cloning an Error reads `stack`, so a throwing Error.prepareStackTrace took the whole clone down and the error surfaced as Bun's pretty-printed text. Node drops just the unreadable stack (lib/internal/error_serdes.js TryGetAllProperties). Retry once with an own undefined stack, only on the failure path, only for an ErrorInstance, using setStackPropertyAlreadyMaterialized so the retry does not re-run the getter. performance.timeOrigin (test-perf-hooks-worker-timeorigin.js) Each worker VM called Instant::now(), so a worker's timeOrigin drifted from the main thread's by the spawn delay (~265ms with a forced gap; node: 0.000). timeOrigin is the process start and every thread reports the same one, so capture it once. This also makes Bun.nanoseconds() match its own documented "nanoseconds since the process started". Vendored test-worker* goes 105/2 -> 113/3 (the 3 are pre-existing and unrelated); bun's worker_threads suite stays at its 2 baseline failures; async_hooks and perf_hooks unchanged.
Node's BroadcastChannel#ref() returns `this` so it chains (lib/internal/worker/io.js); Bun's returned undefined while unref() already returned the channel, so the two disagreed in the same file. This is the other half of oven-sh#19810, which fixed the identical return-value bug in unrefBody and left ref() behind. jsRef()/jsUnref() are mirrors — both void, both guarding m_hasRef around an event-loop ref count — so refBody now matches unrefBody exactly. Verified against node v26.3.0: `bc.ref() === bc` and `bc.unref() === bc` on both, and util.inspect(bc.ref()) is byte-identical. test/js/web/broadcastchannel 16/0.
Node reports 'online' once the worker thread has bootstrapped, before user
code (lib/internal/worker.js). Bun posted it only after the entry-point
promise settled, so a worker whose top-level never returns never reported
online at all:
new Worker('while(true);', { eval: true })
node: ONLINE fired | terminate() -> 1
bun : no online, ever
dispatchOnline did two separable things: the Pending->Running flip under
m_pendingTasksMutex, which also gates message routing, and a postTaskToParent
of the open event. Only the second belongs before the entry point, so split
them: dispatchOnlineEvent() posts the event and is called ahead of the load;
dispatchOnline() keeps the state flip exactly where it was, leaving message
routing and fireEarlyMessages untouched. Moving both regresses the suite.
Removes a comment claiming the flip must precede the post or a parent 'online'
handler calling getHeapSnapshot() would see ERR_WORKER_NOT_RUNNING. It cannot:
postTaskToWorkerGlobalScope queues on Pending and returns true, rejecting only
for Closing/Closed, as JSWorker.cpp already documents. Verified by driving it —
getHeapSnapshot() from inside the online handler resolves.
Also fixes online-before-error: a worker whose entry throws now reports
["online","error"] like node, where it previously reported only ["error"].
Verified against node v26.3.0: spinning worker goes online and terminate()
resolves to 1; online fires exactly once; ordering vs a worker message and vs a
throwing entry both match. Failure set of the worker suite is unchanged
(strict subset of baseline); the new test fails 0/3 without the fix, 3/3 with.
Known gap left: a worker with an unresolvable specifier still skips 'online'
(node fires it) — the event goes out after entry resolution.
Only src/js/node/worker_threads.ts conflicted, in three hunks where oven-sh#34338 ("don't hang when captured stdout/stderr is never consumed") and this branch touch the same lines. oven-sh#34338 removed the #stdoutAutoPipe/#stderrAutoPipe fields and moved the stdio port ref/unref out of ref()/unref() — ports now manage their own ref via makePortReadable's incrementsPortRef. This branch only added #hasRef bookkeeping there, so main's structure is taken wholesale and only the two `if (!this.#exited) this.#hasRef = ...` lines and the field are kept. async_hooks.ts (oven-sh#31825) and VirtualMachine.rs (oven-sh#34293, oven-sh#32498) auto-merged. `git diff origin/main -- src/js/node/worker_threads.ts` is a pure addition: zero deleted lines, so nothing from oven-sh#34338 or oven-sh#31825 is reverted. Verified on the merge result: test-worker-hasref, test-worker-error-stack- getter-throws, test-perf-hooks-worker-timeorigin, test-diagnostics-channel- worker-threads and the new "online fires before the entry point finishes" all pass; oven-sh#34338's own repro still exits 0 like node; BroadcastChannel ref()/unref() and the 'online' timing fix both still match node v26.3.0.
- drop test-worker-memory.js: RSS ratio assertion fails on macOS aarch64
(builds 74530 and 74665), same bar as the fshandles tests
- emit the WORKER async_hooks init before the diagnostics_channel publish
so the observable order matches node (AsyncWrap fires mid-constructor)
- hasRef() starts false when Bun's { ref: false } option is passed
- update the stale async_hooks_tick header comment (WORKER now flows there)
- test that BroadcastChannel ref()/unref() return the channel
`bun -pe "1+1"` printed a ReferenceError for `e`. `-p` is declared
`-p, --print <STR>` and the short parser accepts attached values, so `-pe X`
read as `-p` carrying the value `e` and evaluated that identifier. Node has the
same ambiguity and resolves it the same way: `-pe` is not a short at all, it's a
whole-token alias applied before short parsing (AddAlias, node_options.cc).
Adds an alias table to ParseOptions, applied in StreamingClap::parse_next_arg —
the one place a token is classified as a flag. Option values and `--` targets
are pulled straight off the iterator and never pass through it, so they stay
verbatim; node scopes its own lookup to the option-name branch for the same
reason. Only AutoCommand/RunAsNodeCommand pass the node table: `bun run -pe` and
`bunx -p` (where -p means --package) are unaffected.
process.execArgv re-parses argv against a set built from AUTO_PARAMS to find
value-taking flags. An alias is not a param, so `-pe` missed the set and the
code string was dropped from execArgv; the set now also takes any alias whose
target takes a value, derived from the same table rather than hardcoded.
Verified against node v26.3.0, matching byte-for-byte:
bun -pe '1+1' -> 2
bun -p '1+1' / -e 'console.log(3)' -> 2 / 3 (unchanged)
bun -- -pe -> Script not found "-pe" (not "-p")
bun -e -pe -> evaluates "-pe" (value intact)
bun script.js -pe x -> ["-pe","x"] (argv intact)
bun -pe 'process.execArgv' -> ["-pe","..."]
test-preload-worker.js goes 0/6 -> 6/6; it needed only this.
Not addressed: `-pe=x`. Node rejects `-pe=3+3` and `-p=1+1` alike ('=' splitting
is long-flags-only there); bun evaluates them. That divergence predates this and
is orthogonal.
Node writes one CPU profile per thread when the process is started with --cpu-prof, and honours a Worker's own execArgv. Bun set cpu_profiler_config only on the main-thread run path, so a process with a worker wrote 1 profile where node writes 2, and `execArgv: ['--cpu-prof']` did nothing at all. VirtualMachine::on_exit already writes whatever config its VM carries, so the worker side only needed the config and a profiler start. Inheritance follows node: execArgv absent means inherit the parent's, and an explicitly-provided list — even an empty one — replaces it, so a worker with `execArgv: []` is not profiled (node_worker.cc resets to fresh defaults whenever execArgv is given). Bun already carries that distinction as Option<&[...]> from WorkerOptions; the worker's own --cpu-prof simply wins. The sampling interval is a thread_local, so it is set on the worker thread — without that a worker silently sampled at the 1000us default instead of the requested rate (312 vs 1529 samples at --cpu-prof-interval 100). Default profile filenames gain the thread id for workers only, so concurrent writes can't land on the same name; the main thread keeps the name it has always had. A custom --cpu-prof-name still collides across threads, which is node's behaviour too (it only thread-stamps the default name). --cpu-prof-dir/-name are not read from a worker's execArgv: their values would have to outlive the parse, and no test needs them. set_sampling_interval now clamps instead of panicking — a Worker's execArgv can reach it, and '--cpu-prof-interval 3000000000' parses as u32 but not as c_int. Verified against node v26.3.0 — profiles written, per worker options: no execArgv node 2 bun 2 execArgv: [] node 1 bun 1 execArgv: ['--no-addons'] node 1 bun 1 test-cpu-prof-dir-worker and test-cpu-prof-worker-argv both go 0/3 -> 5/5; node itself is 10/10 on the latter. Bun's worker_threads suite is unchanged.
create_exec_argv added -pe to the value-taking set unconditionally, so bun run -pe script reported the script path as part of execArgv even though Arguments::parse only applies the alias under AutoCommand and RunAsNodeCommand. Check the alias at the use site, gated on seen_run, instead of baking it into the static set.
--cpu-prof-interval 0 hung the process: the sampler never fires and the profile
is never written (30s timeout, 0 profiles). Pre-existing on the CLI, but
worker execArgv now reaches the same setter from JS, where a hang is a DoS.
Clamp to [1, c_int::MAX]. The upper bound also covers a value that fits u32 but
not c_int, which panicked the cast.
bun --cpu-prof --cpu-prof-interval 0 -e '...' 30s timeout -> 0s, 1 profile
new Worker(f, { execArgv: [..., '0'] }) 30s timeout -> exit 0
--cpu-prof-interval 100 with a worker 2 profiles, unchanged
Reported by coderabbitai on oven-sh#34424. Its other two points on the same parse —
non-UTF-8 and malformed values — need no change: they fall back to the default,
which is what Bun's own CLI (unwrap_or(1000)) and node both do.
…em-ca
Node treats --use-system-ca as an Environment option: a Worker's execArgv can
enable or disable it independently of the process. Bun kept the decision in two
process-global atomics, so a worker could not differ and the two upstream tests
covering exactly that had nowhere to land.
Node's actual model, measured against v26.3.0 (env -u NODE_EXTRA_CA_CERTS):
baseline 120 (= bundled)
NODE_USE_SYSTEM_CA=1 122 env adds the system store
--no-use-system-ca + env=1 120 the flag wins, env ignored
--use-bundled-ca + env=1 122 env still wins
So the intent is three-valued, not a bool: unset lets NODE_USE_SYSTEM_CA decide,
and only the explicit negation overrides it. --use-bundled-ca deliberately does
not, which is why it maps to None rather than Some(false).
VirtualMachine carries Option<bool>; getUseSystemCA() returns true/false/
undefined and tls.ts only consults the env var when it is undefined. A worker
takes its own execArgv when one was given — an explicit list, even empty,
replaces the parent's rather than inheriting, as node resets to fresh defaults
whenever execArgv is present — and otherwise inherits the parent's intent.
--no-use-system-ca is new (node gets the negation from its generic --no- prefix)
and it restricts trust for real, not just for reporting: root_certs.cpp checks it
before both Bun__Node__UseSystemCA and getenv, and Arguments no longer lets
NODE_USE_SYSTEM_CA select the System store underneath it. A flag whose whole
purpose is to restrict trust must not leave connections trusting the system
store while getCACertificates() claims otherwise.
bun test parses the CA flags too, so its VM is seeded like run/repl; without that
`bun test --use-system-ca` would have started under-reporting.
test-tls-get-ca-certificates-worker-{,no-}use-system-ca: 0/2 -> 5/5. All 12
vendored get-ca tests pass; tls suite 178 pass/0 fail; cpu-prof unaffected.
Known gaps, deliberately not in scope: bun reports one fewer system certificate
than node (121 vs 122) from the same store, unrelated to this; TLS connections
still resolve the store once per process, so a worker that only *enables*
--use-system-ca reports certs its own connections won't use (node builds the
store per-Environment); and `--use-system-ca --no-use-system-ca` together
resolves by priority here, where node is last-one-wins.
inherited_config_for_worker copied name verbatim, so with --cpu-prof --cpu-prof-name foo.cpuprofile every worker resolved to the same output path as the main thread and whichever on_exit ran last overwrote the other. Clearing name lets each worker fall through to the thread-id-suffixed default, matching node (--cpu-prof-name is per-Environment there).
A worker that throws something structured-clone can't serialize falls back to
sending only the message text, and the parent rebuilds a bare Error from it —
losing `code`. Bun's own ResolveMessage is exactly that case: it carries the
right code and is not even an Error instance.
new Worker(`require("node:internal/freelist")`, { eval: true })
node: code ERR_UNKNOWN_BUILTIN_MODULE
bun : code undefined (the worker-side value has the right code)
Carry the code alongside the message instead of replacing the value. Replacing
it was the first attempt and it broke `support require in eval for a file that
doesnt exist`: rebuilding from ResolveMessage's own .message drops Bun's
"error: ..." prefix, which that test asserts on. The message text is now
untouched — only `code` is added.
The read is shared with the value path as Worker::errorCodeOf, under a top
exception scope since a `code` getter can run JS.
test-worker-internal-modules.mjs: 3 fail -> 3 pass, and 0/2 without the change.
Vendored test-worker* 106 pass/2 fail (both pre-existing: arraybuffer-zerofill
needs `bun test`, on-process-exit is debug-only). Thrown errors keep their code
as before, and test-worker-error-stack-getter-throws still passes.
eventLoopUtilization() returned hardcoded zeros and worker.performance's was a
notImplemented stub, so test-worker-eventlooputil did not fail — it HUNG
FOREVER, spinning on `if (elu().idle <= 0) return setTimeout(r, 5)`.
The loop already knew when it was about to park (`will_idle_inside_event_loop`),
so the accounting is two clock reads on ticks that were going to sleep anyway; a
busy tick pays nothing. libuv's own idle metrics cover the Windows path, enabled
with uv_loop_configure(UV_METRICS_IDLE_TIME) as node does unconditionally.
Two things this got wrong first, both worth recording:
`us_internal_loop_data_t` is us_loop_t's FIRST member and is MIRRORED in Rust
(src/uws_sys/InternalLoopData.rs). Adding a field without the mirror shifted
num_polls, so us_loop_run_bun_tick took its `num_polls == 0` early return and the
loop stopped parking — no compile error, and it read as an architectural wall
until a printf of num_polls showed 1 vs 0.
A counter only folded in when a park ENDS reads stale mid-park, which
over-reports active (49.8 vs the required 50). libuv has the same problem and
solves it the same way: publish the park's entry time and let the reader add the
in-progress interval (uv_metrics_idle_time, uv-common.c:1042).
The read order — idle, then now — and the unguarded divisions both match node:
eventLoopUtilization(u, u) yields NaN there, verified on v26.3.0, so collapsing
it to 0 would diverge. The shared math lives in internal/perf/event_loop_utilization
exactly as node shares it between perf_hooks and worker_threads.
Also fixes MessagePort listeners being called with `this === undefined` where
node passes the port; injectFakeEmitter's wrapper had the receiver and dropped
it. Worker is unaffected (real EventEmitter, already correct).
test-worker-eventlooputil: hung -> 10/10, byte-identical to node, clean under
BUN_JSC_validateExceptionChecks. perf_hooks 8 pass/0 fail; worker_threads
unchanged at its 2 known failures. Matches node on main-script ({0,0,0} before
the loop turns), 2-arg identical (NaN), and no-arg (0 < utilization < 1).
Known divergence: node reports {0,0,0} during synchronous main-script evaluation
because its loopStart milestone is still unset; Bun's loop_start is fixed at VM
init. Modelling that needs node's real milestone, not a proxy — iteration_nr
looks like one but is wrong, since a worker's script runs after its loop starts
and the main script runs before.
Clearing `name` for inherited worker configs gave every thread its own thread-id-suffixed file, which reads like a fix — a custom name no longer gets truncated by whichever thread writes last. But it is not what node does, and this is a node-compat surface. Measured against v26.3.0, worker awaited so the profiles are actually flushed: --cpu-prof --cpu-prof-name main.cpuprofile node 1 file bun 2 -> 1 --cpu-prof (default name) node 2 files bun 2 2 node only thread-stamps the default name; an explicit --cpu-prof-name is inherited verbatim and the threads collide. Keeping the collision. The test is kept — it covers a gap nothing else did — with its expectation corrected to node's behaviour. cpu-prof suite 10 pass/0 fail; the vendored test-cpu-prof-dir-worker and test-cpu-prof-worker-argv stay 3/3. Reverses one line of e20129c; the rest of that commit stands.
- worker_threads: drop the now-unused warnNotImplementedOnce import; the stub that used it is gone. - uws_sys: `core::ptr::from_ref(self).cast_mut()` instead of an `as` chain, which tripped both ptr_as_ptr and ref_as_ptr. - web_worker: restore two SAFETY comments an earlier comment trim removed. The last two were not in CI's report: bun_uws_sys failed first and masked every downstream crate's lints, so fixing only what CI showed would have turned red again on the next run. Verified with `cargo clippy -p bun_jsc -p bun_runtime -p bun_uws_sys` (rc=0) and `bun lint` (0 errors), and test-worker-eventlooputil stays 3/3 since the cast is on its read path.
- MessagePort .once() now forwards the port as this to the listener (.on() already did); covered by a new worker_threads test - seed use_system_ca in init_with_module_graph, init_bake and boot_standalone so compiled/bake binaries keep the per-VM value - workers inherit cpu-prof from the immediate parent VM instead of a process-global OnceLock, so nested workers follow node's per-Environment model; the OnceLock and its publish call are dead and removed - parse_worker_exec_argv consumes the value of --cpu-prof-dir/-name so a later flag is not dropped by the positional short-circuit - drop unused warnNotImplementedOnce import (oxlint) - fix 'uncgated' typo in internal.h
- epoll_kqueue: zero idle_entry_ns before folding the delta into idle_ns, so a reader landing between the two under-counts (bounded, monotonic) instead of double-counting and driving active negative - WebWorker__getELU: read a loop pointer cached under vm_lock instead of following event_loop_handle, which spawnSync swaps on the worker thread without taking the lock (data race, and the temp loop's idle_ns is ~0)
A release store does not order a later relaxed RMW before it on ARM, so the zero-then-add could still reorder and let a cross-thread reader double-count. seq_cst on all five accesses (pre-park store, post-wake zero+add, reader loads) keeps the pair atomic to readers; the path only runs once per park so the extra barriers are in the noise.
A data: URL module with a JavaScript MIME type now goes through the concurrent transpiler store. In debug builds the store dumps each module under /tmp/bun-debug-src and treats the specifier as a file path. For a data: URL that writes to a meaningless path, and a specifier longer than a path buffer makes resolve_path::z panic with "path too long". The long data: URL tests in test/js/node/string-module.test.js hit that panic under `bun bd`. Also add two cases to import-data-url.test.ts: require() of a CommonJS payload, and the base64 form of the TypeScript syntax check from oven-sh#29159.
### What does this PR do? Integrates the 19 captured upstream compatibility PRs into the OpenClaw Bun fork, retaining their original commits as merge parents. The base is upstream `86771d09fd486a7256790d6f36602b683f7a19de`. This integration is separate from upstream PR review and does not publish a Bun release. The two stacked PRs also bring their prerequisites: [worker support oven-sh#34424](oven-sh#34424) and [file-URL query handling oven-sh#35601](oven-sh#35601). | Upstream PR | Captured head | | --- | --- | | [42349: fix(sqlite): allow workers to reuse custom library](oven-sh#42349) | `65924882863e` | | [42374: fix(node:fs): preserve POSIX locks in realpath](oven-sh#42374) | `4df5e0600308` | | [42446: fix(node:fs): preserve child rm permission errors](oven-sh#42446) | `17d1237bcbac` | | [42469: fix(runtime): preserve encoded file URL path delimiters](oven-sh#42469) | `cc5b9fb06de9` | | [42576: fix(node:https): support live secure context updates](oven-sh#42576) | `b8666fde28e6` | | [42593: fix(worker_threads): preserve async context for worker events](oven-sh#42593) | `f72285db962b` | | [42594: fix(node:https): wrap injected raw connections with TLS](oven-sh#42594) | `82a9d26cf2cc` | | [42599: fix(node:os): observe runtime HOME changes](oven-sh#42599) | `772e4acb9263` | | [42600: fix(worker_threads): preserve cloned error metadata](oven-sh#42600) | `7254eaec568c` | | [42601: fix(node:path): honor replaced process.cwd](oven-sh#42601) | `e040ec4cf1c0` | | [42607: fix(process): allow clearing exitCode](oven-sh#42607) | `bacfa9ee3cb3` | | [42610: fix(node:http): uncork reused upgrade sockets](oven-sh#42610) | `33f89359c50a` | | [42614: fix(node): resolve listen hosts before binding](oven-sh#42614) | `5b9ab5644122` | | [42616: fix(node:module): synchronize builtin ESM exports](oven-sh#42616) | `aa78523549c1` | | [42620: fix(worker_threads): apply execArgv preloads](oven-sh#42620) | `60fbb60c9a16` | | [42621: fix(node:async_hooks): report timer lifecycles](oven-sh#42621) | `6e044db91d6b` | | [42622: fix(node:http): align shutdown transport lifecycle](oven-sh#42622) | `98d5f813e8fe` | | [42635: fix(node:fs): preserve Win32 semantics in recursive mkdir checks](oven-sh#42635) | `891eb8df52f3` | | [42636: fix(runtime): derive data URL loaders from MIME](oven-sh#42636) | `4570e105f422` | Integration repairs preserve newer upstream loop-init error handling, use current Rust loader/string-view interfaces, coordinate WORKER init hook mutations with timer/nextTick dispatch, apply TLS context updates made during pending listen, retain draining native listeners for force-close, and preserve literal filename delimiters across ESM/CommonJS resolution and lookup paths. Superseded C++ CommonJS key reconstruction is removed in favor of the shared resolver owner. ### How did you verify your code works? - Fresh optimized macOS arm64 build: 1,687 passed, 37 existing skips, one existing todo, zero failures across the 22 selected suites, including standalone compilation. - Debug/ASAN build and focused integration regressions passed. Its earlier full run passed 1,681 tests but hit an inherited standalone-compilation fixture limitation: the large debug template exceeded that test budget, and relocated output needs its ASAN sidecar. The optimized run covers that production flow; no sanitizer setting, test timeout, or skip was weakened. - Ten directly affected vendored Node conformance files passed with retries disabled. - All twelve Rust targets passed: zero failed and zero skipped. These are compilation checks, not native execution claims for every target. - Oxlint, root TypeScript, Rust formatting, and `git diff --check` passed. - Independent review is clean through P2. Confirmed integration regressions were repaired; an empty-query/fragment review claim was rejected using actual Node 26.8.2 behavior and protected by a regression. - Repeated recursive-directory testing keeps its 200 optimized-build iterations and descriptor-leak checks, with a fixed nested fixture instead of scanning the growing source tree.
|
Hi! I'm the It looks like you correctly set up a CI job that uses the autofix.ci GitHub Action, but the autofix.ci GitHub App has not been installed for this repository. This means that autofix.ci unfortunately does not have the permissions to fix this pull request. If you are the repository owner, please install the app and then restart the CI workflow! 😃 |
Document raw slice lifetimes at their unsafe operations, remove MIME and response cork helpers orphaned by the compatibility integration, and sort HTTP exports. Validation: workspace Clippy and formatting passed; 392 debug/ASAN tests passed with one existing skip; independent review clean through P2.
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.
What does this PR do?
Integrates the 19 captured upstream compatibility PRs into the OpenClaw Bun fork, retaining their original commits as merge parents. The base is upstream
86771d09fd486a7256790d6f36602b683f7a19de. This integration is separate from upstream PR review and does not publish a Bun release.The two stacked PRs also bring their prerequisites: worker support #34424 and file-URL query handling #35601.
65924882863e4df5e060030817d1237bcbaccc5b9fb06de9b8666fde28e6f72285db962b82a9d26cf2cc772e4acb92637254eaec568ce040ec4cf1c0bacfa9ee3cb333f89359c50a5b9ab5644122aa78523549c160fbb60c9a166e044db91d6b98d5f813e8fe891eb8df52f34570e105f422Integration repairs preserve newer upstream loop-init error handling, use current Rust loader/string-view interfaces, coordinate WORKER init hook mutations with timer/nextTick dispatch, apply TLS context updates made during pending listen, retain draining native listeners for force-close, and preserve literal filename delimiters across ESM/CommonJS resolution and lookup paths. Superseded C++ CommonJS key reconstruction is removed in favor of the shared resolver owner.
How did you verify your code works?
git diff --checkpassed.Final CI corrections
The follow-up removes MIME decoding and response cork adapters whose last callers were replaced by the integrated PRs, documents raw-slice ownership immediately above the unsafe operations, and sorts HTTP exports. Workspace Clippy and formatting pass locally. The final debug/ASAN check passes 392 tests across the data-URL, worker-thread, and HTTP suites, with one existing skip and no failures. Independent review of this follow-up is clean through P2.
The first CI run also exposed two fork-service limitations: the issue-linking bot has no Anthropic credentials, and autofix.ci cannot push formatter changes without its GitHub App. The formatting change was applied locally.
Mordant's advisory
unchecked_constructionwarning points to the existing server reload assignment ofuser_routes_to_build. That assignment moves fields fromnew_config, whichon_reloadobtains throughServerConfig::from_jsbefore callingon_reload_from_zig; the integrated TLS setter also parses its replacement throughSSLConfig::from_js. This is not an unchecked user-input path. Its baseline and enforcement were left intact; the three unused-helper findings were repaired.The final optimized macOS arm64 build passes all four affected suites: 433 passed, one existing skip, zero failures in 9.11 seconds, including standalone compilation. This supplements the initial 22-suite run (1,687 passed), ten vendored Node conformance files, and twelve Rust compilation targets. The final cleanup also passes 392 debug/ASAN tests and workspace Clippy.
The reload validation path discussed above is visible at ServerConfig::from_js before reload, while the flagged assignment transfers that parsed configuration.
Final hosted validation on
597b78c2c4b6a0e15b4b1724ab0e5ebff80f5678: formatting, JavaScript/source lint, TypeScript types, package tests, Clippy, Miri, and lol-html tests passed. The Rust workflow succeeded; its advisory Mordant job retains only the documented reload-validation false positive.