Commit Graph

14463 Commits

Author SHA1 Message Date
SUZUKI Sosuke
7c50164987 fix(build-jsc): enable REMOTE_INSPECTOR for macOS release builds (#26453)
### What does this PR do?

  Move `-DENABLE_REMOTE_INSPECTOR=ON` from the debug-only flags to the
  macOS common flags so it applies to all build configurations (debug,
  release, lto). This was already the case for Linux and Windows.

  Without this, `build:release:local` fails because BunDebugger.cpp and
InspectorLifecycleAgent.cpp unconditionally use JSC inspector APIs that
  are only available when REMOTE_INSPECTOR is enabled.

### How did you verify your code works?

Build locally
2026-01-26 00:19:11 -08:00
Dylan Conway
a553fda32b fix(napi): fix use-after-free in property names and external buffer lifetime (#26450)
## Summary

- **PROPERTY_NAME_FROM_UTF8 use-after-free:** The macro used
`StringImpl::createWithoutCopying` for ASCII strings, which left
dangling pointers in JSC's atom string table when the caller freed the
input buffer (e.g. napi-rs `CString`). Fixed by using
`Identifier::fromString` which copies only when inserting into the atom
table, but never retains a reference to the caller's buffer.

- **napi_create_external_buffer data lifetime:** `finalize_cb` was
attached via `addFinalizer` (tied to GC of the `JSUint8Array` view)
instead of the `ArrayBuffer` destructor. Extracting `.buffer` and
letting the Buffer get GC'd would free the backing data while the
`ArrayBuffer` still referenced it. Fixed by attaching the destructor to
the `ArrayBuffer` via `createFromBytes`, using an armed
`NapiExternalBufferDestructor` to safely handle the
`JSUint8Array::create` error path.

Closes #26446
Closes #26423

## Test plan

- [x] Added regression test `test_napi_get_named_property_copied_string`
-- strdup/free cycles with GC to reproduce the atom table dangling
pointer
- [x] Added regression test `test_external_buffer_data_lifetime` --
extracts ArrayBuffer, drops Buffer, GCs, verifies data is intact
- [x] Both tests pass with `bun bd test` and match Node.js output via
`checkSameOutput`
- [x] Verified `test_external_buffer_data_lifetime` fails without the
fix (data corrupted) and passes on Node.js
- [x] Verified impit reproducer from #26423 works correctly with the fix

Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-25 20:11:52 -08:00
Dylan Conway
f87fa27fac Update WebKit (#26449)
### What does this PR do?
Updates WebKit to
6e287faaed
### How did you verify your code works?
2026-01-25 18:43:55 -08:00
Dylan Conway
4071624edd Update WebKit (#26381)
### What does this PR do?
Updates WebKit to
5b6a0ac49b
### How did you verify your code works?

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 10:38:13 -08:00
robobun
bfe40e8760 fix(cmake): use BUILDKITE_BUILD_NUMBER to avoid 302 redirect (#26409)
## What does this PR do?

Fixes CMake "No jobs found" error during the build-bun step in CI by
using `BUILDKITE_BUILD_NUMBER` instead of `BUILDKITE_BUILD_ID` (UUID)
for the Buildkite API URL.

### Problem

When `BUN_LINK_ONLY=ON`, `SetupBuildkite.cmake` fetches build info from
the Buildkite API to download artifacts from earlier build steps
(build-cpp, build-zig).

The `BUILDKITE_BUILD_ID` environment variable contains a UUID (e.g.,
`019bee3e-da45-4e9f-b4d8-4bdb5aeac0ac`). When this UUID is used in the
URL, Buildkite returns a **302 redirect** to the numeric build number
URL (e.g., `/builds/35708`).

CMake's `file(DOWNLOAD)` command **does not follow HTTP redirects**, so
the downloaded file is empty. Parsing the empty JSON yields 0 jobs,
triggering the fatal error:

```
CMake Error at cmake/tools/SetupBuildkite.cmake:67 (message):
  No jobs found:
  https://buildkite.com/bun/bun/builds/019bee3e-da45-4e9f-b4d8-4bdb5aeac0ac
```

### Solution

Prefer `BUILDKITE_BUILD_NUMBER` (numeric, e.g., `35708`) when available,
which doesn't redirect. This environment variable is automatically set
by Buildkite.

## How did you verify your code works?

- Verified UUID URL returns 302: `curl -sS -w '%{http_code}'
"https://buildkite.com/bun/bun/builds/019bee3e-da45-4e9f-b4d8-4bdb5aeac0ac"`
→ `302`
- Verified numeric URL returns 200 with JSON: `curl -sS -H "Accept:
application/json" "https://buildkite.com/bun/bun/builds/35708"` → valid
JSON with jobs array

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 23:54:33 -08:00
github-actions[bot]
bcaae48a95 deps: update lolhtml to v2.7.1 (#26430)
## What does this PR do?

Updates lolhtml to version v2.7.1

Compare:
d64457d9ff...e9e16dca48

Auto-updated by [this
workflow](https://github.com/oven-sh/bun/actions/workflows/update-lolhtml.yml)

Co-authored-by: Jarred-Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2026-01-24 23:54:09 -08:00
robobun
6b3403a2b4 ci: fix update workflows creating duplicate PRs (#26433)
## Summary
- Fixed all `update-*.yml` workflows that were creating duplicate PRs
every week

## Problem
The update workflows (libarchive, zstd, cares, etc.) were using `${{
github.run_number }}` in the branch name, e.g.:
```yaml
branch: deps/update-libarchive-${{ github.run_number }}
```

This caused a new unique branch to be created on every workflow run, so
the `peter-evans/create-pull-request` action couldn't detect existing
PRs and would create duplicates.

**Evidence:** There are currently 8+ open duplicate PRs for libarchive
alone:
- #26432 deps: update libarchive to v3.8.5 (deps/update-libarchive-56)
- #26209 deps: update libarchive to v3.8.5 (deps/update-libarchive-55)
- #25955 deps: update libarchive to v3.8.5 (deps/update-libarchive-54)
- etc.

## Solution
Changed all workflows to use static branch names, e.g.:
```yaml
branch: deps/update-libarchive
```

This allows the action to:
1. Detect if an existing branch/PR already exists
2. Update the existing PR with new changes instead of creating a new one
3. Properly use `delete-branch: true` when the PR is merged

## Files Changed
- `.github/workflows/update-cares.yml`
- `.github/workflows/update-hdrhistogram.yml`
- `.github/workflows/update-highway.yml`
- `.github/workflows/update-libarchive.yml`
- `.github/workflows/update-libdeflate.yml`
- `.github/workflows/update-lolhtml.yml`
- `.github/workflows/update-lshpack.yml`
- `.github/workflows/update-root-certs.yml`
- `.github/workflows/update-sqlite3.yml`
- `.github/workflows/update-vendor.yml`
- `.github/workflows/update-zstd.yml`

## Test plan
- [x] Verified the change is syntactically correct
- [ ] Wait for next scheduled run of any workflow to verify it updates
existing PR instead of creating a new one

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 23:53:50 -08:00
robobun
70fe76209b fix(readline): use symbol key for _refreshLine in tab completer (#26412) 2026-01-24 15:37:29 -08:00
Jarred Sumner
ab3df344b8 Delete slop test 2026-01-23 23:22:32 -08:00
robobun
4680e89a91 fix(bundler): add missing semicolons in minified bun module imports (#26372)
## Summary
- Fix missing semicolons in minified output when using both default and
named imports from `"bun"` module
- The issue occurred in `printInternalBunImport` when transitioning
between star_name, default_name, and items sections without flushing
pending semicolons

## Test plan
- Added regression tests in `test/regression/issue/26371.test.ts`
covering:
  - Default + named imports (`import bun, { embeddedFiles } from "bun"`)
- Namespace + named imports (`import * as bun from "bun"; import {
embeddedFiles } from "bun"`)
  - Namespace + default + named imports combination
- Verified test fails with `USE_SYSTEM_BUN=1` (reproduces bug)
- Verified test passes with `bun bd test` (fix works)

Fixes #26371

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 23:09:01 -08:00
robobun
f88f60af5a fix(bundler): throw error when Bun.build is called from macro during bundling (#26361)
## Summary
- Fixes #26360
- Detects when `Bun.build` is called from within macro mode during
bundling and throws a clear error instead of hanging indefinitely

## Problem
When `Bun.build` API is called to bundle a file that imports from a
macro which itself uses `Bun.build`, the process would hang indefinitely
due to a deadlock:

1. The bundler uses a singleton thread for processing `Bun.build` calls
2. During parsing, when a macro is encountered, it's evaluated on that
thread
3. If the macro calls `Bun.build`, it tries to enqueue to the same
singleton thread
4. The singleton is blocked waiting for macro completion → deadlock

## Solution
Added a check in `Bun.build` that detects when it's called from macro
mode (`vm.macro_mode`) and throws a clear error with guidance:

```
Bun.build cannot be called from within a macro during bundling.

This would cause a deadlock because the bundler is waiting for the macro to complete,
but the macro's Bun.build call is waiting for the bundler.

To bundle code at compile time in a macro, use Bun.spawnSync to invoke the CLI:
  const result = Bun.spawnSync(["bun", "build", entrypoint, "--format=esm"]);
```

## Test plan
- [x] Added regression test in `test/regression/issue/26360.test.ts`
- [x] Verified test hangs/fails with system Bun (the bug exists)
- [x] Verified test passes with the fix applied
- [x] Verified regular `Bun.build` (not in macro context) still works

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2026-01-23 20:24:12 -08:00
robobun
232e0df956 fix(http): respect port numbers in NO_PROXY environment variable (#26347)
## Summary
- Fix NO_PROXY environment variable to properly respect port numbers
like Node.js and curl do
- Previously `NO_PROXY=localhost:1234` would bypass proxy for all
requests to localhost regardless of port
- Now entries with ports (e.g., `localhost:8080`) do exact host:port
matching, while entries without ports continue to use suffix matching

## Test plan
- Added tests in `test/js/bun/http/proxy.test.js` covering:
  - [x] Bypass proxy when NO_PROXY matches host:port exactly
  - [x] Use proxy when NO_PROXY has different port  
- [x] Bypass proxy when NO_PROXY has host only (no port) - existing
behavior preserved
  - [x] Handle NO_PROXY with multiple entries including port
- Verified existing proxy tests still pass

🤖 Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2026-01-23 20:21:57 -08:00
robobun
9f0e78fc42 fix(serve): add missing return after handling invalid stream in response body (#26396)
## Summary
- Fix missing return after handling `.Invalid` stream case in response
body rendering
- Add regression test for Bun.serve() concurrent instances (#26394)

## Details

When a response body contains a locked value with an invalid readable
stream (`stream.ptr == .Invalid`), the code would:
1. Call `this.response_body_readable_stream_ref.deinit()` 
2. Fall through without returning

This missing `return` caused the code to fall through to subsequent
logic that could set up invalid callbacks on an already-used body value,
potentially causing undefined behavior.

The fix adds `this.doRenderBlob()` to render an empty response body for
the invalid stream case, then returns properly.

## Test plan
- [x] Added `test/regression/issue/26394.test.ts` with tests for
concurrent Bun.serve instances
- [x] Verified test passes with `bun bd test
test/regression/issue/26394.test.ts`
- [x] Verified test passes with system Bun (`USE_SYSTEM_BUN=1 bun test`)


🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:27:36 -08:00
robobun
043fafeefa fix(http): set #js_ref in Request.toJS for server-created requests (#26390)
## Summary

- Fix Request.text() failure with "TypeError: undefined is not a
function" after many requests on certain platforms
- Set `#js_ref` in `Request.toJS()` for server-created requests,
matching the existing pattern in `Response.toJS()`

## Root Cause

When Request objects are created by the server (via `Request.init()`),
the `#js_ref` field was never initialized. This caused
`checkBodyStreamRef()` to fail silently when called in `toJS()` because
`#js_ref.tryGet()` returned null.

The bug manifested on macOS after ~4,500 requests when GC conditions
were triggered, causing the weak reference lookup to fail and resulting
in "TypeError: undefined is not a function" when calling `req.text()`.

## Fix

The fix mirrors the existing pattern in `Response.toJS()`:
1. Create the JS value first via `js.toJSUnchecked()`
2. Set `#js_ref` with the JS wrapper reference
3. Then call `checkBodyStreamRef()` which can now properly access the JS
value

## Test plan

- [x] Added regression test that exercises Request.text() with 6000
requests and periodic GC
- [x] Existing request tests pass
- [x] HTTP server tests pass

Fixes #26387

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:09:03 -08:00
Alistair Smith
ce173b1112 Revert 0c3b5e501b 2026-01-23 11:02:26 -08:00
Alistair Smith
0c3b5e501b Merge branch 'main' of github.com:oven-sh/bun into ali/esm-bytecode 2026-01-23 10:48:40 -08:00
Alistair Smith
5dc72bc1d8 use esm module info in source provider 2026-01-23 10:48:34 -08:00
Alistair Smith
dfc36a8255 start laying groundwork 2026-01-23 10:43:22 -08:00
robobun
827c7091d9 fix(pack): re-read package.json after prepack/prepare scripts (#26267)
## Summary

- Fixes `bun pm pack` not respecting changes to `package.json` made by
prepack/prepare scripts
- Tracks whether lifecycle scripts (prepublishOnly, prepack, prepare)
ran
- If scripts ran, invalidates the cached package.json and re-reads from
disk before creating the tarball

Closes #24314

## Test plan

- Added regression test `test/regression/issue/24314.test.ts` that
verifies package.json modifications from prepack/prepare scripts are
included in the tarball
- All existing pack tests pass (69 tests)


🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 00:44:12 -08:00
robobun
13f78a7044 fix(streams): return null from controller.desiredSize when stream is detached (#26378)
## Summary

- Fixed `controller.desiredSize` throwing `TypeError: null is not an
object` when the stream's internal `controlledReadableStream` has been
set to `null` during cleanup
- Added null check in `readableStreamDefaultControllerGetDesiredSize` to
return `null` when the stream reference is null, matching WHATWG Streams
spec behavior for detached/errored streams

## Root Cause

When piping streams (e.g., `fetch` with `ReadableStream` body), cleanup
code in `assignStreamIntoResumableSink` and `readStreamIntoSink` sets
`controlledReadableStream` to `null` for GC purposes. If the user's
`pull()` function is still running asynchronously when this happens,
accessing `controller.desiredSize` throws instead of returning `null`.

## Test plan

- [x] Added regression test `test/regression/issue/26377.test.ts`
- [x] Verified tests pass with `bun bd test
test/regression/issue/26377.test.ts`

Fixes #26377

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 00:26:52 -08:00
Dylan Conway
d8d8182e8e fix(compile): apply BUN_OPTIONS env var to standalone executables (#26346)
## Summary
- Fixed `BUN_OPTIONS` environment variable not being applied as runtime
options for standalone executables (`bun build --compile`). Previously,
args from `BUN_OPTIONS` were incorrectly passed through to
`process.argv` instead of being parsed as Bun runtime options
(`process.execArgv`).
- Removed `BUN_CPU_PROFILE`, `BUN_CPU_PROFILE_DIR`, and
`BUN_CPU_PROFILE_NAME` env vars since `BUN_OPTIONS="--cpu-prof
--cpu-prof-dir=... --cpu-prof-name=..."` now works correctly with
standalone executables.
- Made `cpu_prof.name` and `cpu_prof.dir` non-optional with empty string
defaults.

fixes #21496

## Test plan
- [x] Added tests for `BUN_OPTIONS` with standalone executables (no
`compile-exec-argv`)
- [x] Added tests for `BUN_OPTIONS` combined with `--compile-exec-argv`
- [x] Added tests for `BUN_OPTIONS` with user passthrough args
- [x] Verified existing `compile-argv` tests still pass
- [x] Verified existing `bun-options` tests still pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
2026-01-23 00:24:18 -08:00
Jarred Sumner
e8b2455f11 feat: add Bun.JSONL.parse() for streaming newline-delimited JSON parsing (#26356)
Adds a built-in JSONL parser implemented in C++ using JavaScriptCore's
optimized JSON parser.

## API

### `Bun.JSONL.parse(input)`
Parse a complete JSONL string or `Uint8Array` and return an array of all
parsed values. Throws on invalid input.

```ts
const results = Bun.JSONL.parse('{"a":1}\n{"b":2}\n');
// [{ a: 1 }, { b: 2 }]
```

### `Bun.JSONL.parseChunk(input, start?, end?)`
Parse as many complete values as possible, returning `{ values, read,
done, error }`. Designed for streaming use cases where input arrives
incrementally.

```ts
const result = Bun.JSONL.parseChunk('{"id":1}\n{"id":2}\n{"id":3');
result.values; // [{ id: 1 }, { id: 2 }]
result.read;   // 17
result.done;   // false
result.error;  // null
```

## Implementation Details

- C++ implementation in `BunObject.cpp` using JSC's `streamingJSONParse`
- ASCII fast path: zero-copy `StringView` for pure ASCII input
- Non-ASCII: uses `fromUTF8ReplacingInvalidSequences` with
`utf16_length_from_utf8` size check to prevent overflow
- UTF-8 BOM automatically skipped for `Uint8Array` input
- Pre-built `Structure` with fixed property offsets for fast result
object creation
- `Symbol.toStringTag = "JSONL"` on the namespace object
- `parseChunk` returns errors in `error` property instead of throwing,
preserving partial results
- Comprehensive boundary checks on start/end parameters

## Tests

234 tests covering:
- Complete and partial/streaming input scenarios
- Error handling and recovery
- UTF-8 multi-byte characters and BOM handling
- start/end boundary security (exhaustive combinations, clamping, OOB
prevention)
- 4 GB input rejection (both ASCII and non-ASCII paths)
- Edge cases (empty input, single values, whitespace, special numbers)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-23 00:23:25 -08:00
robobun
c4f6874960 fix(ws): allow ws.once() to work multiple times (#26359)
## Summary
- Fixes `ws.once()` only working on the first call for each event type
- The bug was in the `#onOrOnce` method which tracked native listeners
via a bitset but didn't account for `once` listeners auto-removing after
firing
- Now only persistent `on()` listeners set the bitset; `once()`
listeners always add new native handlers unless a persistent listener
already exists

## Test plan
- [x] Added regression test `test/regression/issue/26358.test.ts`
- [x] Test verifies `once('message')` works multiple times
- [x] Test verifies `once('pong')` works multiple times  
- [x] Test verifies `on()` still works correctly
- [x] Test verifies mixing `on()` and `once()` works correctly
- [x] Verified test fails with `USE_SYSTEM_BUN=1` (bug exists)
- [x] Verified test passes with `bun bd test` (fix works)

Fixes #26358

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2026-01-23 00:22:36 -08:00
Dylan Conway
c63415c9c9 Mimalloc v3 update (#26379)
### What does this PR do?

### How did you verify your code works?

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2026-01-23 00:10:48 -08:00
Jarred Sumner
86d4d87beb feat(unicode): migrate grapheme breaking to uucode with GB9c support (#26376)
## Summary

Replace Bun's outdated grapheme breaking implementation with [Ghostty's
approach](https://github.com/ghostty-org/ghostty/tree/main/src/unicode)
using the [uucode](https://github.com/jacobsandlund/uucode) library.
This adds proper **GB9c (Indic Conjunct Break)** support — Devanagari
and other Indic script conjuncts now correctly form single grapheme
clusters.

## Motivation

The previous implementation used a `GraphemeBoundaryClass` enum with
only 12 values and a 2-bit `BreakState` (just `extended_pictographic`
and `regional_indicator` flags). It had no support for Unicode's GB9c
rule, meaning Indic conjunct sequences (consonant + virama + consonant)
were incorrectly split into multiple grapheme clusters.

## Architecture

### Runtime (zero uucode dependency, two table lookups)

```
codepoint → [3-level LUT] → GraphemeBreakNoControl enum (u5, 17 values)
(state, gb1, gb2) → [8KB precomputed array] → (break_result, new_state)
```

The full grapheme break algorithm (GB6-GB13, GB9c, GB11, GB999) runs
only at **comptime** to populate the 8KB decision array. At runtime it's
pure table lookups.

### File Layout

```
src/deps/uucode/              ← Vendored library (MIT, build-time only)
src/unicode/uucode/           ← Build-time integration
  ├── uucode_config.zig       ← What Unicode properties to generate
  ├── grapheme_gen.zig        ← Generator: queries uucode → writes tables
  ├── lut.zig                 ← 3-level lookup table generator
  └── CLAUDE.md               ← Maintenance docs
src/string/immutable/         ← Runtime (no uucode dependency)
  ├── grapheme.zig            ← Grapheme break API + comptime decisions
  ├── grapheme_tables.zig     ← Pre-generated tables (committed, ~91KB source)
  └── visible.zig             ← Width calculation (2 lines changed)
scripts/update-uucode.sh      ← Update vendored uucode + regenerate
```

### Key Types

| Type | Size | Values |
|------|------|--------|
| `GraphemeBreakNoControl` | u5 | 17 (adds
`indic_conjunct_break_{consonant,linker,extend}`, `emoji_modifier_base`,
`zwnj`, etc.) |
| `BreakState` | u3 | 5 (`default`, `regional_indicator`,
`extended_pictographic`, `indic_conjunct_break_consonant`,
`indic_conjunct_break_linker`) |

### Binary Size

The tables store only the `GraphemeBreakNoControl` enum per codepoint
(not width or emoji properties, which visible.zig handles separately):

- stage1: 8192 × u16 = **16KB** (maps high byte → stage2 offset)
- stage2: 27392 × u8 = **27KB** (maps to stage3 index; max value is 16)
- stage3: 17 × u5 = **~17 bytes** (one per enum value)
- Precomputed decisions: **8KB**
- **Total: ~51KB** (vs previous ~70KB+)

## How to Regenerate Tables

```bash
# After updating src/deps/uucode/:
./scripts/update-uucode.sh

# Or manually:
vendor/zig/zig build generate-grapheme-tables
```

Normal builds never run the generator — they use the committed
`grapheme_tables.zig`.

## Testing

```bash
bun bd test test/js/bun/util/stringWidth.test.ts
```

New test cases verify Devanagari conjuncts (GB9c):
- `क्ष` (Ka+Virama+Ssa) → single cluster, width 2
- `क्‍ष` (Ka+Virama+ZWJ+Ssa) → single cluster, width 2
- `क्क्क` (Ka+Virama+Ka+Virama+Ka) → single cluster, width 3

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-23 00:07:06 -08:00
robobun
3b1c3bfe97 feat(cli): add --heap-prof and --heap-prof-text flags for heap profiling (#26326)
## Summary
- Add `--heap-prof` CLI flag for generating V8-compatible heap snapshots
(`.heapsnapshot`)
- Add `--heap-prof-md` CLI flag for generating markdown heap profiles
(`.md`) designed for CLI analysis
- Add `--heap-prof-name` and `--heap-prof-dir` options for customizing
output location

## Usage

```bash
# Generate V8-compatible heap snapshot (opens in Chrome DevTools)
bun --heap-prof script.js

# Generate markdown heap profile (for CLI analysis with grep/sed/awk)
bun --heap-prof-md script.js

# Specify output location
bun --heap-prof --heap-prof-dir ./profiles --heap-prof-name my-snapshot.heapsnapshot script.js
```

## Example Output (`--heap-prof-md`)

<details>
<summary>Click to expand example markdown profile</summary>

```markdown
# Bun Heap Profile

Generated by `bun --heap-prof-md`. This profile contains complete heap data in markdown format.

**Quick Search Commands:**
```bash
grep 'type=Function' file.md          # Find all Function objects
grep 'size=[0-9]\{5,\}' file.md       # Find objects >= 10KB
grep 'EDGE.*to=12345' file.md         # Find references to object #12345
grep 'gcroot=1' file.md               # Find all GC roots
```

---

## Summary

| Metric | Value |
|--------|------:|
| Total Heap Size | 208.2 KB (213265 bytes) |
| Total Objects | 2651 |
| Total Edges | 7337 |
| Unique Types | 73 |
| GC Roots | 426 |

## Top 50 Types by Retained Size

| Rank | Type | Count | Self Size | Retained Size | Largest Instance |
|-----:|------|------:|----------:|--------------:|-----------------:|
| 1 | `Function` | 568 | 18.7 KB | 5.4 MB | 10.4 KB |
| 2 | `Structure` | 247 | 27.0 KB | 2.0 MB | 10.4 KB |
| 3 | `FunctionExecutable` | 306 | 38.2 KB | 375.5 KB | 13.0 KB |
| 4 | `FunctionCodeBlock` | 25 | 21.5 KB | 294.1 KB | 14.0 KB |
| 5 | `string` | 591 | 11.3 KB | 75.9 KB | 177 B |
...

## Top 50 Largest Objects

Objects that retain the most memory (potential memory leak sources):

| Rank | ID | Type | Self Size | Retained Size | Out-Edges | In-Edges |
|-----:|---:|------|----------:|--------------:|----------:|---------:|
| 1 | 0 | `<root>` | 0 B | 58.1 KB | 852 | 0 |
| 2 | 774 | `GlobalObject` | 10.0 KB | 41.9 KB | 717 | 807 |
| 3 | 600 | `ModuleProgramCodeBlock` | 1.2 KB | 23.9 KB | 30 | 1 |
...

## Retainer Chains

How the top 20 largest objects are kept alive (path from GC root to object):

### 1. Object #0 - `<root>` (58.1 KB retained)

```
(no path to GC root found)
```

### 2. Object #774 - `GlobalObject` (41.9 KB retained)

```
GlobalObject#774 [ROOT] (this object is a GC root)
```
...

## GC Roots

| ID | Type | Size | Retained | Label |
|---:|------|-----:|---------:|-------|
| 0 | `<root>` | 0 B | 58.1 KB | |
| 774 | `GlobalObject` | 10.0 KB | 41.9 KB | |
...

<details>
<summary>Click to expand 2651 objects (searchable with grep)</summary>

| ID | Type | Size | Retained | Flags | Label |
|---:|------|-----:|---------:|-------|-------|
| 0 | `<root>` | 0 | 59467 | gcroot=1  |  |
| 1 | `Structure` | 112 | 10644 |  |  |
...

</details>
```

</details>

## Test plan
- [x] `bun bd test test/cli/heap-prof.test.ts` - All 7 tests pass
- [x] `USE_SYSTEM_BUN=1 bun test test/cli/heap-prof.test.ts` - Tests
fail (feature not in system bun)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-22 15:27:37 -08:00
Ciro Spaciari
2582e6f98e fix(http2): fix settings, window size handling, and dynamic header buffer allocation (#26119)
## Summary

This PR fixes multiple HTTP/2 protocol compliance issues that were
causing stream errors with various HTTP/2 clients (Fauna, gRPC/Connect,
etc.).
fixes https://github.com/oven-sh/bun/issues/12544
fixes https://github.com/oven-sh/bun/issues/25589
### Key Fixes

**Window Size and Settings Handling**
- Fix initial stream window size to use `DEFAULT_WINDOW_SIZE` until
`SETTINGS_ACK` is received
- Per RFC 7540 Section 6.5.1: The sender can only rely on settings being
applied AFTER receiving `SETTINGS_ACK`
- Properly adjust existing stream windows when `INITIAL_WINDOW_SIZE`
setting changes (RFC 7540 Section 6.9.2)

**Header List Size Enforcement**  
- Implement `maxHeaderListSize` checking per RFC 7540 Section 6.5.2
- Track cumulative header list size using HPACK entry overhead (32 bytes
per RFC 7541 Section 4.1)
- Reject streams with `ENHANCE_YOUR_CALM` when header list exceeds
configured limit

**Custom Settings Support**
- Add validation for `customSettings` option (up to 10 custom settings,
matching Node.js `MAX_ADDITIONAL_SETTINGS`)
- Validate setting IDs are in range `[0, 0xFFFF]` per RFC 7540
- Validate setting values are in range `[0, 2^32-1]`

**Settings Validation Improvements**
- Use float comparison for settings validation to handle large values
correctly (was using `toInt32()` which truncates)
- Use proper `HTTP2_INVALID_SETTING_VALUE_RangeError` error codes for
Node.js compatibility

**BufferFallbackAllocator** - New allocator that tries a provided buffer
first, falls back to heap:
- Similar to `std.heap.stackFallback` but accepts external buffer slice
- Used with `shared_request_buffer` (16KB threadlocal) for common cases
- Falls back to `bun.default_allocator` for large headers

## Test Plan

- [x] `bun bd` compiles successfully
- [x] Node.js HTTP/2 tests pass: `bun bd
test/js/node/test/parallel/test-http2-connect.js`
- [x] New regression tests for frame size issues: `bun bd test
test/regression/issue/25589.test.ts`
- [x] HTTP/2 continuation tests: `bun bd test
test/js/node/http2/node-http2-continuation.test.ts`

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2026-01-22 14:35:18 -08:00
mmitchellg5
85080f7949 fix: handle DT_UNKNOWN in dir_iterator for bind-mounted filesystems (#25838)
### What does this PR do?
Fixes #24007
Possibly fixes https://github.com/oven-sh/bun/issues/18902,
https://github.com/oven-sh/bun/issues/7412

Some filesystems (bind mounts, FUSE, NFS) don't provide `d_type` in
directory entries, returning `DT_UNKNOWN`. This caused glob and
recursive readdir to skip entries entirely.

## Problem
On Linux filesystems that don't populate `d_type` in directory entries
(bind mounts, FUSE, NFS, some ext4 configurations), `readdir()` returns
`DT_UNKNOWN` instead of the actual file type. This caused:
- `Bun.Glob` to skip files/directories entirely
- `fs.readdirSync(..., {recursive: true})` to not recurse into
subdirectories
- `fs.readdirSync(..., {withFileTypes: true})` to report incorrect types

## Solution
Implemented a **lazy `lstatat()` fallback** when `d_type == DT_UNKNOWN`:

- **`sys.zig`**: Added `lstatat()` function - same as `fstatat()` but
with `AT_SYMLINK_NOFOLLOW` flag to correctly identify symlinks
- **`GlobWalker.zig`**: When encountering `.unknown` entries, first
check if filename matches pattern, then call `lstatat()` only if needed
- **`node_fs.zig`**: Handle `.unknown` in both async and sync recursive
readdir paths; propagate resolved kind to Dirent objects
- **`dir_iterator.zig`**: Return `.unknown` for `DT_UNKNOWN` entries,
letting callers handle lazy stat

**Why `lstatat` instead of `fstatat`?** We use `AT_SYMLINK_NOFOLLOW` to
preserve consistent behavior with normal filesystems - symlinks should
be reported as symlinks, not as their target type. This matches [Node.js
behavior](https://github.com/nodejs/node/blob/main/lib/internal/fs/utils.js#L251-L269)
which uses `lstat()` for the DT_UNKNOWN fallback, and follows the lazy
stat pattern established in PR #18172.

### How did you verify your code works?

**Testing:**
- Regression test: `test/regression/issue/24007.test.ts`
- FUSE filesystem test: `test/cli/run/glob-on-fuse.test.ts` (reuses
`fuse-fs.py` from PR #18172, includes symlink verification)
- All existing glob/readdir tests pass
- **Verified in Docker bind-mount environment:**
  - Official Bun: `0 files`
  - Patched Bun: `3 files`

**Performance:** No impact on normal filesystems - the `.unknown` branch
is only hit when `d_type == DT_UNKNOWN`. The lazy stat pattern avoids
unnecessary syscalls by checking pattern match first.

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-22 13:44:49 -08:00
robobun
2a9980076d feat(windows): Add Windows ARM64 support (#26215) 2026-01-22 04:22:45 -08:00
Dylan Conway
1da41b7f91 update WebKit (#26324)
### What does this PR do?
Updates WebKit to
87c6cde57d
### How did you verify your code works?

---------

Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2026-01-21 20:17:46 -08:00
robobun
136d345752 fix(install): show dependency name when file: path resolution fails (#26340)
## Summary
- When `bun install` encounters a stale lockfile with a `file:`
dependency path that differs from the package.json, it now shows which
dependency caused the issue instead of the misleading "Bun could not
find a package.json file to install from" error.

## Test plan
- Added regression test in `test/regression/issue/26337.test.ts`
- Verified test fails with system bun (`USE_SYSTEM_BUN=1 bun test
test/regression/issue/26337.test.ts`)
- Verified test passes with debug build (`bun bd test
test/regression/issue/26337.test.ts`)

Fixes #26337

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 18:41:15 -08:00
Alistair Smith
93d5cc6e56 fix: rename compile "Target" ➜ "CompileTarget" 2026-01-21 13:58:32 -08:00
Jarred Sumner
37c41137f8 feat(transpiler): add replMode option for REPL transforms (#26246)
## Summary

Add a new `replMode` option to `Bun.Transpiler` that transforms code for
interactive REPL evaluation. This enables building a Node.js-compatible
REPL using `Bun.Transpiler` with `vm.runInContext` for persistent
variable scope.

## Features

- **Expression result capture**: Wraps the last expression in `{
__proto__: null, value: expr }` for result capture
- **IIFE wrappers**: Uses sync/async IIFE wrappers to avoid extra
parentheses around object literals in output
- **Variable hoisting**: Hoists `var`/`let`/`const` declarations outside
the IIFE for persistence across REPL lines
- **const → let conversion**: Converts `const` to `let` for REPL
mutability (allows re-declaration)
- **Function hoisting**: Hoists function declarations with
`this.funcName = funcName` assignment for vm context persistence
- **Class hoisting**: Hoists class declarations with `var` for vm
context persistence
- **Object literal detection**: Auto-detects object literals (code
starting with `{` without trailing `;`) and wraps them in parentheses

## Usage

```typescript
import vm from "node:vm";

const transpiler = new Bun.Transpiler({
  loader: "tsx",
  replMode: true,
});

const context = vm.createContext({ console, Promise });

async function repl(code: string) {
  const transformed = transpiler.transformSync(code);
  const result = await vm.runInContext(transformed, context);
  return result.value;
}

// Example REPL session
await repl("var x = 10");        // 10
await repl("x + 5");             // 15
await repl("class Counter {}"); // [class Counter]
await repl("new Counter()");    // Counter {}
await repl("{a: 1, b: 2}");     // {a: 1, b: 2} (auto-detected object literal)
await repl("await Promise.resolve(42)"); // 42
```

## Transform Examples

| Input | Output |
|-------|--------|
| `42` | `(() => { return { __proto__: null, value: 42 }; })()` |
| `var x = 10` | `var x; (() => { return { __proto__: null, value: x =
10 }; })()` |
| `await fetch()` | `(async () => { return { __proto__: null, value:
await fetch() }; })()` |
| `{a: 1}` | `(() => { return { __proto__: null, value: ({a: 1}) };
})()` |
| `class Foo {}` | `var Foo; (() => { return { __proto__: null, value:
Foo = class Foo {} }; })()` |

## Files Changed

- `src/ast/repl_transforms.zig`: New module containing REPL transform
logic
- `src/ast/P.zig`: Calls REPL transforms after parsing in REPL mode
- `src/bun.js/api/JSTranspiler.zig`: Adds `replMode` config option and
object literal detection
- `src/options.zig`, `src/runtime.zig`, `src/transpiler.zig`: Propagate
`repl_mode` flag
- `packages/bun-types/bun.d.ts`: TypeScript type definitions
- `test/js/bun/transpiler/repl-transform.test.ts`: Test cases

## Testing

```bash
bun bd test test/js/bun/transpiler/repl-transform.test.ts
```

34 tests covering:
- Basic transform output
- REPL session with node:vm
- Variable persistence across lines
- Object literal detection
- Edge cases (empty input, comments, TypeScript, etc.)
- No-transform cases (await inside async functions)

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-21 13:39:25 -08:00
Dylan Conway
dc203e853a fix patch-bounds-check.test.ts (#26344)
### What does this PR do?

### How did you verify your code works?
2026-01-21 13:22:21 -08:00
robobun
2febdb5b49 feat(cli): add --cpu-prof-md flag for markdown CPU profile output (#26327)
## Summary
- Adds `--cpu-prof-md` flag that outputs CPU profiling data in markdown
format optimized for GitHub rendering and LLM analysis
- Complements the existing `--cpu-prof` flag which outputs Chrome
DevTools JSON format
- `--cpu-prof-md` works standalone or combined with `--cpu-prof` to
generate both formats

## Usage
```bash
# Markdown only
bun --cpu-prof-md script.js

# Both formats
bun --cpu-prof --cpu-prof-md script.js
```

## Example Output

# CPU Profile

| Duration | Samples | Interval | Functions |
|----------|---------|----------|----------|
| 255.7ms | 178 | 1ms | 32 |

**Top 10:** \`fibonacci\` 23.6%, \`fibonacci\` 12.6%, \`parseModule\`
11.7%, \`(anonymous)\` 9.5%, \`loadAndEvaluateModule\` 5.5%,
\`requestSatisfyUtil\` 3.7%, \`main\` 2.7%,
\`moduleDeclarationInstantiation\` 2.6%, \`loadModule\` 2.5%,
\`cacheSatisfyAndReturn\` 2.5%

## Hot Functions (Self Time)

| Self% | Self | Total% | Total | Function | Location |
|------:|-----:|-------:|------:|----------|----------|
| 23.6% | 60.5ms | 23.6% | 60.5ms | \`fibonacci\` | /tmp/test-profile.js
|
| 12.6% | 32.3ms | 100.0% | 1.29s | \`fibonacci\` |
/tmp/test-profile.js:3 |
| 11.7% | 29.9ms | 11.7% | 29.9ms | \`parseModule\` | [native code] |
| 9.5% | 24.3ms | 43.4% | 111.0ms | \`(anonymous)\` | [native code] |
| 5.5% | 14.2ms | 99.9% | 255.5ms | \`loadAndEvaluateModule\` | [native
code] |

## Call Tree (Total Time)

| Total% | Total | Self% | Self | Function | Location |
|-------:|------:|------:|-----:|----------|----------|
| 100.0% | 1.29s | 12.6% | 32.3ms | \`fibonacci\` |
/tmp/test-profile.js:3 |
| 99.9% | 255.5ms | 5.5% | 14.2ms | \`loadAndEvaluateModule\` | [native
code] |
| 86.0% | 219.9ms | 1.3% | 3.3ms | \`moduleEvaluation\` | [native code]
|
| 43.4% | 111.0ms | 9.5% | 24.3ms | \`(anonymous)\` | [native code] |

## Function Details

### \`fibonacci\`

- **Location:** \`/tmp/test-profile.js:3\`
- **Self:** 12.6% (32.3ms) | **Total:** 100.0% (1.29s)
- **Called by:** \`fibonacci\` (864), \`main\` (68)
- **Calls:** \`fibonacci\` (864), \`fibonacci\` (44), \`fibonacci\` (2)

### \`main\`

- **Location:** \`/tmp/test-profile.js:9\`
- **Self:** 0.0% (0us) | **Total:** 38.4% (98.2ms)
- **Called by:** \`(module)\` (72)
- **Calls:** \`fibonacci\` (68), \`inspect\` (2), \`fibonacci\` (2)

## Files

| Self% | Self | File |
|------:|-----:|------|
| 58.8% | 150.6ms | \`[native code]\` |
| 40.1% | 102.6ms | \`/tmp/test-profile.js\` |
| 0.9% | 2.4ms | \`bun:main\` |

## Test plan
- [x] `--cpu-prof-md` generates `.md` file with markdown tables
- [x] `--cpu-prof-md` works standalone without `--cpu-prof`
- [x] Both flags together generate both `.cpuprofile` and `.md` files
- [x] Custom filename with `--cpu-prof-name` works
- [x] Custom directory with `--cpu-prof-dir` works
- [x] All 9 tests pass

🤖 Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2026-01-21 13:21:01 -08:00
Jarred Sumner
b6b3626c14 fix(bindings): handle errors from String.toJS() for oversized strings (#26213)
## Summary

- When a string exceeds `WTF::String::MaxLength` (~4GB),
`bun.String.createUninitialized()` returns a `.Dead` tag
- The C++ layer now properly throws `ERR_STRING_TOO_LONG` when this
happens
- Updated `String.toJS()` in Zig to return `bun.JSError!jsc.JSValue`
instead of just `jsc.JSValue`
- Updated ~40 Zig caller files to handle the error with `try`
- C++ callers updated with `RETURN_IF_EXCEPTION` checks

## Test plan

- [x] `bun bd test test/js/node/buffer.test.js` - 449 tests pass
- [x] `bun bd
test/js/node/test/parallel/test-buffer-tostring-rangeerror.js` - passes

🤖 Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
2026-01-21 13:01:25 -08:00
Dylan Conway
7f70b01259 feat: support CPU profiling via environment variables (#26313)
Adds environment variable support for enabling CPU profiling without CLI
flags:

- `BUN_CPU_PROFILE=1` — enables the CPU profiler
- `BUN_CPU_PROFILE_DIR=<path>` — sets the output directory for the
profile
- `BUN_CPU_PROFILE_NAME=<name>` — sets the profile file name

These are used as fallbacks when the corresponding `--cpu-prof` CLI
options are not provided. This is useful for profiling in contexts where
modifying the command line isn't practical (e.g. scripts invoked by
other tools).
2026-01-20 22:43:36 -08:00
robobun
08103aa2ff fix(compile): ensure bytecode alignment accounts for section header (#26299)
## Summary

Fixes bytecode alignment in standalone executables to prevent crashes
when loading bytecode cache on Windows.

The bytecode offset needs to be aligned such that when loaded at
runtime, the bytecode pointer is 128-byte aligned. Previously, alignment
was based on arbitrary memory addresses during compilation, which didn't
account for the 8-byte section header prepended at runtime. This caused
the bytecode to be misaligned, leading to segfaults in
`JSC::CachedJSValue::decode` on Windows.

## Root Cause

At runtime, embedded data starts 8 bytes after the PE/Mach-O section
virtual address (which is page-aligned, hence 128-byte aligned). For
bytecode at offset `O` to be aligned:
```
(section_va + 8 + O) % 128 == 0
=> (8 + O) % 128 == 0
=> O % 128 == 120
```

The previous code used `std.mem.alignInSlice()` which found aligned
addresses based on the compilation buffer's arbitrary address, not
accounting for the 8-byte header offset at load time.

## Changes

- **`src/StandaloneModuleGraph.zig`**: Calculate bytecode offset to
satisfy `offset % 128 == 120` instead of using `alignInSlice`
- **`test/regression/issue/26298.test.ts`**: Added regression tests for
bytecode cache in standalone executables

## Test plan

- [x] Added regression test `test/regression/issue/26298.test.ts` with 3
test cases
- [x] Existing `HelloWorldBytecode` test passes
- [x] Build succeeds

Fixes #26298

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 22:42:38 -08:00
Jarred Sumner
bb4d150aed Try using internal string-width in node:readline (#26306)
### What does this PR do?

Remove NFKDC normalization and stripVTControlCharacters since
Bun.stringWidth does this now

### How did you verify your code works?

ci

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-20 22:41:14 -08:00
Dylan Conway
45af3335e6 Missing changes from #26080 (#26308)
### What does this PR do?

### How did you verify your code works?
2026-01-20 14:51:36 -08:00
robobun
dbad2857ea fix(test): delete setTimeout.clock property when disabling fake timers (#26285)
## Summary

- Fixes the `setTimeout.clock` property not being properly deleted after
`jest.useRealTimers()` is called
- Previously, the property was set to `false` instead of deleted,
causing `hasOwnProperty` checks to return `true`
- This broke React Testing Library and other libraries that check for
fake timers using `Object.prototype.hasOwnProperty.call(setTimeout,
'clock')`

## Changes

- Added `JSValue.deleteProperty()` binding in Zig to call JSC's
`deleteProperty()` method
- Updated `setFakeTimerMarker()` in `FakeTimers.zig` to delete the
`clock` property when disabling fake timers
- Updated existing test in `test/regression/issue/25869.test.ts` to
verify correct behavior
- Added new regression test in `test/regression/issue/26284.test.ts`

## Test plan

- [x] Verified new test fails with system bun (before fix)
- [x] Verified new test passes with debug build (after fix)
- [x] Verified existing fake timer tests still pass
- [x] Verified test for issue #25869 passes with fix

Fixes #26284

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 12:51:54 -08:00
robobun
6140eb5faf fix(windows): chunk large buffers in preadv/pwritev to avoid integer overflow (#26303)
## Summary
- Fix panic "integer does not fit in destination type" when
reading/writing large files on Windows
- Add chunking for iovec arrays that exceed `c_uint` max entries
- Add chunking for individual buffers that exceed 4GB (`u32` max)

The libuv functions `uv_fs_read` and `uv_fs_write` have two size
limitations:
1. `nbufs` parameter is `c_uint` (32-bit), limiting the number of iovec
entries
2. `uv_buf_t.len` is `ULONG` (u32 on Windows), limiting individual
buffer sizes to 4GB

This change processes large operations in chunks, accumulating results
and updating file positions between chunks.

## Test plan
- [x] Verified `bun run zig:check-all` passes on all platforms
- [x] Verified `bun bd` builds successfully
- [x] Verified basic file read/write operations work correctly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-20 12:47:07 -08:00
Alistair Smith
497a4d4818 Fix duplicate exports when two entrypoints share symbols (#26089)
### What does this PR do?

Fixes #5344
Fixes #6356

### How did you verify your code works?

Some test coverage

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
2026-01-20 12:40:33 -08:00
Dylan Conway
66d8397bd7 fix(compile): fix native module export corruption with multiple NAPI modules (#26080)
## Summary

Fixes native module export corruption when compiling multiple NAPI
modules with `bun build --compile` on Linux.

- When loading multiple `.node` files in a compiled binary, the second
module would incorrectly get the first module's exports
- Root cause: memfd file descriptors were closed after dlopen, allowing
fd reuse. Since dlopen caches by path (`/proc/self/fd/N`), it returned
the wrong cached handle
- This bug occurs when loading native modules in quick succession, as
the fd number is likely to be reused immediately after being closed
- Fix: Disable the memfd optimization and always use temp files with
unique paths

## Test plan

- [x] Added regression test in `test/regression/issue/26045/`
- [x] Test fails with production bun (v1.3.6)
- [x] Test passes with the fix

Fixes https://github.com/oven-sh/bun/issues/26045

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-01-20 12:40:12 -08:00
robobun
170f8f7962 fix(tls): use correct variable in setVerifyMode (#26255)
## Summary
- Fix typo in `setVerifyMode` where `reject_unauthorized` was
incorrectly reading from `request_cert_js` instead of
`reject_unauthorized_js`

## Test plan
- Existing TLS renegotiation tests pass
- Code inspection shows the fix is correct (simple variable name typo)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 22:47:18 -08:00
robobun
5f470278d1 fix(update): 'l' key now selects package in interactive update (#26265)
## Summary
- The 'l' key in `bun update --interactive` now correctly selects the
package when toggling between Target and Latest versions
- Previously, pressing 'l' would toggle `use_latest` but not mark the
package as selected, causing the underline indicator to disappear and
the package not being included when confirming

## Test plan
- [x] Added regression test `test/regression/issue/24131.test.ts` that
verifies 'l' selects the package
- [x] Test fails with system bun (before fix) and passes with debug
build (after fix)
- [x] `bun bd test test/regression/issue/24131.test.ts` passes

Fixes #24131

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 22:46:36 -08:00
robobun
3a4daa95ac fix(bundler): fix out-of-bounds access in DynamicBitSetUnmanaged.bytes() (#26283)
## Summary

Fixes an index out-of-bounds panic that occurs during bundler code
splitting on Windows.

- The `bytes()` function in `DynamicBitSetUnmanaged` was accessing
`masks[0..numMasks(bit_length) + 1]`, reading one element past the
allocated array
- When the bit set has exactly one mask (bit_length <= 64), this causes
a panic: "index out of bounds: index 1, len 1"
- The bug manifests when sorting chunk deduplication keys derived from
`AutoBitSet.bytes()`

The fix simply removes the erroneous `+ 1` from the slice bounds.

## Test plan

- [x] `bun bd test test/bundler/bundler_splitting.test.ts` - passes
- [x] `bun bd test test/bundler/esbuild/splitting.test.ts` - passes
- [x] `bun bd test test/bundler/bundler_regressions.test.ts` - passes
- [x] `bun bd test test/bundler/bundler_edgecase.test.ts` - passes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 22:45:56 -08:00
robobun
d3f8bec565 fix(Terminal): callbacks not invoked inside AsyncLocalStorage.run() (#26288)
## Summary
- Fixed `Bun.Terminal` callbacks (data, exit, drain) not being invoked
when the terminal is created inside `AsyncLocalStorage.run()`

## Root Cause
The bug was a redundant `isCallable()` check when storing callbacks in
`initTerminal()`:

1. In `Options.parseFromJS()`, callbacks are validated with
`isCallable()`, then wrapped with `withAsyncContextIfNeeded()`
2. Inside `AsyncLocalStorage.run()`, `withAsyncContextIfNeeded()`
returns an `AsyncContextFrame` object that wraps the callback + async
context
3. An `AsyncContextFrame` is NOT callable - it's a wrapper object. So
the second `isCallable()` check fails
4. Because the check fails, the callback is never stored via
`js.gc.set()`
5. When `onReadChunk()` tries to get the callback, it returns `null` and
the callback is never invoked

## Fix
Removed the redundant `isCallable()` check in `initTerminal()`. The
check was already performed in `parseFromJS()` before wrapping. Other
similar patterns (socket Handlers, Timer) simply store the wrapped
callback without re-checking.

Fixes #26286

## Test plan
- [x] Added regression test in `test/regression/issue/26286.test.ts`
- [x] Verified test fails with system Bun (times out because callback
never invoked)
- [x] Verified test passes with debug build

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 22:45:22 -08:00
robobun
62834e1bfe fix(init): resolve TypeScript errors in react-tailwind template build.ts (#26258)
## Summary
- Fixes TypeScript errors in the react-tailwind template's `build.ts`
when used with the template's strict `tsconfig.json`

## Test plan
- Added regression test `test/regression/issue/24364.test.ts` that
verifies TypeScript compilation passes
- Verified test fails with old template code and passes with fix

Closes #24364

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 17:12:42 -08:00
robobun
704252e85f fix(npm): show helpful error when postinstall script hasn't run (#26259)
## Summary
- Replaces empty placeholder executables with shell scripts that print
helpful error messages
- The scripts exit with code 1 instead of silently succeeding with code
0
- Helps users diagnose issues when installing with `--ignore-scripts` or
using pnpm

## Problem
When installing the `bun` npm package with `--ignore-scripts` or using
pnpm (which skips postinstall by default), the placeholder `bun.exe` and
`bunx.exe` files were empty, causing them to silently exit with code 0
and produce no output. This made it very difficult for users to
understand why bun wasn't working.

## Solution
The placeholder files are now shell scripts that:
1. Print a clear error message explaining the issue
2. Provide instructions on how to fix it (manually running postinstall
or reinstalling without `--ignore-scripts`)
3. Exit with code 1 to indicate failure

Example output when running the placeholder:
```
Error: Bun's postinstall script was not run.

This occurs when using --ignore-scripts during installation, or when using a
package manager like pnpm that does not run postinstall scripts by default.

To fix this, run the postinstall script manually:
  cd node_modules/bun && node install.js

Or reinstall bun without the --ignore-scripts flag.
```

## Test plan
- [x] Added regression test that verifies the placeholder script
behavior
- [x] Test passes with `bun bd test test/regression/issue/24329.test.ts`

Fixes #24329

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 17:11:38 -08:00