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
Core: Disable component manifest by default - #34408, thanks @yannbf!
[!NOTE] Version >=0.5.0 of @​storybook/addon-mcp enables component manifests again. If you're upgrading Storybook from version >= 10.3.0 to >= 10.3.5 and are using the MCP addon, you should also upgrade @​storybook/addon-mcp to keep the docs toolset in the MCP server.
Core: Disable component manifest by default - #34408, thanks @yannbf!
[!NOTE] Version >=0.5.0 of @​storybook/addon-mcp enables component manifests again. If you're upgrading Storybook from version >= 10.3.0 to >= 10.3.5 and are using the MCP addon, you should also upgrade @​storybook/addon-mcp to keep the docs toolset in the MCP server.
Core: Disable component manifest by default - #34408, thanks @yannbf!
[!NOTE] Version >=0.5.0 of @​storybook/addon-mcp enables component manifests again. If you're upgrading Storybook from version >= 10.3.0 to >= 10.3.5 and are using the MCP addon, you should also upgrade @​storybook/addon-mcp to keep the docs toolset in the MCP server.
$ echo J1x48LmRnCf/ | base64 -d | bun-debug -panic: attempt to subtract with overflow (src/js_parser/lexer.rs:803:41)
The input is ' \ x F0 B9 91 9C ' FF — a string literal starting with \x immediately followed by a 4-byte UTF-8 codepoint.
Cause
In decode_escape_sequences, several error paths place the diagnostic
caret by computing start + iter.i - widthN — the position of the
offending character minus its byte width. When that character is the
very first thing in the string literal, iter.i can be smaller than widthN (e.g. iter.i = 1, widthN = 4), and the usize subtraction
underflows. Debug builds panic; release silently wraps to a nonsense self.end.
The original Zig source used -| (saturating subtract) in two of these
spots (lexer.zig:511, 517), but the rest used raw subtraction —
inherited as-is by the Rust port.
Fix
Use saturating_sub in every such error path in decode_escape_sequences. The result is only used to produce a caret
position inside an error message; saturating to 0 when iter.i < widthN
still gives a reasonable location. Patched sites:
2-digit \x — first hex invalid (the reported panic)
2-digit \x — second hex invalid
\u fixed-length — any of 4 hex digits invalid
JSON-mode checks inside \0, \u{, \r, \n, and the catch-all
non-JSON-allowed escape
octal_start computation for the legacy-octal range error
Four new tests in test/regression/issue/invalid-escape-sequences.test.ts cover \x
(1st-hex), \x2 (2nd-hex), \u (fixed-length), and \u{
(variable-length) each followed by a 4-byte codepoint. Without the fix:
3 of the 4 panic on their respective lines (803/817/909); the \u{
variant already used saturating_sub so it passes either way but is
retained for coverage.
� decodes to 0x76B22B, well beyond the maximum valid Unicode
codepoint U+10FFFF.
Cause
maybe_decode_jsx_entity (src/js_parser/lexer.rs:3312) parses the
numeric entity as i32 and only errors on InvalidCharacter / Overflow from bun_core::parse_int. Any value that fits in i32 is
forwarded to strings::push_codepoint_utf16 (called from decode_jsx_entities), which calls encode_surrogate_pair → u16_lead / u16_trail. Both assert supplementary <= 0x10FFFF
(src/bun_core/lib.rs:1319 and :1326). Debug builds panic; release
builds silently encode garbage surrogate pairs (the Zig original used @​truncate inline and hid the problem).
Fix
Reject values outside the Unicode range 0..=0x10FFFF in the same
arm that handles parse_int success, emitting the existing
"JSX entity escape is too big" diagnostic and substituting UNICODE_REPLACEMENT (U+FFFD) — matching the Overflow branch.
Verification
$ bun-debug -e 'const x = <div>�</div>'1 | const x = <div>�</div> ^error: JSX entity escape is too big: #7777707
Boundary cases covered:
 (max valid) — still encodes as a surrogate pair
� (max + 1) — "JSX entity escape is too big"
&#-1; (negative i32) — "JSX entity escape is too big"
� (i32::MAX) — "JSX entity escape is too big"
Test added to test/bundler/transpiler/transpiler.test.js alongside
the other JSX lexer tests. Without the fix the test binary panics in u16_lead; with the fix each out-of-range entity throws and the  boundary still compiles.
Release builds correctly emit a user-facing syntax error; only the debug
assertion fires, because the assertion is gated on cfg!(debug_assertions).
Cause
src/js_parser/parse/parse_property.rs at the static { ... } branch
entered the class-static-block path purely on identifier + {, without
checking whether the enclosing context was a class body:
}elseif p.lexer.token == T::TOpenBrace && name == b"static"{// ... parse statements until `}` ...returnOk(Some(G::Property{kind:PropertyKind::ClassStaticBlock,class_static_block:Some(js_ast::StoreRef::from_bump(block)),
..Default::default()// key = None, value = None}));}
Every sibling branch in the same match (PStatic, PDeclare, PAbstract, PAccessor, PPrivate/PProtected/PPublic/POverride/PReadonly)
already gates on opts.is_class; this one was the only exception. When
invoked from the object-literal path
(parse_prefix.rs::pfx_t_open_brace), opts.is_class is false and the returned property had neither key
nor value, tripping the debug_assert in the caller.
Fix
Add opts.is_class && to the condition so static { ... } is only
parsed
as a class static block inside a class body. Outside a class body static
now falls through to ordinary identifier handling and the trailing {
produces a syntax error, matching release-mode behavior.
The Zig sibling (parse_property.zig) had the same un-gated branch;
it's
updated in lockstep to keep the porting reference aligned.
Test
test/bundler/transpiler/transpiler.test.js — added four malformed
object-literal cases ([{static{}, ({static{}}), ({static{};}), ({static{},})) alongside the existing "class static blocks" suite,
each
asserting a SyntaxError with Expected "}" but found "{".
Verification
bun bd test test/bundler/transpiler/transpiler.test.js -t "class static blocks" — 1 pass, 24 expect() calls (130 filtered).
With the fix stashed, the same test panics at the original assertion
site → confirms the test exercises the fix.
bun bd test test/cli/run/syntax.test.ts — 590 pass; valid class A{static{}} still works.
Blob::borrowed_view() constructed its name field via OwnedStringCell::new(self.name.get()), which copies the String value without bumping its refcount. The surrounding doc comment assumed name was Copy raw data with no Drop, but OwnedStringCell does implement Drop (it deref()s the inner String).
So every time a borrowed_view() was dropped — which happens in BunFile.prototype.write(), Bun.write(file, ...), and WriteFile::create via PathOrBlob::Blob(...) — the original Blob's nameStringImpl was deref'd without a matching ref.
After two writes to a BunFile whose .name had been materialized, the StringImpl refcount hit 0 while the cached JSString in JSBlob::m_name still pointed at it. The next GC then hit StringImpl::costDuringGC() → divideRoundedUp(len, refCount()) with refCount() == 0 → SIGFPE.
Fix
Clone the OwnedStringCell (which dupe_ref()s, balanced by its Drop), same as the store field already does.
How did you verify your code works?
Added regression tests to test/js/bun/io/bun-write.test.js covering file.write(), Bun.write(file, ...), and concurrent writes followed
by GC. All crash on the unfixed binary and pass with the fix.
Found by Fuzzilli (fingerprint db407a6431bfbfa7).
c47ec9 Validate Bun.password.hash memoryCost against argon2 minimum (#30964)
Fixes #30960.
Problem
Bun.password.hash({ algorithm: "argon2id", memoryCost: N }) silently
rounded up any N < 8 to 8 and emitted the clamped value in the
encoded PHC string:
consthash=awaitBun.password.hash("test",{algorithm: "argon2id",memoryCost: 3,timeCost: 1,});// Actual: m=8,t=1,p=1// Expected: an error, or m=3,t=1,p=1
This is a 1.4.0 regression from the Rust port. The old Zig impl passed m through unchanged because its argon2 had a special clamp on working
memory (@​max(m_rounded_down, 2*sync_points*p)). rust-argon2 instead
hard-rejects mem_cost < 8*lanes with MemoryTooLittle, and the port
worked around that by clamping m up to 8 * p before the call — the
clamped value is what got baked into the PHC output.
Fix
Validate at the JS argument-parsing boundary (AlgorithmValue::from_js
in PasswordObject.rs) and reject memoryCost < 8 with "Memory cost
must be at least 8". Bun hard-codes parallelism = 1, so 8 * parallelism == 8. The clamp in the pwhash shim is removed — with the
validation in place, rust-argon2 sees only valid parameters and the
user-provided value round-trips through the encoded hash.
Docs (docs/runtime/hashing.mdx, docs/guides/util/hash-a-password.mdx) used memoryCost: 4 as an
example; bumped to the documented minimum of 8.
Verification
$ bun bd /tmp/repro.ts
error: Memory cost must be at least 8
Added regression coverage in test/js/bun/util/password.test.ts:
invalid algorithm throws now also asserts that memoryCost of 1, 3,
7 each throw with "Memory cost must be at least 8".
New argon2 memoryCost at the 8 minimum is encoded faithfully test
confirms memoryCost: 8 produces m=8,t=1,p=1 in the PHC string and
verifies round-trip.
The stashed-src gate confirms the new assertions fail against unmodified
source (clamp returns silently) and pass with the fix.
JSPromise is no longer JSInternalFieldObjectImpl<2>. The getPromiseInternalField/putPromiseInternalField bytecode intrinsics
and the promiseField*/promiseState* intrinsic constants were removed
upstream. Added three C++ host functions exposed as private globals — $peekPromiseStatus(p) (0/1/2 = pending/fulfilled/rejected), $peekPromiseSettledValue(p), and $pokePromiseAsHandled(p) — and
rewrote all 26 builtin call sites across Peek.ts, CommonJS.ts, BundlerPlugin.ts, StreamInternals.ts, WritableStreamInternals.ts, ReadableStreamInternals.ts, and internal/util/inspect.js. The $isPromiseFulfilled/$isPromiseRejected/$isPromisePending codegen
macros now expand to $peekPromiseStatus(...) === N.
C++ bindings (bindings.cpp, BunPlugin.cpp) that touched JSPromise::Field/internalField() switched to flags()/setFlags()/setSlot()/payloadCell().
RapidHash (ee2220df2080)
WTF replaced WyHash/SuperFastHash with RapidHash. The static-property
lookup tables (*.lut.h) embed the string hash, so the create_hash_table perl script must produce the same hash the runtime
computes. Replaced src/codegen/create_hash_table with the upstream
RapidHash version and re-grafted Bun's ConstantInteger extension.
Without this every static property on JSGlobalObject (Bun, fetch, process, ...) is invisible.
JSType enum (24cf2e544f58)
JSWebAssemblyStreamingContextType was inserted at slot 27 — src/jsc/JSType.rs/.zig re-numbered.
WebAssembly streaming hooks
compileStreaming/instantiateStreaming global hook signatures
changed: the JSPromise* is now passed in instead of returned.
JSModuleNamespaceObject/AbstractModuleRecord
getModuleNamespace() gains a ModulePhase argument (import defer);
Bun's shouldPreventExtensions parameter is preserved as a trailing
default.
Known regression (pre-existing investigation)
Error inside minified file snapshot tests in inspect-error.test.js
show an extra at require (native:50:24) frame. The require builtin's ImplementationVisibility::Private is no longer hiding the frame after
the upstream PCH/builtin refactor — needs follow-up.
9ecb98 Fix FileSink.start() crash when called without path/fd on an open writer (#30953)
What
Fixes a debug assertion failure (fd != Fd::INVALID in src/sys/Error.rs) when calling .start() on a FileSink created by Bun.file(path).writer() with an options object that does not include a path or fd property.
Start::from_js_with_tag::<FileSink> returns Start::FileSink { input_path: Fd(Fd::INVALID), .. } when the options object has neither path nor fd. FileSink::start() then unconditionally called setup(), which called dup_with_flags(Fd::INVALID, 0). The fcntl
fails with EBADF, and when building the error via .with_fd(Fd::INVALID) we hit the debug assertion.
In Blob::get_writer the invalid-fd placeholder is always overwritten
with the real file path/fd before start() is called, but the
JS-exposed .start() has no such override.
How
Add a match guard in FileSink::start() to skip setup() when the
incoming input_path is the invalid-fd placeholder — the writer is
already configured, so we only update done/started/signal state as
before. Calls with a real path or fd still re-run setup().
80a06a Add cargo-miri support and fix HiveArray aliasing UB (#30876)
Fixes #30719
Miri support
Adds bun run rust:miri (scripts/rust-miri.ts), which runs cargo miri test over the FFI-free crate set (bun_collections, bun_paths, bun_clap, bun_base64, bun_hash, bun_wyhash, bun_ptr, bun_md, bun_ast, bun_dispatch, bun_errno, bun_http_types, bun_resolve_builtins, bun_shell_parser).
Aliasing model is -Zmiri-tree-borrows, not the default Stacked
Borrows. Stacked Borrows pops every raw pointer derived through &mut self the moment a later &mut self is formed — that's the entire
premise of the slab/pool/slot types in this codebase. Tree Borrows is
the candidate replacement spec, allows that pattern, and still catches
the bugs we care about (UAF, OOB, uninit, races).
Bit-rot fixes
cargo test has never run on these crates. Enabling it surfaced compile
errors and wrong assertions that have been there since the original
port:
bun_collections: dead test stub needing generic_const_exprs, wrong
type name, ambiguous init(), plus a cfg(miri) iteration-count guard.
bun_paths: back_then_forward expected previous() → None to
rewind the cursor; it doesn't (matches std.fs.path.ComponentIterator).
Plus 2 doc-comments that compiled as failing doctests.
bun_clap: the errors test expected unrecognized long flags to
error; Bun's StreamingClap intentionally skips them (warn + continue).
bun_base64: test_base64_url_safe_no_pad fed unpadded input to decoder_with_ignore, which for URL_SAFE_NO_PAD is the padded
decoder (the field is shared with URL_SAFE). Reworked the test helpers
to feed each decoder the form it accepts; the impl is unchanged.
bun_ast: 3 doc-comments compiling as failing doctests.
HiveArray interior mutability
Tree Borrows immediately flagged HiveArray/Fallback/HiveRef as UB.
The pool used &mut self receivers and handed out raw *mut T pointers
into its internal buffer. Under noalias, any subsequent &mut self
reborrow invalidates pointers derived from a prior one. This was a
mechanical Zig→Rust translation: Zig's *Self carries no aliasing
contract, but Rust's &mut self is noalias.
Fix is the bumpalo/typed-arena shape — &self receivers + interior
mutability:
HiveArray::buffer → UnsafeCell<[MaybeUninit<T>; CAP]>. Slot
pointers come from UnsafeCell::get() and survive &self reborrows.
Made private; ptr_at() is the typed accessor.
HiveBitSet::masks → [Cell<usize>; 32]. Same size, same alignment,
no atomics; verified identical codegen.
All &mut self receivers → &self. HiveSlot::write() writes
through a raw pointer.
HiveRef::ref_count → Cell<u32>; unref(this: *mut Self) (not a
protected &mut self it would then free through the pool back-pointer).
New HiveRefHandle<T, CAP> smart pointer — Clone/Drop track the
refcount, into_raw()/from_raw() for raw round-trips, get_mut()
instead of DerefMut (a blanket DerefMut on a shared-ownership handle
is unsound). Safe API; unsafe only at raw-pointer ingress.
Adds 11 tests covering the previously-untested API surface. All 13 hive
tests pass under MIRIFLAGS=-Zmiri-tree-borrows cargo miri test -p bun_collections hive_array with no ignores.
HiveRefHandle migration
Request.body and RequestContext.request_body migrated from raw NonNull + manual ref/unref to HiveRefHandle. Value::ref_/unref
deleted (they recovered the parent HiveRef from a *mut Value via offsetof, only needed because RequestContext held a payload
pointer). The RuntimeHooks.init_request_body_value vtable slot
deleted: its only caller and only impl were both in bun_runtime. Net unsafe delta across the touched files: −17.
Verification
cargo +nightly check --workspace # clean
cargo +nightly test -p bun_collections # 35 passed
MIRIFLAGS=-Zmiri-tree-borrows cargo +nightly miri test -p bun_collections hive_array # 13 passed, 0 ignored
bun run rust:miri # all 14 crates pass
error: Duplicate package path
at bun.lock:XXX:5
InvalidPackageKey: failed to parse lockfile: 'bun.lock'
Also reproduces without -g — any `bun add ` run twice
duplicates the entry. It's the folder-install code path, not the
global-install code path, that's broken. The same shape also regresses
`bun add ` (previously fixed in #30499 for the Zig file,
which is no longer compiled after the Rust port).
Cause
`PackageJSONEditor::edit`'s initial name-match phase looks up the
existing entry by `UpdateRequest::get_name()`. For a non-aliased
folder/path/tarball positional, `get_name()` returns the version
literal (the path/URL the user typed), not the resolved package name,
so `query.expr.as_property(name)` never finds the existing
`""` key and the code falls through to the append path.
The fallback URL-value-match loop below only fired for `Tag::Github` /
`Tag::Git`, so `.folder` and `.tarball` both fell through to the
append path. `get_resolved_name(lockfile)` then returned the real
package name (`"myproject"`) which was written as a second key
next
to the existing `"myproject": "/tmp/myproject"` entry — producing
two keys with the same name.
Fix
Extend the fallback URL/path-match loop to also match on `.tarball`
and `.folder`, skip it when the user wrote `alias@url` (that form is
an explicit request to key by `alias`, so consolidating into an
existing entry under a different name would silently drop the alias),
and guard on `request.e_string.is_none()` so a match in an earlier
dependency list (e.g. `dependencies`) isn't re-counted when the
outer loop continues to the remaining lists (`devDependencies`, etc.).
The `.zig` sibling is kept aligned as a porting reference (it is no
longer compiled — CLAUDE.md: "not compiled, not shipped").
Verification
Two regression tests added in `test/cli/install/bun-add.test.ts`:
`should not add duplicate package.json entries when installing the
same local folder twice (#30933)` — this issue.
`should not add duplicate package.json entries when installing the
same tarball URL twice (#30499)` — re-port of the test dropped during
the Rust rewrite.
Gate:
`USE_SYSTEM_BUN=1 bun test test/cli/install/bun-add.test.ts -t
'30933|30499'` — both fail on the duplicate `"":` key.
`bun bd test test/cli/install/bun-add.test.ts -t '30933|30499'` —
both pass with the fix.
Existing tests that exercise the same fallback still pass:
`should add local tarball dependency` ✅
`should add dependency without duplication` ✅
`should add aliased dependency (npm)` ✅
Supersedes #30500 (which targeted the `.zig` file only).
Fixes a Vec allocator-layout UB in to_bun_string_from_owned_slice's Ucs2/Utf16le arm — the sole code path for fs.readFile(path, { encoding: "utf16le" | "ucs2" }).
What was wrong
The arm took an owned Vec<u8> and rebuilt it as a Vec<u16> via Vec::from_raw_parts(ptr.cast::<u16>(), len/2, cap/2), then handed that
to WebKit's external-string deallocator. This is a direct port of the
Zig original's @​alignCast(bytesAsSlice(u16, …)), but Rust's Vec is
stricter:
Vec::from_raw_parts requires that T's alignment equal the
alignment of the original allocation. Vec<u8> allocates with Layout
align 1; reclaiming as Vec<u16> makes the eventual dealloc use align
Per alloc::alloc::dealloc's safety contract, the Layout used to
free must match the one used to allocate. Mismatched align is UB.
The TODO(port) comment above the code already called this out:
Reinterpreting a Vec<u8> as Vec<u16> is not generally sound in
Rust (alignment + allocator layout).
Why it never crashed
mimalloc gives us over-aligned (>= 8 byte) pointers, so the layout
mismatch is benign in practice on the allocator we ship. Miri would flag
it, and a future allocator change would surface it as either a crash on
free or silent heap corruption.
The fix
Mirror the already-merged solution in construct_from_u16's utf16le arm
(same file). That arm had the symmetric Vec<u16> -> Vec<u8>
reinterpret problem when porting from Zig, and the porter resolved it
the same way — comment from encoding.rs:786-789:
The Zig original allocated u16-aligned then reinterpreted the Vec
header to u8, which is allocator-layout-dependent in Rust; a fresh u8
Vec sidesteps that…
Same shape here, opposite direction: allocate a fresh Vec<u16> and
copy the input bytes into it via bytemuck::cast_slice_mut. Sound by
construction — the Vec is allocated and freed with the same Layout.
- // TODO(port): Zig reinterpreted the owned u8 allocation as []u16 ...- let as_u16 = unsafe {- let mut input = core::mem::ManuallyDrop::new(input);- Vec::from_raw_parts(- input.as_mut_ptr().cast::<u16>(),- usable_len / 2,- input.capacity() / 2,- )- };+ let mut as_u16 = vec![0u16; usable_len / 2];+ let dst: &mut [u8] = bytemuck::cast_slice_mut(&mut as_u16);+ dst.copy_from_slice(&input[..usable_len]);
create_external_globally_allocated_utf16(as_u16)
Tradeoff
One extra usable_len-byte memcpy on the fs.readFile(path, "utf16le"|"ucs2") hot path. For typical file sizes (KB-MB) this is
negligible. The zero-copy path can be restored later by adding a bun_core::String constructor that accepts (ptr, len, cap, dtor) —
exactly what the original TODO(refactor) suggested.
Sole caller
src/runtime/node/node_fs.rs:7071 — the fs.readFile(path, { encoding }) return-as-string path, when the encoding is utf16le or ucs2
(Latin-1/UTF-8 paths take separate arms and aren't affected).
Test plan
bun bd test test/regression/issue/utf16-encoding-crash.test.ts — fs.readFileSync(path, "utf16le") plus "ucs2", including a 256 KB + 1
byte case that hits the dynamic-allocation branch
bun bd test test/js/node/buffer-utf16.test.ts — Buffer.from(str, "utf-16le") roundtrip
$ bun bd --asan=off test test/regression/issue/utf16-encoding-crash.test.ts test/js/node/buffer-utf16.test.ts
3 pass
0 fail
9 expect() calls
No new tests added — this is a soundness fix, not a behavior fix. The
existing tests already exercise the relevant code path (fs.readFile
with utf16le/ucs2 encodings, including the >256 KB
dynamic-allocation branch); their pre/post-fix behavior is identical, as
expected.
Co-authored-by: Khang Le Duy <khangl@nvidia.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
c7a757 Collapse ThreadPool::init dead generic and fix cpuid cfg gate (#30886)
ThreadPool::init dead generic.bundler::ThreadPool::init (and its
helper init_with_pool) were generic over V2 because, during the
phased Zig→Rust port, bundle_v2.rs carried two BundleV2 definitions
— the canonical one plus a bv2_impl draft module — and both needed to
call init. The PORT NOTE on the function explicitly said it should
collapse to &BundleV2<'_> once bv2_impl was dropped. That module is
gone (grep -rn "struct BundleV2" src/bundler/ finds exactly one
definition, bundle_v2.rs:75), so this collapses the generic, names the
concrete type, and drops the now-stale PORT NOTE. The body was already
storing a type-erased raw pointer, so the monomorphised code is
identical.
cpuid cfg-gate mismatch.perf/hw_timer.rs defines struct CpuidResult and fn cpuid() under #[cfg(target_arch = "x86_64")],
but their only callers live inside a #[cfg(not(any(target_os = "macos", target_os = "freebsd")))] block (macOS/FreeBSD read the boot-time TSC
frequency from sysctl instead of probing CPUID). On x86_64-apple-darwin and x86_64-unknown-freebsd the helpers were
therefore compiled with no callers, producing never constructed / never used warnings. The cfg gates now mirror the callers' conditions.
Verified with cargo check --workspace --keep-going (clean), bun run rust:check-all (10/10 targets), and cargo fmt -p bun_bundler -p bun_perf --check. Confirmed the dead-code warnings on x86_64-apple-darwin are present without this change and gone with it.
File set is disjoint from #30879.
172afa Replace bun assert helpers with Rust builtin assert macros (#30918)
880ee8 Clean up Zig-port phase comments and trivial lint warnings (#30877)
What
Removes the ~1,750 stale "Phase A" / "Phase B" references the Zig→Rust
port left across ~600 files. The port phases are complete; the
references confuse what's a real TODO vs. a finished process step.
Comments that encode real deferred work (e.g. PERF(port): was X — profile in Phase B) keep the substance and drop the phase framing
(PERF(port): was X — profile if hot.). Comments that only describe
past process steps are removed.
Also fixes the trivial lint warnings cargo check surfaced along the way:
unused imports, an unnecessary unsafe block over a safe extern "C" fn, unreachable_pub items, ambiguous glob re-exports, an unused #[must_use] result, and a private-type-in-public-alias. Two
SAFETY/rustdoc comments that referenced API methods removed in a
follow-up are rewritten to name the current entry points.
What this is not
No behavior changes. No public API changes. The hive-pool deprecation
warnings (HiveArrayFallback::get/try_get) are not silenced here — the
call sites are migrated to the safe API in a follow-up PR.
Verification
cargo check --workspace clean for everything this PR touches
Bump typescript from 4.9.4 to 4.9.5 #2 — Dangling proxy slice across reentrant JS getter — copy process.env proxy href to an owned Vec before reentrant getters can
free the env map (Blob.rs)
[master] Michijs Dependabot changes #110 — Async randomFill uses stale resizable buffer pointer — fill a
worker-owned scratch buffer; copy back on the JS thread after
re-validating bounds (node_crypto_binding.rs)
Bump rome from 12.0.0 to 12.1.0 #36 — Close reason length mismatch causes panic — clamp body_len to
125 and bail on overlong UTF-8 transcode (websocket_client.rs)
Bump typescript from 5.6.3 to 5.7.2 #56 — Unsanitized filename injects response headers — reject \r/\n/NUL/" in content-disposition filenames
(RequestContext.rs)
Bump del-cli from 5.0.0 to 5.0.1 #43 — Missing CRLF checks for signed host/auth headers — also validate region, access_key_id, and host (s3_signing/credentials.rs)
f7c692 Fix worker teardown crash from missing dupeRef on synthetic-module specifiers (#30882)
Summary
~SourceProvider() derefs m_resolvedSource.specifier and .source_url (introduced in c713ab53130b to fix a leak), which requires
every ResolvedSource producer to hand those BunStrings in as +1.
The synthetic-module paths in jsc_hooks.rs (bun:main, bun:wrap, macro:, standalone-graph, embedded sqlite) stored a bitwise copy of
the borrowed specifier (*specifier) with no extra ref, so the
destructor over-derefs. The atom impl frees while a JSString in the
worker heap still references it, and once that slot is reused, Heap::lastChanceToFinalize trips RELEASE_ASSERT(wasRemoved) in AtomStringImpl::remove during worker VM teardown — symptom is a
SIGABRT on a random atom string.
Fix: specifier.dupe_ref() for both specifier and source_url on
the paths whose ResolvedSource flows into Zig::SourceProvider::create(), matching RuntimeTranspilerStore::run_from_js_thread.
Test plan
test/js/node/test/parallel/test-worker-console-listeners.js —
480/480 clean (was ~5/240 SIGABRT) with BUN_DESTRUCT_VM_ON_EXIT=1
under 8× parallel debug-build loop
test/js/node/test/parallel/test-crypto-worker-thread.js —
400/400 clean under the same harness
Diagnostic heap walk before ~VM confirms worker heaps no longer
hold a bun:mainJSString whose StringImpl is absent from the
worker's atom table (foreign=1 → foreign=0)
8438ff resolver: split the port's module wrapper into files; type the extern-Rust pointers (#30880)
What
Module split
The Zig→Rust port wrapped the entire resolver implementation in a single
7,664-line pub mod __phase_a_body { ... } inside src/resolver/lib.rs
(lines 2609–10,273) — a port artifact ("this is the
mechanically-translated block"). There's no name for a module that wraps
a crate's whole body that isn't redundant, which is the tell the wrapper
shouldn't exist. Split it into sibling files following the crate's
existing convention (data_url.rs / dir_info.rs / package_json.rs):
lib.rs shrinks 10,273 → 2,615 lines. Public API surface is
byte-identical — bun_resolver::Resolver, ::Result, ::options, …
all resolve as before.
Typed extern-Rust pointers
Un-erase the extern "Rust" link-time pointers where the declaring
crate already names the type. The port applied "type-erase across the
crate boundary" uniformly to every #[no_mangle] upward call, but extern "Rust" carries full Rust types — both crates can name the
parameters. Where visible, use the typed pointer with the Zig pointer
shape (NonNull<T> for *T, Option<NonNull<T>> for ?*T):
Core: Disable component manifest by default - #34408, thanks @yannbf!
[!NOTE] Version >=0.5.0 of @​storybook/addon-mcp enables component manifests again. If you're upgrading Storybook from version >= 10.3.0 to >= 10.3.5 and are using the MCP addon, you should also upgrade @​storybook/addon-mcp to keep the docs toolset in the MCP server.
Core: Disable component manifest by default - #34408, thanks @yannbf!
[!NOTE] Version >=0.5.0 of @​storybook/addon-mcp enables component manifests again. If you're upgrading Storybook from version >= 10.3.0 to >= 10.3.5 and are using the MCP addon, you should also upgrade @​storybook/addon-mcp to keep the docs toolset in the MCP server.
Core: Disable component manifest by default - #34408, thanks @yannbf!
[!NOTE] Version >=0.5.0 of @​storybook/addon-mcp enables component manifests again. If you're upgrading Storybook from version >= 10.3.0 to >= 10.3.5 and are using the MCP addon, you should also upgrade @​storybook/addon-mcp to keep the docs toolset in the MCP server.
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