You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
3 contributors have changed 39 files with 1,176 additions and 27 deletions in 4 commits (next...v8.0.2)
19,806 total features
1,254 total contributors
5,681 total stargazers
### v8.0.1### Statistics
2 contributors have changed 1 file with 89 additions and 0 deletions in 1 commit (next...v8.0.1)
19,752 total features
1,251 total contributors
5,679 total stargazers
### v8.0.0### Breaking changes
This release introduces two breaking changes in the TypeScript definitions. The shape of the published data.json has not changed.
Summary: The published TypeScript definitions (types.d.ts) now fully match the actual shape of the published data.json. Two existing types are now stricter.
1. source_file is now required on CompatStatement (#29041)
Previously, the CompatStatement.source_file property was optional in the TypeScript definitions, even though it is always present in published data.json releases (it is generated at build time).
Now, source_file is typed as required, matching the actual shape of the data.
Impact: You may need to remove checks for a missing source_file (e.g. if (compat.source_file)).
2. BrowserStatement.upstream is narrowed to UpstreamBrowserName (#29041)
Previously, the BrowserStatement.upstream property was typed as BrowserName, allowing any of the 17 known browser keys.
Now, upstream is typed as the new UpstreamBrowserName, a subset of BrowserName containing only the browsers that other browsers actually derive from: "chrome" | "chrome_android" | "firefox" | "safari" | "safari_ios".
Impact: You may need to widen the type when passing upstream into functions expecting a full BrowserName, or switch on the narrower set.
Statistics
2 contributors have changed 91 files with 1,681 additions and 784 deletions in 1 commit (v7.3.17...v8.0.0)
97f284 build(deps-dev): bump eslint from 9.39.1 to 10.5.0 (#29933)
build(deps-dev): remove unused eslint-plugin-node
The plugin is listed in devDependencies but is never referenced in eslint.config.js. It is also long-deprecated (superseded by eslint-plugin-n) and caps its eslint peer at older majors, which
would obstruct an ESLint 10 upgrade.
build(deps-dev): migrate to eslint-plugin-import-x
8f1a95 FileSystemRouter: skip empty-key query pairs instead of terminating the parse (#34027)
Repro
constr=newBun.FileSystemRouter({style: "nextjs",dir: "./pages"});r.match("/top?=v&x=1&y=2").query// {} (x and y lost)r.match("/top?x=1&=v&y=2").query// {"x":"1"} (y lost)// URLSearchParams keeps everything:Object.fromEntries(newURLSearchParams("=v&x=1&y=2"))// {"":"v","x":"1","y":"2"}
A single empty-key pair (=value) anywhere in the query string made match().query silently drop every parameter after it.
Cause
Scanner::next in src/url/lib.rs has an explicit "skip" branch for
empty-key pairs, but it was implemented as return None. Every caller
(QueryStringMap::init, init_with_scanner) loops while let Some(...) = scanner.next(), so None ends the scan instead of skipping the pair.
Fix
Advance self.i past the pair and continue 'outer so the scanner
resumes at the next &, matching the behavior the comment already
describes and what the adjacent bare-& arm already does.
Verification
USE_SYSTEM_BUN=1 bun test test/js/bun/util/filesystem_router.test.ts -t empty-key # fails
bun bd test test/js/bun/util/filesystem_router.test.ts # 28 pass
no test proof · iteration 1 · Platform-specific test(s) that do not
run on this machine. Deferring to CI, which covers all platforms:
test/js/bun/util/filesystem_router.test.ts
005ddf Bun.randomUUIDv7: preserve monotonicity when the 12-bit counter rolls over (#34022)
What does this PR do?
Fixes Bun.randomUUIDv7() so it stays monotonic when more than 4096
UUIDs are generated within a single millisecond.
The docs say Bun.randomUUIDv7() "is monotonic and suitable for sorting
and databases", but call 4097 at the same millisecond sorts before call
4096. A bare for (;;) Bun.randomUUIDv7() loop clears 4096 calls per
real millisecond, so this is reachable without pinning the timestamp
(about 30 ordering breaks per 200k calls).
UUID7::get_count in src/jsc/uuid.rs returned UUID_V7_COUNTER.fetch_add(1, Relaxed) % 4096, silently wrapping the
12-bit rand_a counter under a constant timestamp. It also reset the
counter to 0 on a new millisecond, whereas the docs describe a
pseudo-random reset; the fixed 0 is why the wrap lands deterministically
at call 4097 and leaks the per-millisecond generation count.
The new UUID7::next tracks the last emitted timestamp. When the
requested timestamp has not moved past it, the last emitted timestamp is
reused and the counter is incremented; when the 12-bit counter has been
exhausted, the emitted timestamp is bumped by 1 ms and the counter is
reseeded rather than wrapped. This is the behaviour RFC 9562 section 6.2
(Fixed Bit-Length Dedicated Counter) specifies for rollover and matches
npm uuid's v7. When the timestamp does move forward, the counter is
reseeded from the call's entropy with the high bit of the 12-bit field
kept clear, so at least 2048 increments remain before the next rollover.
Because the emitted timestamp never moves backward, passing an explicit
timestamp older than one already emitted in the same process now
continues from the newer value (the custom timestamp test was updated
to use a far-future constant so it still exercises the encoding path).
The docs paragraph was updated to describe the rollover handling.
How did you verify your code works?
USE_SYSTEM_BUN=1 bun test test/js/bun/util/randomUUIDv7.test.ts
6 pass, 3 fail
older explicit timestamps do not move UUIDs backward: Expected true, Received false
monotonic across 12-bit counter rollover: Expected -1, Received 4096
counter is seeded pseudo-randomly on a new millisecond: Expected > 1, Received 1
bun bd test test/js/bun/util/randomUUIDv7.test.ts
9 pass, 0 fail
ASAN without fix: 3 FAILED
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test"--reporter=junit""--reporter-outfile=/tmp/mechgate.xml""test/js/bun/util/randomUUIDv7.test.ts"info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: checking for self-update (current version: 1.29.0)bun test v1.4.0 (828466833)test/js/bun/util/randomUUIDv7.test.ts:(pass) randomUUIDv7 > basic [6.35ms](pass) randomUUIDv7 > timestamp [3.16ms](pass) randomUUIDv7 > base64 format [2.18ms]019f556e60227000a823ca80ae281347(pass) randomUUIDv7 > buffer output encoding [2.79ms](pass) randomUUIDv7 > monotonic [28.33ms]50 | for (let i = 0; i < 10000; i++) {51 | const u = Bun.randomUUIDv7("hex", ts);52 | if (i > 0 && u <= prev && firstBreak === -1) firstBreak = i;53 | prev = u;54 | }55 | expect(firstBreak).toBe(-1); ^error: expect(received).toBe(expected)Expected: -1Received: 4096 at <anonymous> (/workspace/bun/test/js/bun/util/randomUUIDv7.test.ts:55:24)(fail) randomUUID... (truncated)release without fix: all passedbun test v1.4.0-canary.1 (dd1b708f9)test/js/bun/util/randomUUIDv7.test.ts:(pass) randomUUIDv7 > basic [0.15ms](pass) randomUUIDv7 > timestamp [0.09ms](pass) randomUUIDv7 > base64 format [0.03ms]019f556e686977d28de1825977269f93(pass) randomUUIDv7 > buffer output encoding [0.08ms](pass) randomUUIDv7 > monotonic [0.32ms](pass) randomUUIDv7 > monotonic across 12-bit counter rollover [1.88ms](pass) randomUUIDv7 > custom timestamp [11.61ms](pass) randomUUIDv7 > older explicit timestamps do not move UUIDs backward [12.11ms](pass) randomUUIDv7 > counter is seeded pseudo-randomly on a new millisecond [11.76ms] 9 pass 0 fail 19 expect() callsRan 9 tests across 1 file. [259.00ms]__F:0:S:0
passes on PR (with fix)
ASAN with fix: all passed
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test"--reporter=junit""--reporter-outfile=/tmp/mechgate.xml""test/js/bun/util/randomUUIDv7.test.ts"info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: checking for self-update (current version: 1.29.0)bun test v1.4.0 (828466833)test/js/bun/util/randomUUIDv7.test.ts:(pass) randomUUIDv7 > basic [4.85ms](pass) randomUUIDv7 > timestamp [3.17ms](pass) randomUUIDv7 > base64 format [2.21ms]019f556ef8907057805601495326935b(pass) randomUUIDv7 > buffer output encoding [2.83ms](pass) randomUUIDv7 > monotonic [28.69ms](pass) randomUUIDv7 > monotonic across 12-bit counter rollover [388.81ms](pass) randomUUIDv7 > custom timestamp [513.79ms](pass) randomUUIDv7 > older explicit timestamps do not move UUIDs backward [511.85ms](pass) randomUUIDv7 > counter is seeded pseudo-randomly on a new millisecond [545.57ms] 9 pass 0 fail 19 expect() callsRan 9 tests across 1 file. [4.03s]__F:0:S:0release with fix: all passed
$ bun scripts/build.ts --profile=releaseinfo: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: checking for self-update (current version: 1.29.0)[configured] bun-profile → bun (stripped) in 677ms (unchanged)ninja: Entering directory `/workspace/bun/build/release'[1/8] cxx obj/unified/UnifiedSource-src_jsc_bindings-2.cpp.o[2/8] gen generated_host_exports.rsgenerated_host_exports.rs: 91 exports (host=3, lazy=10, generic=78, rust=0); 243 extern-C blocks audited[3/8] gen cpp.rs (cppbind)[3/8] cargo bun_bin → libbun_rust.a (--target x86_64-unknown-linux-gnu)info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: component rust-std is up to dateinfo: checking for self-update (current version: 1.29.0) nightly-2026-05-06-x86_64-unknown-linux-gnu unchanged - rustc 1.97.0-nightly (e95e73209 2026-05-05)�[1m�[92m Compiling�[0m bun_core v0.0.0 (/workspace/bun/s... (truncated)
b4045c sql: reject a MySQL prepare-OK carrying statement_id 0 (#33238)
What
A COM_STMT_PREPARE response carrying statement_id = 0 is accepted
without validation. statement_id == 0 is the client's own "not yet
prepared" sentinel: handle_prepared_statement keys its packet dispatch
on statement_id > 0, and bind_and_execute asserts it. Accepting a 0
therefore marks the cached statement Prepared while leaving it in an
impossible state. Only the peer controls this value, so a compromised or
desynced MySQL-compatible server or proxy can trigger it with a single
frame.
On assert-enabled builds the very next execute aborts the process:
panic: statement is not prepared
bind_and_execute src/sql_jsc/mysql/MySQLQuery.rs:173
run_prepared_query src/sql_jsc/mysql/MySQLQuery.rs:398
check_if_prepared_statement_is_done src/sql_jsc/mysql/MySQLConnection.rs:1126
handle_prepared_statement src/sql_jsc/mysql/MySQLConnection.rs:1228
On release builds the assertion compiles out and the client sends COM_STMT_EXECUTE for statement id 0 on the wire, with the poisoned
entry left in the statement cache.
Repro
test/js/sql/sql-mysql-prepare-ok-zero-statement-id.test.ts: a scripted node:net MySQL server that completes the handshake and then answers COM_STMT_PREPARE with [00][statement_id = 0 (u32)][columns = 0][params = 0]. No fault injection; the client runs in a spawned
process so the abort is observable, and the mock records the statement_id of any COM_STMT_EXECUTE it receives.
Fix
Validate the prepare-OK at the response boundary. StmtPrepareOKPacket::decode_internal now rejects statement_id == 0
with InvalidPrepareOKPacket, the same error it already returns for a
bad status byte, so the connection fails with ERR_MYSQL_INVALID_PREPARE_OK_PACKET before any statement state is
mutated and nothing is written to the socket. The execute-time assertion
is kept as defense in depth.
Also deletes PrepareOK in src/sql/mysql/protocol/PreparedStatement.rs: an unused duplicate
decoder of the same packet (zero callers) that would otherwise remain as
a second, unvalidated parser of it.
The server-reported param count is already cross-checked against the
query's placeholder count at bind time and surfaces as a recoverable WrongNumberOfParametersProvided, so no change is needed there.
Verification
build
result
release 1.4.0, without fix
test fails: mock records
COM_STMT_EXECUTE for statement_id 0
bun bd debug, without fix
test fails: child aborts, `panic:
statement is not prepared`
bun bd debug, with fix
test passes: query rejects with
ERR_MYSQL_INVALID_PREPARE_OK_PACKET, no execute sent
fa1fe9 Bun.serve(http3): send CONNECTION_CLOSE on abrupt stop of an idle connection (#34038)
Fixes test/js/bun/http/serve-http3.test.ts going intermittently red,
most often as POST body without Content-Length still reaches the handler failing with HTTP3StreamReset after ~8-10s.
Reproduction
// 1. H3 server A at port P, one fetch to warm the client session// 2. server.stop(true) <- conn is idle here// 3. H3 server B at the SAME port P// 4. fetch with a ReadableStream body// -> HTTP3StreamReset after ~9s
On a release build step 2 usually runs while the server still has ACKs
scheduled, so the bug is masked; under debug/ASAN or a loaded CI runner
the connection is idle and the POST stalls every time.
Root cause
server.stop(true) reaches us_quic_listen_socket_close(), which
called lsquic_conn_close() on each live conn and then closed the UDP
fd. For a server connection, ietf_full_conn_ci_close sets IFC_CLOSING and only schedules SF_SEND_CONN_CLOSE when conn_ok_to_close(); the tick that actually packs the frame (the end_write block in lsquic_full_conn_ietf.c) additionally requires
one of:
IFC_GOAWAY_CLOSE, or
a received CONNECTION_CLOSE, or
lsquic_send_ctl_n_scheduled() > 0
An idle server conn satisfies none of those, so the UDP fd was closed
with nothing on the wire. The client's pooled session stays in ClientContext.sessions until the negotiated idle timeout. If a later
listener binds the same ephemeral port before that fires, fetch({protocol:'http3'}) matches the dead session and enqueues on it.
Non-stream bodies recover via retry_or_fail; a ReadableStream body
has already been drained into lsquic's send buffer and retry_or_fail
refuses on is_streaming_body, so it surfaces HTTP3StreamReset once
the idle alarm fires.
The pattern has been present since HTTP/3 support landed (#29768 for the
server side, #29795 for the pooled client); it turns into a test failure
whenever two ephemeral ports collide inside one idle-timeout window.
Fix
us_quic_listen_socket_close() now calls lsquic_conn_abort() instead
of lsquic_conn_close(). IFC_ABORTED is in IFC_IMMEDIATE_CLOSE_FLAGS, which takes the immediate_close path and
unconditionally packs a CONNECTION_CLOSE (transport error NO_ERROR,
"user aborted connection").
The test file's withCustomServer fixtures now server.stop(true) on
stdin close and withCustomServer gives them a moment to do so before kill(), so every server process in the file exits through this path.
Verification
New lifecycle test lets the server conn go idle, stops abruptly, rebinds
the same port in-process, then POSTs a ReadableStream body:
# without packages/ change
(fail) server.stop(true) sends CONNECTION_CLOSE on an idle H3 connection [4766ms]
# with packages/ change
(pass) server.stop(true) sends CONNECTION_CLOSE on an idle H3 connection [1713ms]
Across the full file with BUN_DEBUG_h3_client=1 on a debug build,
client-session close breakdown:
connects
idle-timeout (status=4)
CONNECTION_CLOSE (status=8)
before
43
29
10
after
46
2
42
The two remaining timeout closes are the intentional AbortSignal.timeout probes against a stopped port; those sessions are
still handshaking and recover on retransmission.
ASAN without fix: 1 FAILED
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test"--reporter=junit""--reporter-outfile=/tmp/mechgate.xml""test/js/bun/http/serve-http3.test.ts"info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: checking for self-update (current version: 1.29.0)bun test v1.4.0 (304079670)test/js/bun/http/serve-http3.test.ts:(pass) Bun.serve HTTP/3 > basic GET [1669.34ms](pass) Bun.serve HTTP/3 > POST echoes body, status, request headers [1769.09ms](pass) Bun.serve HTTP/3 > 204 with no body [1648.64ms](pass) Bun.serve HTTP/3 > query string is preserved [1627.15ms](pass) Bun.serve HTTP/3 > large response body crosses multiple QUIC packets [1726.89ms](pass) Bun.serve HTTP/3 > concurrent requests across separate connections [1872.41ms](pass) Bun.serve HTTP/3 > client abort mid-response does not crash the server [1631.50ms](pass) Bun.serve HTTP/3 > http1: false rejects HTTP/1.1 but accepts HTTP/3 [1631.31ms](pass) Bun.serve HTTP/3 > http1: false — url/address/stop see the QUIC listener [2540.7... (truncated)release without fix: all passedbun test v1.4.0-canary.1 (887d0bef6)test/js/bun/http/serve-http3.test.ts:(pass) Bun.serve HTTP/3 > basic GET [150.04ms](pass) Bun.serve HTTP/3 > POST echoes body, status, request headers [135.34ms](pass) Bun.serve HTTP/3 > 204 with no body [135.24ms](pass) Bun.serve HTTP/3 > query string is preserved [135.18ms](pass) Bun.serve HTTP/3 > large response body crosses multiple QUIC packets [136.75ms](pass) Bun.serve HTTP/3 > concurrent requests across separate connections [134.72ms](pass) Bun.serve HTTP/3 > client abort mid-response does not crash the server [134.80ms](pass) Bun.serve HTTP/3 > http1: false rejects HTTP/1.1 but accepts HTTP/3 [137.19ms](pass) Bun.serve HTTP/3 > http1: false — url/address/stop see the QUIC listener [1035.49ms](pass) Bun.serve HTTP/3 > maxRequestBodySize is enforced for H3 bodies without Content-Length [132.29ms](pass) Bun.serve HTTP/3 > unknown route returns 404 [135.57ms](pass) Bun.serve HTTP/3 > routes: handler with :params [134.53ms](pass) Bun.serve HTTP/3 > routes: per-method handler [135.74ms](pass) Bun.serve HTTP/3 > routes: method-specific '/*' falls through to fetch() on other methods [133.48ms](pass) Bun.serve H... (truncated)
passes on PR (with fix)
ASAN with fix: all passed
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test"--reporter=junit""--reporter-outfile=/tmp/mechgate.xml""test/js/bun/http/serve-http3.test.ts"info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: checking for self-update (current version: 1.29.0)bun test v1.4.0 (304079670)test/js/bun/http/serve-http3.test.ts:(pass) Bun.serve HTTP/3 > basic GET [1655.54ms](pass) Bun.serve HTTP/3 > POST echoes body, status, request headers [1688.02ms](pass) Bun.serve HTTP/3 > 204 with no body [1758.76ms](pass) Bun.serve HTTP/3 > query string is preserved [1610.79ms](pass) Bun.serve HTTP/3 > large response body crosses multiple QUIC packets [1732.70ms](pass) Bun.serve HTTP/3 > concurrent requests across separate connections [1708.12ms](pass) Bun.serve HTTP/3 > client abort mid-response does not crash the server [1686.35ms](pass) Bun.serve HTTP/3 > http1: false rejects HTTP/1.1 but accepts HTTP/3 [1675.08ms](pass) Bun.serve HTTP/3 > http1: false — url/address/stop see the QUIC listener [2582.9... (truncated)release with fix: all passed
$ bun scripts/build.ts --profile=releaseinfo: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: checking for self-update (current version: 1.29.0)[configured] bun-profile → bun (stripped) in 688ms (unchanged)ninja: Entering directory `/workspace/bun/build/release'[1/21] gen JS modules (bundle-modules)Preprocess modules (6473ms)Bundle modules (27ms)Postprocesss modules (21ms)Bundle Functions (706ms)Generate Code (75ms)[7.32s] Bundled "src/js" for production 1912 kb 162 internal modules 12 native modules 90 internal functions across 19 files[1/7] cargo bun_bin → libbun_rust.a (--target x86_64-unknown-linux-gnu)info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: component rust-std is up to date nightly-2026-05-06-x86_64-unknown-linux-gnu unchanged - rustc 1.97.0-nightly (e95e73209 2026-05-05)info: checking for self-update (current version:... (truncated)
134838 bake: fix use-after-free in ~DevServerSourceProvider during VM teardown (#34035)
Fixes test/bake/dev/request-cookies.test.ts going red on the debian 13 x64-asan lane (seen in build
72183
and build 71964):
dev| ==1715==ERROR: AddressSanitizer: SEGV on unknown address 0x000000007490
error: DevServer panicked
at gracefulExit (test/bake/bake-harness.ts:614:17)
✗ DEV:request-cookies-1: request.cookies.get() basic functionality
Cause
~DevServerSourceProvider held a raw Zig::GlobalObject* and called m_globalObject->bunVM() to reach Bun__removeDevServerSourceProvider.
Under BUN_DESTRUCT_VM_ON_EXIT=1 (set by the CI runner for the asan
lane), the harness's process.exit(0) runs Zig__GlobalObject__destructOnExit, which does gcUnprotect(globalObject) then collectNow(Sync, Full) then two vm.derefSuppressingSaferCPPChecking(). The global object cell is swept
during collectNow, but the provider's last Ref is only released
later from ~CodeCache inside ~JSC::VM, so the destructor read m_bunVM out of a freed cell.
With bmalloc the freed cell usually still holds the old value and the
read happens to work, which is why this was ~0.5% in CI and never
reproduced locally. When the memory is reused with a zero at that offset
the Rust side receives a null VirtualMachine* and the next access is (null)->source_mappings.mutex, which lands at exactly 0x7490.
Store the Rust VirtualMachine* directly (void* m_bunVM), captured in create(), so the destructor no longer indirects through a GC cell.
This mirrors Zig::SourceProvider, which already stores m_bunVM for
the same reason. The Rust VirtualMachine outlives every GC cell (step
10 of global_exit is self.destroy(), after destructOnExit has
finished).
Verification
New ASAN-only case in test/bake/dev/server-sourcemap.test.ts runs the
dev server with Malloc=1 + BUN_DESTRUCT_VM_ON_EXIT=1 so ASAN poisons
the swept global-object cell, making the UAF deterministic. Added an env option to the devTest harness so the test can set those for the
spawned dev server.
# fail-before (src/ stashed)
SUMMARY: AddressSanitizer: heap-use-after-free ZigGlobalObject.h:353:48 in Zig::GlobalObject::bunVM() const
(fail) DEV:server-sourcemap-5: DevServerSourceProvider destructor does not touch the swept global object on process exit
# pass-after
(pass) DEV:server-sourcemap-5: DevServerSourceProvider destructor does not touch the swept global object on process exit
test/bake/dev/server-sourcemap.test.ts (5 tests) and test/bake/dev/request-cookies.test.ts (2 tests) are green. request-cookies.test.ts now also passes under the full CI LeakSan
config (BUN_DESTRUCT_VM_ON_EXIT=1 + detect_leaks=1).
The bug is from a89e61fcaaa (#22138), which introduced DevServerSourceProvider with the raw global-object pointer.
ASAN without fix: 1 FAILED
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test"--reporter=junit""--reporter-outfile=/tmp/mechgate.xml" test/bake/dev/request-cookies.test.ts test/bake/dev/server-sourcemap.test.tsinfo: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: checking for self-update (current version: 1.29.0)bun test v1.4.0 (722d6f067)test/bake/dev/server-sourcemap.test.ts:Dev server testing directory: /tmp/bun-dev-test-tI2Y82bun add v1.4.0 (722d6f067)Resolving dependenciesResolved, downloaded and extracted [2]Saved lockfileinstalled react@​0.0.0-experimental-603e6108-20241029installed react-dom@​0.0.0-experimental-603e6108-20241029installed react-server-dom-bun@​0.0.0-experimental-603e6108-20241029installed react-refresh@​0.0.0-experimental-603e6108-202410296 packages installed [462.00ms]bun install v1.4.0 (722d6f067)Checked 6 installs across 7 packages (no changes) [167.00ms]�[0;30mdev|�[0m Started development server: http://localhost:37377�[0;30mdev|�[0m �[32mBundled page in 2125ms�[0m�[2m:�[0... (truncated)release without fix: all passedbun test v1.4.0-canary.1 (1498d7b77)test/bake/dev/server-sourcemap.test.ts:Dev server testing directory: /tmp/bun-dev-test-7LPeWvbun add v1.4.0-canary.1 (1498d7b77)Resolving dependenciesResolved, downloaded and extracted [0]Saved lockfileinstalled react@​0.0.0-experimental-603e6108-20241029installed react-dom@​0.0.0-experimental-603e6108-20241029installed react-server-dom-bun@​0.0.0-experimental-603e6108-20241029installed react-refresh@​0.0.0-experimental-603e6108-202410296 packages installed [9.00ms]bun install v1.4.0-canary.1 (1498d7b77)Checked 6 installs across 7 packages (no changes) [0.00ms]�[0;30mdev|�[0m Started development server: http://localhost:43275�[0;30mdev|�[0m �[32mBundled page in 47ms�[0m�[2m:�[0m pages/[...slug].tsx �[2m+ 2 more�[0m�[0;30mdev|�[0m �[0m�[1m1 |�[0m �[0m�[35mexport�[0m �[0m�[35mdefault�[0m �[0m�[35masync�[0m �[0m�[35mfunction�[0m MyPage(params) {�[0;30mdev|�[0m �[0m�[1m2 |�[0m myFunc()�[0m�[2m;�[0m�[0;30mdev|�[0m �[0m�[1m3 |�[0m �[0m�[35mreturn�[0m �[0m<�[0mh1>{JSON�[0m�[3m�[1m.stringify�[0m(params)}�[0m<�[0m/h1>�[0m�[2m;�[0m�[0;30mdev|�[0m �[0m�[1m4 |�[0m }�[0;30mdev|�[0m �[0m�[1m5 |�[0m �[0;30mdev|�[0m �[0m�... (truncated)
passes on PR (with fix)
ASAN with fix: all passed
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test"--reporter=junit""--reporter-outfile=/tmp/mechgate.xml" test/bake/dev/request-cookies.test.ts test/bake/dev/server-sourcemap.test.tsinfo: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: checking for self-update (current version: 1.29.0)bun test v1.4.0 (722d6f067)test/bake/dev/server-sourcemap.test.ts:Dev server testing directory: /tmp/bun-dev-test-dkR1sebun add v1.4.0 (722d6f067)Resolving dependenciesResolved, downloaded and extracted [0]Saved lockfileinstalled react@​0.0.0-experimental-603e6108-20241029installed react-dom@​0.0.0-experimental-603e6108-20241029installed react-server-dom-bun@​0.0.0-experimental-603e6108-20241029installed react-refresh@​0.0.0-experimental-603e6108-202410296 packages installed [119.00ms]bun install v1.4.0 (722d6f067)Checked 6 installs across 7 packages (no changes) [97.00ms]�[0;30mdev|�[0m Started development server: http://localhost:44249�[0;30mdev|�[0m �[32mBundled page in 2351ms�[0m�[2m:�[0m... (truncated)release with fix: all passed
$ bun scripts/build.ts --profile=releaseinfo: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: checking for self-update (current version: 1.29.0)[configured] bun-profile → bun (stripped) in 690ms (unchanged)ninja: Entering directory `/workspace/bun/build/release'[0/6] cargo bun_bin → libbun_rust.a (--target x86_64-unknown-linux-gnu)info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: component rust-std is up to date nightly-2026-05-06-x86_64-unknown-linux-gnu unchanged - rustc 1.97.0-nightly (e95e73209 2026-05-05)info: checking for self-update (current version: 1.29.0)�[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... (truncated)
node v26.3.0: flowing=false isPaused=true
bun 1.4.0: flowing=true isPaused=false
readableEnded / destroyed agree; only readableFlowing / isPaused() diverge, so code that branches on those after the adapter
finishes (pooling, reuse logic, diagnostics) observes the wrong state.
Bun dropped that guard in 6abf57e204 so that fd-slicer-style readables
(yauzl / extract-zip / puppeteer), which assign this.destroyed = true
right before push(null), can still be resumed by a piped destination's 'drain' to flush their buffered tail. With no guard at all, the
adapter's post-EOF resume() flips readableFlowing back to true.
Fix
Narrow the resume() guard to kDestroyed && kEndEmitted instead of
removing it entirely. In the fd-slicer case 'end' has not yet been
emitted when drain resumes the source, so the narrowed guard does not
fire and the buffered tail still flushes. Once 'end' has fired there
is nothing left to flush, so resume() can become a no-op and keep
state parity with Node.
pause() is left unchanged; #33467 addresses the mirrored post-pipe
divergence on the pause() side.
Verification
New test Readable.toWeb leaves the source paused / non-flowing after EOF fails on main (readableFlowing: true, isPaused: false) and passes
with this change; the same test body passes on Node v26.3.0.
The existing fd-slicer regression test (drain still resumes a source that flagged itself destroyed before EOF) still passes.
test/js/node/stream/ and the vendored test-stream-*.js parallel
suite: no new failures relative to main.
no test proof · iteration 1 · Platform-specific test(s) that do not
run on this machine. Deferring to CI, which covers all platforms:
test/js/node/stream/node-stream.test.js
af7faf test: stop deliberate crash tests from uploading to CI's crash-report server (#34024)
test/integration/next-pages/test/dev-server-ssr-100.test.ts was
reported RED in build 72106 on :darwin: 26 aarch64 with 5 crashes reported during this test. The test itself is
fine on main (last four completed main builds 72110/72020/71943/71860
have no mention of it). The RED was manufactured by CI's crash-report
attribution.
Cause
scripts/runner.node.mjs exports BUN_CRASH_REPORT_URL=http://localhost:<remapPort> to every test so
real crashes are captured. It only drains /traces when a test exits
non-zero (the known caveat is documented in the runner at the drain
site).
run-crash-handler.test.ts spawns processes that crash on purpose with env: bunEnv, which inherits that URL, and native-plugin.test.ts's
"prints name when plugin crashes" does the same via Bun.$. Both files
pass (exit 0), so their five crash reports stay on the remap server:
Segmentation fault at address 0x00000000 # native-plugin
panic: invoked crashByPanic() handler # run-crash-handler (x2)
Bun ran out of memory # run-crash-handler
Segmentation fault at address 0xDEADBEEF # run-crash-handler
In build 72106 a transient npm-registry hang on one tart agent
(66790-tart-26) made dev-server-ssr-100's bun i block for 100 s
and time out. That non-zero exit drained /traces, inherited the five
deliberate crashes, and became error = "crash reported"; isAlwaysFailure("crash reported") blocks retries, so one transient
timeout became a hard RED. next-auth.test.ts hit the same npm hang on
the same agent minutes later, got normal retries, and passed on attempt Bump @vscode/web-custom-data from 0.4.5 to 0.4.6 #4.
Same five reports pinned on unrelated tests in other recent PR builds:
build 72085: test/js/web/fetch/fetch-leak.test.ts (segfault at 0x0 from native-plugin)
Fix
Set BUN_CRASH_REPORT_URL="" (and BUN_ENABLE_CRASH_REPORTING=0 for
the fall-through branch in is_reporting_enabled()) on every spawn that
crashes on purpose but is not asserting on upload behaviour:
run-crash-handler.test.ts: the three env: bunEnv spawns now use a
shared noReportEnv.
native-plugin.test.ts: the "prints name when plugin crashes" Bun.$
command sets the two vars inline.
The "automatic crash reporter" and "raise ignoring panic handler" tests
already point BUN_CRASH_REPORT_URL at their own local server and are
unchanged.
Verification
Simulated CI's remap server and ran the test file against it:
bun bd test test/cli/run/run-crash-handler.test.ts passes. native-plugin.test.ts "prints name when plugin crashes" is skipIf(isASAN) so it is skipped under the debug build; a neighbouring
case in the same file still loads, and the inline-env override was
verified separately (Bun.$\VAR="" ...`reaches the child as an empty string, whichis_reporting_enabled()` treats as disabled).
Test-only change; no src/ diff because the behaviour being fixed lives
in the CI runner and the child's env, not in bun itself.
no test proof · iteration 3 · Platform-specific test-only change;
deferring to CI.
e16e6d jest: fix null deref in useFakeTimers when setTimeout is a non-object cell (#34030)
What does this PR do?
Fixes a segfault found by fuzzing. jest.useFakeTimers() sets a clock
marker property on globalThis.setTimeout so that testing-library/react
can detect fake timers. The guard before the write used is_cell(), but
strings, symbols and heap bigints are cells that are not objects. JSC__JSValue__put does asCell()->getObject() which returns nullptr
for those and then calls ->putDirect() on it.
Repro:
globalThis.setTimeout="x";Bun.jest().jest.useFakeTimers();// panic(main thread): Segmentation fault at address 0x0
Changed the guard to is_object() so the marker write is simply skipped
when setTimeout has been replaced with a primitive. The useRealTimers side was already safe since JSC__JSValue__deleteProperty checks isObject() internally.
How did you verify your code works?
Added a test.each over string, symbol and bigint that spawns a
subprocess, overrides setTimeout, calls useFakeTimers()/useRealTimers() and asserts clean exit. Segfaults on USE_SYSTEM_BUN=1, passes on this build.
ASAN without fix: 3 FAILED
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test"--reporter=junit""--reporter-outfile=/tmp/mechgate.xml" test/js/bun/test/test-timers.test.tsinfo: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: checking for self-update (current version: 1.29.0)bun test v1.4.0 (a27a303bf)test/js/bun/test/test-timers.test.ts:(pass) we can go back in time [38.51ms](pass) advanceTimersByTime ticks from the setSystemTime value [5.58ms](pass) setSystemTime accepts pre-epoch and epoch times and resets with no argument [4.56ms]91 | env: bunEnv,92 | stdout: "pipe",93 | stderr: "pipe",94 | });95 | const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);96 | expect({ stdout, stderr, exitCode }).toEqual({ stdout: "ok\n", stderr: "", exitCode: 0 }); ^error: expect(received).toEqual(expected) {- "exitCode": 0,- "stderr": "",- "stdout": - "ok+ "exitCode": 1,+ "stderr": + ".... (truncated)release without fix: all passedbun test v1.4.0-canary.1 (764f47f1d)test/js/bun/test/test-timers.test.ts:(pass) we can go back in time [2.73ms](pass) advanceTimersByTime ticks from the setSystemTime value [0.10ms](pass) setSystemTime accepts pre-epoch and epoch times and resets with no argument [0.06ms](pass) useFakeTimers does not crash when globalThis.setTimeout is 'x' [11.80ms](pass) useFakeTimers does not crash when globalThis.setTimeout is Symbol() [11.13ms](pass) useFakeTimers does not crash when globalThis.setTimeout is 1n [11.85ms](pass) real timer heap is ticked against the real clock under useFakeTimers [36.14ms] 7 pass 0 fail 27 expect() callsRan 7 tests across 1 file. [231.00ms]__F:0:S:0
passes on PR (with fix)
ASAN with fix: all passed
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test"--reporter=junit""--reporter-outfile=/tmp/mechgate.xml" test/js/bun/test/test-timers.test.tsinfo: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: checking for self-update (current version: 1.29.0)bun test v1.4.0 (a27a303bf)test/js/bun/test/test-timers.test.ts:(pass) we can go back in time [49.49ms](pass) advanceTimersByTime ticks from the setSystemTime value [6.51ms](pass) setSystemTime accepts pre-epoch and epoch times and resets with no argument [4.69ms](pass) useFakeTimers does not crash when globalThis.setTimeout is 'x' [538.72ms](pass) useFakeTimers does not crash when globalThis.setTimeout is Symbol() [444.00ms](pass) useFakeTimers does not crash when globalThis.setTimeout is 1n [443.49ms](pass) real timer heap is ticked against the real clock under useFakeTimers [1843.89ms] 7 pass 0 fail 27 expect() callsRan 7 tests across 1 file. [5.36s]__F:0:S:0release with fix: all passed
$ bun scripts/build.ts --profile=releaseinfo: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: checking for self-update (current version: 1.29.0)[configured] bun-profile → bun (stripped) in 709ms (unchanged)ninja: Entering directory `/workspace/bun/build/release'[1/21] gen generated_host_exports.rsgenerated_host_exports.rs: 91 exports (host=3, lazy=10, generic=78, rust=0); 243 extern-C blocks audited[2/21] gen JS modules (bundle-modules)Preprocess modules (6796ms)Bundle modules (64ms)Postprocesss modules (156ms)Bundle Functions (697ms)Generate Code (79ms)[7.81s] Bundled "src/js" for production 1912 kb 162 internal modules 12 native modules 90 internal functions across 19 files[2/6] cargo bun_bin → libbun_rust.a (--target x86_64-unknown-linux-gnu)info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnuinfo: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)info: component rust-src is up to dateinfo: component rust-std is up to date nightl... (truncated)
b0b9f6 napi: finalize threadsafe function when last ref is released after abort (#34026)
Three related mechanisms could leave an aborted napi_threadsafe_function with its finalizer never invoked and its
event-loop keepalive still held, so the process (or Worker) hung at
exit. Node v26 finalizes and exits 0 in each case.
Reproduction
// create + acquire + abort, then on a later tick release the last refnapi_create_threadsafe_function(env, cb, ..., /*initial_thread_count=*/1, ..., fin, ..., &tsfn);
napi_acquire_threadsafe_function(tsfn); // thread_count 2napi_release_threadsafe_function(tsfn, napi_tsfn_abort); // thread_count 1, closing// ... next tick ...napi_release_threadsafe_function(tsfn, napi_tsfn_release); // thread_count 0
timeout 5 bun driver.mjs returns 124; fin never runs. The same hang
reproduces when items are queued before abort, and when two or more
producers are parked in napi_call_threadsafe_function(..., napi_tsfn_blocking) on a bounded queue at abort time.
Cause
ThreadSafeFunction::release() only called schedule_dispatch()
under !is_closing(), so releasing the last reference of an
already-aborted tsfn never reached dispatch_one's thread_count == 0
finalize path.
dispatch_one() returned has_more = !is_closing(), so once closing
it processed at most one queued item per scheduled dispatch and never
drained to the finalize path.
enqueue()'s blocking wait loop only checked queue occupancy (Node's Push() also checks state == kOpen), and release(abort) used signal(), so with multiple blocked producers at most one woke.
Fix
release(): when prev_remaining == 1 and the tsfn is already
closing, schedule a dispatch so dispatch_one observes thread_count == 0 and queues the finalizer. schedule_dispatch() is idempotent via the dispatch_state swap.
dispatch_one(): keep returning true after dequeuing so on_dispatch loops until the queue is empty, where the existing thread_count == 0 branch finalizes.
enqueue(): guard the blocking wait on !is_closing() and broadcast() on abort so every blocked producer observes closing and
releases its reference.
Verification
$ bun bd test test/napi/napi.test.ts -t "napi_threadsafe_function"
(pass) napi > napi_threadsafe_function > does not hang on finalize
(pass) napi > napi_threadsafe_function > keeps the event loop alive without async_work
(pass) napi > napi_threadsafe_function > runs the finalizer and exits when the last reference is released after abort (0 queued items)
(pass) napi > napi_threadsafe_function > runs the finalizer and exits when the last reference is released after abort (3 queued items)
(pass) napi > napi_threadsafe_function > wakes blocked producers, runs the finalizer and exits when aborted with a bounded queue
All three new cases time out on the unfixed build and match Node's
output (finalized: true, exit 0) with the fix. Node's own test_threadsafe_function suite continues to pass.
no test proof · iteration 1 · Platform-specific test(s) that do not
run on this machine. Deferring to CI, which covers all platforms:
test/napi/napi.test.ts
3deda1 test(fs): assert on path segment instead of "1234" substring in tmpdirTestMkdir (#34050)
Repro
Build 72286 went red on test/js/node/fs/fs.test.ts:
error: expect(received).not.toInclude(expected)
Expected to not include: "1234"
Received: "/tmp/buntmp-isTkgw/fs.test.ts/1783876123471b71a01c3"
at tmpdirTestMkdir (test/js/node/fs/fs.test.ts:91:19)
Cause
tmpdirTestMkdir() builds ${tmpdir()}/fs.test.ts/${now}/1234/hi where now is Date.now().toString() + <8 hex chars>, then asserts that the
path mkdirSync({recursive: true}) returns (the first directory it
created) does not include "1234". That fails whenever the Date.now()
digits happen to contain 1234 as a substring, as 1783876123471 did
at 2026-07-12T17:08:43Z. The random hex suffix can also produce it.
The helper has been in place since #14510; this is a time-dependent
flake, not a recent regression.
Fix
Check path.basename(res) against the two leaf segment names ("1234", "hi") instead of a substring of the whole path. Same invariant (the
returned path is a parent of the leaf), no dependence on the digits of now.
Verification
With now forced to the failing value "1783876123471b71a01c3", mkdirSync returns /tmp/fs.test.ts/1783876123471b71a01c3; the old
assertion fails on the substring while path.basename is the ${now}
segment and the new assertion passes.
$ bun bd test test/js/node/fs/fs.test.ts
400 pass
6 skip
0 fail
no test proof · iteration 0 · Platform-specific test-only change;
deferring to CI.
c07bd5 udp: reject out-of-range connect.port instead of silently clamping to 0 (#34029)
What does this PR do?
Bun.udpSocket({connect: {port}}) with a port outside 1..=65535 was
silently rewritten to 0 in UDPSocketConfig::from_js. The socket
would kernel-connect to port 0, report remoteAddress === undefined,
and every send()/sendMany() would return true while 100% of the
datagrams were dropped.
constrx=awaitBun.udpSocket({socket: {data(){console.log("got a datagram");}}});constbadPort=65536+rx.port;consttx=awaitBun.udpSocket({connect: {hostname: "127.0.0.1",port: badPort}});console.log(tx.remoteAddress);// undefined, yet "connected"console.log(tx.send("hello"));// true// "got a datagram" never prints; /proc/net/udp shows tx connected to 0100007F:0000
The bind side of the same constructor already throws Expected "port" to be an integer between 0 and 65535 for the same values, and node:dgram connect() throws ERR_SOCKET_BAD_PORT before any syscall. This change
replaces the clamp with a throw, matching both.
The valid range for connect is 1..=65535 (port 0 is not a connectable
destination; node rejects it too).
The same clamp shape exists in UDPSocket::js_connect (the native
target of node:dgramSocket#connect), but that path is guarded by validatePort(port, "Port", false) on the JS side so out-of-range
values never reach it, and #32274 is already touching that line. Left it
alone here.
How did you verify your code works?
Added a parameterized test covering -1, 0, 65536, 99999, NaN, Infinity, and a non-numeric string, plus a boundary test that 1 and 65535 still connect and populate remoteAddress correctly.
Before: every out-of-range case resolved to a socket with remoteAddress === undefined.
After: every out-of-range case throws Expected "connect.port" to be an integer between 1 and 65535.
bun bd test test/js/bun/udp/udp_socket.test.ts
195 pass
0 fail
no test proof · iteration 1 · Platform-specific test(s) that do not
run on this machine. Deferring to CI, which covers all platforms:
test/js/bun/udp/udp_socket.test.ts
2e2230 node:stream: restore the destroyed-stream guard in Readable.prototype.pause (#33467)
43ee03 bundler: keep onBeforeParse NapiExternal alive across GC (#33999)
What does this PR do?
Fixes a use-after-free in the native bundler plugin path: the external
passed to build.onBeforeParse could be garbage collected while the
build was still running, and the native plugin would then dereference
freed memory.
Crash seen during bun init --react=shadcn on macOS x86_64:
jsBundlerPluginFunction_onBeforeParse stores the user's napi external
as a raw NapiExternal* inside NativePluginCallback, but JSBundlerPlugin::visitAdditionalChildrenInGCThread never visited those
pointers. The JS builtin runSetupFunction only holds the external in a
local onBeforeParsePlugins Map, so once processSetupResult returns
nothing roots it unless the user happens to capture it in an onLoad/onResolve closure.
When GC runs during the build, ~NapiExternal fires the napi finalizer
and frees the underlying data. NativePluginList::call then reads callbacks[i].external->value() off a freed GC cell and hands that
pointer to the native callback on a worker thread, which dereferences it
(heap-use-after-free / segfault). The serve / bake dev server keeps the
plugin around across rebuilds, which makes the window between setup and
parse large.
Fix
Add a WriteBarrierList<NapiExternal> onBeforeParseExternals to BundlerPlugin (mirroring deferredPromises), append the external when
it is registered, and visit it from visitAdditionalChildrenInGCThread.
The raw pointer in NativePluginCallback stays valid for the lifetime
of the JSBundlerPlugin.
How did you verify your code works?
New test in test/bundler/native-plugin.test.ts spawns a subprocess
that:
creates the external inline with no other JS references
forces Bun.gc(true) from a deferred onLoad while the build is
running
asserts the napi finalizer for the external did not run during the
build
native_plugin.cc gained a global finalizer counter and a getExternalFinalizedCount() export so the test can observe
finalization without relying on the UAF to actually crash.
no test proof · iteration 0 · Platform-specific test(s) that do not
run on this machine. Deferring to CI, which covers all platforms:
test/bundler/native-plugin.test.ts
669563 crypto: use BoringSSL RAND_bytes for node:crypto random functions (#32348)
8624c2 test(proxy-stress-matrix): share proxy pair across concurrent tests (#33984)
Problem
test/js/bun/http/proxy-stress-matrix.test.ts went red on darwin 14
aarch64 in build 71934:
error: Unable to connect. Is the computer able to access the url?
path: "https://localhost:60036/",
errno: 0,
code: "ConnectionRefused"
✗ response matrix > https-proxy → https-origin chunked/deflate 65536B keepalive=true
334 pass / 1 fail
The runner additionally labelled it "5 crashes reported", which skipped
the automatic retry; those crashes are the intentional ones from run-crash-handler.test.ts and native_plugin_test earlier in the same
shard that the crash-report collector misattributed (the traces carry 0xDEADBEEF / invoked crashByPanic() handler). The single
ConnectionRefused is the actual failure.
Cause
Same mechanism #33975 diagnosed for the sibling proxy-stress-headers.test.ts: 335 test.concurrent cases each create
a fresh adversarial proxy + origin, so the file issues ~676 listen(0, "127.0.0.1") calls in <1s under the runner's rolling concurrency
window. As early tests dispose their servers, a later test's listen(0)
can be handed a just-freed port while a sibling is still mid-dial (the
proxy's net.connect(port, "localhost") goes through autoSelectFamily,
adding async hops between port capture and connect). #33975 left this
file alone because it was not yet red in CI; now it is.
Fix
Share one {http, https} proxy pair from beforeAll across the 316
tests that use a default stateless proxy (response / upload / stream /
method matrices). Tests that pass non-default proxy options or assert on
the full proxy.connections array (trickled, split-CONNECT,
CONNECT-headers, hop-by-hop, redirect) keep dedicated proxies. This
drops per-file listen(0) churn from ~676 to ~362.
The response matrix's per-test proxy.connections assertions are
preserved: each test owns a unique origin port for the duration of its await using scope, so ownConnections() filters the shared proxy's
append-only log by that port (sliced from the index captured before the
fetch) to unambiguously recover this test's record under test.concurrent, and asserts exactly-one-connection / CONNECT-vs-GET / http:// target on it as before.
335 tests, 1126 expect() calls: identical to main. Introduced by
#32635; related sibling fix is #33975.
Verification
bun bd test test/js/bun/http/proxy-stress-matrix.test.ts # 335 pass, 1126 expect() (debug+ASAN)
bun bd test test/js/bun/http/proxy-stress-matrix.test.ts -t "trickled" # 2 pass, 333 filtered, 0 fail
10 consecutive release runs on linux, all clean (1126 expect() each). Build 71938 ran this file
clean on every darwin lane (14 aarch64 × 2 shards, 14 x64, 26 aarch64 ×
2).
Test-only change.Debug/ASAN (expected pass):
$ bun bd test'test/js/bun/http/proxy-stress-matrix.test.ts'
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test test/js/bun/http/proxy-stress-matrix.test.tsinfo: syncing channel updates for night
1 status change: clear-site-data regressed from Baseline newly available to limited availability, due to browser bugs previously untracked in upstream data sources.
Subscribe to the Upcoming changes announcements thread for news about upcoming releases, such as breaking changes or major features.
### v3.21.0## What's New
2 features: fetch-formdata and navigator-modelcontext
1 group: webmcp
The Baseline status of font-family-math regressed from newly available to limited availability. The feature advanced to newly available prematurely, due to an incorrect version number in upstream data. It's expected to advance correctly in the near future.
Subscribe to the Upcoming changes announcements thread for news about upcoming releases, such as breaking changes or major features.
### v3.10.0## What's New
Features with the discouraged property have reason and reason_html string properties. These provide a brief summary of why the feature was discouraged by a specification or vendor.
Features with the discouraged property may have a removal_date string property. This property marks a feature as pending removal (for dates in the future) or removed from browsers (for dates in the past).
Signed-off-by: dependabot[bot] <support@redirect.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.redirect.github.com>
Co-authored-by: Patrick Brosset <patrickbrosset@gmail.com>
491947 Assign UIEvent keys to foundational mouse-events (#4059)
These could reside with any feature that inherits from UIEvent, but
to the best of my knowledge no events use UIEvent directly, so it
makes sense to park these with the oldest, most foundational feature
that implements UIEvent.
Co-authored-by: Patrick Brosset <patrickbrosset@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
Updated Packages