Commit Graph

14584 Commits

Author SHA1 Message Date
Dylan Conway
ecd6d8ea68 [build images] 2026-02-12 11:38:12 -08:00
Dylan Conway
58e5724a9b [build images] 2026-02-11 21:59:51 -08:00
Dylan Conway
4aafb2a29f [build images] 2026-02-11 21:56:09 -08:00
Dylan Conway
76c2b12821 [build images] 2026-02-11 19:45:06 -08:00
Dylan Conway
35c00e250d [build images] 2026-02-11 19:27:52 -08:00
Dylan Conway
899f08a58d update 2026-02-11 12:50:06 -08:00
Dylan Conway
34f4ecfcc8 Merge remote-tracking branch 'origin/main' into dylan/native-windows-aarch64-build 2026-02-11 12:46:58 -08:00
Dylan Conway
83d1c92cda optional deps 2026-02-11 12:46:41 -08:00
Dylan Conway
50e478dcdc fix(crypto): correct method and constructor names mangled by number renamer (#26876)
## Problem

The bundler's number renamer was mangling `.name` properties on crypto
class prototype methods and constructors:

- `hash.update.name` → `"update2"` instead of `"update"`
- `verify.verify.name` → `"verify2"` instead of `"verify"`
- `cipher.update.name` → `"update3"` instead of `"update"`
- `crypto.Hash.name` → `"Hash2"` instead of `"Hash"`

### Root causes

1. **Named function expressions on prototypes** collided with other
bindings after scope flattening (e.g. `Verify.prototype.verify =
function verify(...)` collided with the imported `verify`)
2. **Block-scoped constructor declarations** (`Hash`, `Hmac`) got
renamed when the bundler hoisted them out of block scope
3. **Shared function declarations** in the Cipher/Decipher block all got
numeric suffixes (`update3`, `final2`, `setAutoPadding2`, etc.)

## Fix

- Use `Object.assign` with object literals for prototype methods —
object literal property keys correctly infer `.name` and aren't subject
to the renamer
- Remove unnecessary block scopes around `Hash` and `Hmac` constructors
so they stay at module level and don't get renamed
- Inline `Cipheriv` methods and copy references to `Decipheriv`

## Tests

Added comprehensive `.name` tests for all crypto classes: Hash, Hmac,
Sign, Verify, Cipheriv, Decipheriv, DiffieHellman, ECDH, plus factory
functions and constructor names.
2026-02-10 23:06:22 -08:00
robobun
e8f73601c0 fix(compile): use remaining buffer in standalone POSIX write loop (#26882)
## What does this PR do?

Fixes the write loop in `StandaloneModuleGraph.inject()` for POSIX
targets (the `else` branch handling ELF/Linux standalone binaries) to
pass `remain` instead of `bytes` to `Syscall.write()`.

## Problem

The write loop that appends the bundled module graph to the end of the
executable uses a standard partial-write retry pattern, but passes the
full `bytes` buffer on every iteration instead of the remaining portion:

```zig
var remain = bytes;
while (remain.len > 0) {
    switch (Syscall.write(cloned_executable_fd, bytes)) {   // bug: should be 'remain'
        .result => |written| remain = remain[written..],
        ...
    }
}
```

If a partial write occurs, the next iteration re-writes from the start
of the buffer instead of continuing where it left off, corrupting the
output binary. The analogous read loop elsewhere in the same file
already correctly uses `remain`.

## Fix

One-character change: `bytes` → `remain` in the `Syscall.write()` call.

## How did you verify your code works?

- `bun bd` compiles successfully
- `bun bd test test/bundler/bun-build-compile.test.ts` — 4/4 pass

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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alistair Smith <alistair@anthropic.com>
2026-02-10 23:04:46 -08:00
robobun
ba6e84fecd fix(compile): seek to start of file before EXDEV cross-device copy (#26883)
## What does this PR do?

Fixes `bun build --compile` producing an all-zeros binary when the
output directory is on a different filesystem than the temp directory.
This is common in Docker containers, Gitea runners, and other
environments using overlayfs.

## Problem

When `inject()` finishes writing the modified executable to the temp
file, the file descriptor's offset is at EOF. If the subsequent
`renameat()` to the output path fails with `EXDEV` (cross-device — the
temp file and output dir are on different filesystems), the code falls
back to `copyFileZSlowWithHandle()`, which:

1. Calls `fallocate()` to pre-allocate the output file to the correct
size (filled with zeros)
2. Calls `bun.copyFile(in_handle, out_handle)` — but `in_handle`'s
offset is at EOF
3. `copy_file_range` / `sendfile` / `read` all use the current file
offset (EOF), read 0 bytes, and return immediately
4. Result: output file is the correct size but entirely zeros

This explains user reports of `bun build --compile
--target=bun-darwin-arm64` producing invalid binaries that `file`
identifies as "data" rather than a Mach-O executable.

## Fix

Seek the input fd to offset 0 in `copyFileZSlowWithHandle` before
calling `bun.copyFile`.

## How did you verify your code works?

- `bun bd` compiles successfully
- `bun bd test test/bundler/bun-build-compile.test.ts` — 6/6 pass
- Added tests that verify compiled binaries have valid executable
headers and produce correct output
- Manually verified cross-compilation: `bun build --compile
--target=bun-darwin-arm64` produces a valid Mach-O binary

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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-10 22:32:31 -08:00
SUZUKI Sosuke
e29e830a25 perf(path): use pre-built Structure for path.parse() result objects (#26865)
## Summary

Optimize `path.parse()` by caching a pre-built JSC Structure for the
result object. Instead of creating a new empty object and performing 5
`putDirect` calls (each triggering a Structure transition), we now use
`constructEmptyObject` with the cached Structure and write values
directly via `putDirectOffset`.

## What changed

- **`ZigGlobalObject.h/cpp`**: Added `m_pathParsedObjectStructure` as a
`LazyPropertyOfGlobalObject<Structure>` with fixed property offsets for
`{root, dir, base, ext, name}`.
- **`Path.cpp`**: Added `PathParsedObject__create` extern "C" factory
function that constructs the object using the pre-built Structure and
`putDirectOffset`.
- **`path.zig`**: Replaced `toJSObject()` implementation to call the C++
factory function instead of `createEmptyObject` + 5x `.put()`.
- **`bench/snippets/path-parse.mjs`**: Added benchmark for
`path.parse()`.

This follows the same pattern used by `JSSocketAddressDTO` and
`m_jsonlParseResultStructure`.

## Benchmark results (Apple M4 Max)

| Benchmark | Before (1.3.9) | After | Speedup |
|---|---|---|---|
| `posix.parse("/home/user/dir/file.txt")` | 266.71 ns | 119.62 ns |
**2.23x** |
| `posix.parse("/home/user/dir/")` | 239.10 ns | 91.46 ns | **2.61x** |
| `posix.parse("file.txt")` | 232.55 ns | 89.20 ns | **2.61x** |
| `posix.parse("/root")` | 246.75 ns | 92.68 ns | **2.66x** |
| `posix.parse("")` | 152.19 ns | 20.72 ns | **7.34x** |
| `win32.parse("/home/user/dir/file.txt")` | 260.08 ns | 118.12 ns |
**2.20x** |
| `win32.parse("/home/user/dir/")` | 234.35 ns | 93.47 ns | **2.51x** |
| `win32.parse("file.txt")` | 224.19 ns | 80.56 ns | **2.78x** |
| `win32.parse("/root")` | 241.20 ns | 88.23 ns | **2.73x** |
| `win32.parse("")` | 160.39 ns | 24.20 ns | **6.63x** |

**~2.2x–2.8x faster** for typical paths, **~7x faster** for empty
strings.

## GC Safety

- `LazyPropertyOfGlobalObject<Structure>` is automatically visited via
`FOR_EACH_GLOBALOBJECT_GC_MEMBER`.
- JSValues created by `createUTF8ForJS` are protected by JSC's
conservative stack scanning during the factory function call.

## Test

All 116 existing path tests pass (`bun bd test test/js/node/path/`).

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-02-10 22:32:08 -08:00
Dylan Conway
00589c3215 Merge branch 'main' into dylan/native-windows-aarch64-build 2026-02-10 17:42:04 -08:00
Dylan Conway
ef5f9adb2c update tests 2026-02-10 17:31:33 -08:00
robobun
4c205486d6 fix(test): update valkey test snapshots for Redis 8 error message format (#26878)
## Summary
- Update inline error snapshots in valkey reliability tests to match
Redis 8's changed error message format
- Redis 8 (`redis:8-alpine` in our test Docker container) no longer
appends `, with args beginning with: ` when an unknown command has no
arguments

## Root cause
Redis commit
[`25f780b6`](25f780b662)
([PR #14690](https://github.com/redis/redis/pull/14690)) changed
`commandCheckExistence()` in `src/server.c` to only append `, with args
beginning with: ` when there are actual arguments (`c->argc >= 2`).
Previously it was always appended, producing a dangling `, with args
beginning with: ` even with zero arguments.

## Changes
- `test/js/valkey/reliability/protocol-handling.test.ts`: Updated
`SYNTAX-ERROR` snapshot (no args case)
- `test/js/valkey/reliability/error-handling.test.ts`: Updated
`undefined` and `123` snapshots (no args cases)

## Test plan
- [ ] Verify `protocol-handling.test.ts` passes in CI (was failing on
every attempt as shown in #26869 / build #36831)
- [ ] Verify `error-handling.test.ts` passes in CI

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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-10 16:30:55 -08:00
Alistair Smith
099b5e430c feat(test): add --retry flag and emit separate testcase entries for retries in JUnit XML (#26866)
#### (Copies commits from #26447)

## Summary
- Add a global `--retry <N>` flag to `bun test` that sets a default
retry count for all tests (overridable by per-test `{ retry: N }`). Also
configurable via `[test] retry = N` in bunfig.toml.
- When a test passes after one or more retries, the JUnit XML reporter
emits a separate `<testcase>` entry for each failed attempt (with
`<failure>`), followed by the final passing `<testcase>`. This gives
flaky test detection tools per-attempt timing and result data using
standard JUnit XML that all CI systems can parse.

## Test plan
- `bun bd test test/js/junit-reporter/junit.test.js` — verifies separate
`<testcase>` entries appear in JUnit XML for tests that pass after retry
- `bun bd test test/cli/test/retry-flag.test.ts` — verifies the
`--retry` CLI flag applies a default retry count to all tests

## Changelog
<!-- CHANGELOG:START -->
- Added `--retry <N>` flag to `bun test` to set a default retry count
for all tests
- Added `[test] retry` option to bunfig.toml
- JUnit XML reporter now emits separate `<testcase>` entries for each
retry attempt, providing CI visibility into flaky tests
<!-- CHANGELOG:END -->

---------

Co-authored-by: Chris Lloyd <chrislloyd@anthropic.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-10 10:58:21 -08:00
Przemysław Rekucki
746771d495 fix(bake): queue pending HMR scripts for reuse (#19736) (#23995)
## Summary

Fixes "Unknown HMR script" error during rapid consecutive edits in HMR

## Test plan
- [x] Basic consecutive HMR edits work correctly

---------

Co-authored-by: Alistair Smith <alistair@anthropic.com>
2026-02-10 01:53:07 -08:00
Dylan Conway
60e21aea92 2026-02-09 23:27:28 -08:00
Dylan Conway
1de01eefd4 2026-02-09 23:23:08 -08:00
Dylan Conway
6c0a4b3fa1 2026-02-09 23:00:14 -08:00
Dylan Conway
6897c110c0 2026-02-09 22:51:07 -08:00
Dylan Conway
e84f625569 2026-02-09 22:42:10 -08:00
Dylan Conway
ae50b05b50 2026-02-09 22:40:52 -08:00
robobun
ead4a8c45d fix(fs): properly handle file: URL strings in fs.watch and fs.watchFile (#26859)
## Summary
- Convert `file:` URL strings to filesystem paths via
`Bun.fileURLToPath()` in the JS layer for both `fs.watch` and
`fs.watchFile`/`fs.unwatchFile`
- Handles percent-decoding (e.g. `%20` → space) and proper URL parsing,
which the previous naive `slice[6..]` stripping in Zig could not do
- Zig-level `file://` stripping is left unchanged; the JS layer now
resolves file URLs before they reach native code

## Test plan
- [x] New test: `fs.watch` with `file:` URL string containing
`%20`-encoded spaces
- [x] New test: `fs.watchFile` with `file:` URL string containing
`%20`-encoded spaces
- [x] Both tests fail with `USE_SYSTEM_BUN=1` and pass with `bun bd
test`
- [x] Existing `fs.watch` "should work with url" test (URL object) still
passes
- [x] Full `fs.watchFile` suite passes (6 pass, 1 skip, 0 fail)

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-09 22:22:22 -08:00
Jarred Sumner
ce715b5a0f feat(transpiler): implement TC39 standard ES decorators lowering (#26436)
## Summary

- Implement complete lowering of TC39 stage-3 standard ES decorators
(the non-legacy variant used when tsconfig has no
`experimentalDecorators`).
- Passes all 147 esbuild decorator tests and 22 additional Bun-specific
tests (191 total, 0 failures).
- Supports method, getter, setter, field, auto-accessor, private member,
and class decorators in both statement and expression positions, with
proper evaluation order, class binding semantics, and decorator
metadata.

Fixes #4122
Fixes #20206
Fixes #14529
Fixes #6051

## What's implemented

| Feature | Details |
|---|---|
| Method/getter/setter decorators | Static and instance, public and
private |
| Field decorators | Initializer replacement + extra initializers via
`__runInitializers` |
| Auto-accessor (`accessor` keyword) | Lowered to WeakMap storage +
getter/setter pair |
| Private member decorators | WeakMap/WeakSet lowering with
`__privateGet`/`__privateSet` |
| Class decorators | Statement and expression positions |
| Class expression decorators | Comma-expression lowering (no IIFE) |
| Decorator metadata | `Symbol.metadata` support via
`__decoratorMetadata` |
| Evaluation order | All decorator expressions + computed keys evaluated
in source order per TC39 spec |
| Class binding semantics | Separate inner/outer class name bindings
(element vs class decorator closures) |
| Static block extraction | `this` replaced with class name ref when
moved to suffix |
| Computed property keys | Pre-evaluated into temp variables for correct
ordering |

## Runtime helpers

Added to `src/runtime.js` and registered in `src/runtime.zig`:
- `__decoratorStart(base)` — creates decorator context array
- `__decorateElement(array, flags, name, decorators, target, extra)` —
applies decorators to a class element
- `__decoratorMetadata(array, target)` — sets `Symbol.metadata` on the
class
- `__runInitializers(array, flags, self, value)` — runs
initializer/extra-initializer arrays

## Test plan

- [x] `bun bd test
test/bundler/transpiler/es-decorators-esbuild.test.ts` — **147/147
pass** (esbuild's full decorator test suite)
- [x] `bun bd test test/bundler/transpiler/es-decorators.test.ts` —
**22/22 pass**
- [x] `bun bd test test/bundler/transpiler/decorators.test.ts` — **22/22
pass** (legacy decorators still work)
- [x] E2E runtime verification of method, field, accessor, class,
private, and expression decorators

🤖 Generated with [Claude Code](https://claude.com/claude-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: Dylan Conway <dylan.conway567@gmail.com>
2026-02-09 22:03:54 -08:00
SUZUKI Sosuke
fa78d2b408 perf(structuredClone): add fast path for arrays containing simple objects (#26818)
## Summary

Adds a **DenseArray fast path** for `structuredClone` / `postMessage`
that completely skips byte-buffer serialization when an
`ArrayWithContiguous` array contains **flat objects** (whose property
values are only primitives or strings).

This builds on #26814 which added fast paths for Int32/Double/Contiguous
arrays of primitives and strings. The main remaining slow case was
**arrays of objects** — the most common real-world pattern (e.g.
`[{name: "Alice", age: 30}, {name: "Bob", age: 25}]`). Previously, these
fell back to the full serialization path because the Contiguous fast
path rejected non-string cell elements.

## How it works

### Serialization
The existing Contiguous array handler is extended to recognize object
elements that pass `isObjectFastPathCandidate` (FinalObject, no
getters/setters, no indexed properties, all enumerable). For qualifying
objects, properties are collected into a `SimpleCloneableObject` struct
(reusing the existing `SimpleInMemoryPropertyTableEntry` type). The
result is stored as a `FixedVector<DenseArrayElement>` where
`DenseArrayElement = std::variant<JSValue, String,
SimpleCloneableObject>`.

If no object elements are found, the existing `SimpleArray` path is used
(no regression).

### Deserialization
A **Structure cache** avoids repeated Structure transitions when the
array contains many same-shape objects (the common case). The first
object is built via `constructEmptyObject` + `putDirect`, and its final
Structure + Identifiers are cached. Subsequent objects with matching
property names are created directly with `JSFinalObject::create(vm,
cachedStructure)`, skipping all transitions.

Safety guards:
- Cache is only used when property count AND all property names match
- Cache is disabled when `outOfLineCapacity() > 0` (properties exceed
`maxInlineCapacity`), since `JSFinalObject::create` cannot allocate a
butterfly

### Fallback conditions

| Condition | Behavior |
|-----------|----------|
| Elements are only primitives/strings | SimpleArray (existing) |
| Elements include `isObjectFastPathCandidate` objects | **DenseArray
(NEW)** |
| Object property value is an object/array | Fallback to normal path |
| Elements include Date, RegExp, Map, Set, ArrayBuffer, etc. | Fallback
to normal path |
| Array has holes | Fallback to normal path |

## Benchmarks

Apple M4 Max, release build vs system Bun v1.3.8 and Node.js v24.12:

| Benchmark | Node.js v24.12 | Bun v1.3.8 | **This PR** | vs Bun | vs
Node |

|-----------|---------------|------------|-------------|--------|---------|
| `[10 objects]` | 2.83 µs | 2.72 µs | **1.56 µs** | **1.7x** | **1.8x**
|
| `[100 objects]` | 24.51 µs | 25.98 µs | **14.11 µs** | **1.8x** |
**1.7x** |

## Test coverage

28 new edge-case tests covering:
- **Property value variants**: empty objects, special numbers (NaN,
Infinity, -0), null/undefined values, empty string keys, boolean-only
values, numeric string keys
- **Structure cache correctness**: alternating shapes, objects
interleaved with primitives, >maxInlineCapacity properties (100+), 1000
same-shape objects (stress test), repeated clone independence
- **Fallback correctness**: array property values, nested objects,
Date/RegExp/Map/Set/ArrayBuffer elements, getters, non-enumerable
properties, `Object.create(null)`, class instances
- **Frozen/sealed**: clones are mutable regardless of source
- **postMessage via MessageChannel**: mixed arrays with objects, empty
object arrays

## Changed files

- `src/bun.js/bindings/webcore/SerializedScriptValue.h` —
`SimpleCloneableObject`, `DenseArrayElement`, `FastPath::DenseArray`,
factory/constructor/member
- `src/bun.js/bindings/webcore/SerializedScriptValue.cpp` — serialize,
deserialize, `computeMemoryCost`
- `test/js/web/structured-clone-fastpath.test.ts` — 28 new tests
- `bench/snippets/structuredClone.mjs` — object array benchmarks

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-02-09 21:54:41 -08:00
Dylan Conway
36944ee646 2026-02-09 20:09:07 -08:00
Dylan Conway
4790197d1d 2026-02-09 13:56:26 -08:00
SUZUKI Sosuke
b7475d8768 fix(buffer): return fixed-length view from slice on resizable ArrayBuffer (#26822)
## Summary

Follow-up to #26819 ([review
comment](https://github.com/oven-sh/bun/pull/26819#discussion_r2781484939)).
Fixes `Buffer.slice()` / `Buffer.subarray()` on resizable `ArrayBuffer`
/ growable `SharedArrayBuffer` to return a **fixed-length view** instead
of a length-tracking view.

## Problem

The resizable/growable branch was passing `std::nullopt` to
`JSUint8Array::create()`, which creates a length-tracking view. When the
underlying buffer grows, the sliced view's length would incorrectly
expand:

```js
const rab = new ArrayBuffer(10, { maxByteLength: 20 });
const buf = Buffer.from(rab);
const sliced = buf.slice(0, 5);
sliced.length; // 5

rab.resize(20);
sliced.length; // was 10 (wrong), now 5 (correct)
```

Node.js specifies that `Buffer.slice()` always returns a fixed-length
view (verified on Node.js v22).

## Fix

Replace `std::nullopt` with `newLength` in the
`isResizableOrGrowableShared()` branch of
`jsBufferPrototypeFunction_sliceBody`.

## Test

Added a regression test that creates a `Buffer` from a resizable
`ArrayBuffer`, slices it, resizes the buffer, and verifies the slice
length doesn't change.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 04:48:20 -08:00
Jarred Sumner
4494170f74 perf(event_loop): avoid eventfd wakeup for setImmediate on POSIX (#26821)
### What does this PR do?

Instead of calling event_loop.wakeup() (which writes to the eventfd)
when there are pending immediate tasks, use a zero timeout in
getTimeout() so epoll/kqueue returns immediately. This avoids the
overhead of the eventfd write/read cycle on each setImmediate iteration.

On Windows, continue to call .wakeup() since that's cheap for libuv.

Verified with strace: system bun makes ~44k eventfd writes for a 5s
setImmediate loop, while this change makes 0.


### How did you verify your code works?

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-09 04:47:52 -08:00
SUZUKI Sosuke
9484218ba4 perf(buffer): move Buffer.slice/subarray to native C++ with int32 fast path (#26819)
## Summary

Move `Buffer.slice()` / `Buffer.subarray()` from a JS builtin to a
native C++ implementation, eliminating the `adjustOffset` closure
allocation and JS→C++ constructor overhead on every call. Additionally,
add an int32 fast path that skips `toNumber()` (which can invoke
`valueOf`/`Symbol.toPrimitive`) when arguments are already int32—the
common case for calls like `buf.slice(0, 10)`.

## Changes

- **`src/bun.js/bindings/JSBuffer.cpp`**: Add
`jsBufferPrototypeFunction_sliceBody` with `adjustSliceOffsetInt32` /
`adjustSliceOffsetDouble` helpers. Update prototype hash table entries
from `BuiltinGeneratorType` to `NativeFunctionType` for both `slice` and
`subarray`.
- **`src/js/builtins/JSBufferPrototype.ts`**: Remove the JS `slice`
function (was lines 667–687).
- **`bench/snippets/buffer-slice.mjs`**: Add mitata benchmark.

## Benchmark (Apple M4 Max)

| Benchmark | Before (v1.3.8) | After | Speedup |
|---|---|---|---|
| `Buffer(64).slice()` | 27.19 ns | **14.56 ns** | **1.87x** |
| `Buffer(1024).slice()` | 27.84 ns | **14.62 ns** | **1.90x** |
| `Buffer(1M).slice()` | 29.20 ns | **14.89 ns** | **1.96x** |
| `Buffer(64).slice(10)` | 30.26 ns | **16.01 ns** | **1.89x** |
| `Buffer(1024).slice(10, 100)` | 30.92 ns | **18.32 ns** | **1.69x** |
| `Buffer(1024).slice(-100, -10)` | 28.82 ns | **17.37 ns** | **1.66x**
|
| `Buffer(1024).subarray(10, 100)` | 28.67 ns | **16.32 ns** | **1.76x**
|

**~1.7–1.9x faster** across all cases. All 449 buffer tests pass.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 01:46:33 -08:00
robobun
2a5e8ef38c fix(kqueue): fix incorrect filter comparison causing excessive CPU on macOS (#26812)
## Summary

Fixes the remaining kqueue filter comparison bug in
`packages/bun-usockets/src/eventing/epoll_kqueue.c` that caused
excessive CPU usage with network requests on macOS:

- **`us_loop_run_bun_tick` filter comparison (line 302-303):** kqueue
filter values (`EVFILT_READ=-1`, `EVFILT_WRITE=-2`) were compared using
bitwise AND (`&`) instead of equality (`==`). Since these are signed
negative integers (not bitmasks), `(-2) & (-1)` = `-2` (truthy), meaning
every `EVFILT_WRITE` event was also misidentified as `EVFILT_READ`. This
was already fixed in `us_loop_run` (by PR #25475) but the same bug
remained in `us_loop_run_bun_tick`, which is the primary event loop
function used by Bun.

This is a macOS-only issue (Linux uses epoll, which is unaffected).

Closes #26811

## Test plan

- [x] Added regression test at `test/regression/issue/26811.test.ts`
that makes concurrent HTTPS POST requests
- [x] Test passes with `bun bd test test/regression/issue/26811.test.ts`
- [ ] Manual verification on macOS: run the reporter's [repro
script](https://gist.github.com/jkoppel/d26732574dfcdcc6bfc4958596054d2e)
and confirm CPU usage stays low

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2026-02-09 00:52:17 -08:00
robobun
a84f12b816 Use edge-triggered epoll for eventfd wakeups (#26815)
## Summary

- Switch both eventfd wakeup sites (Zig IO watcher loop and usockets
async) to edge-triggered (`EPOLLET`) epoll mode, eliminating unnecessary
`read()` syscalls on every event loop wakeup
- Add `EAGAIN`/`EINTR` overflow handling in `us_internal_async_wakeup`,
matching libuv's approach ([commit
`e5cb1d3d`](https://github.com/libuv/libuv/commit/e5cb1d3d))

With edge-triggered mode, each `write()` to the eventfd produces a new
edge event regardless of the current counter value, so draining the
counter via `read()` is unnecessary. The counter will never overflow in
practice (~18 quintillion wakeups), but overflow handling is included
defensively.

### Files changed

- **`src/io/io.zig`** — Add `EPOLL.ET` to eventfd registration, replace
drain `read()` with `continue`
- **`packages/bun-usockets/src/eventing/epoll_kqueue.c`** — Set
`leave_poll_ready = 1` for async callbacks, upgrade to `EPOLLET` via
`EPOLL_CTL_MOD`, add `EAGAIN`/`EINTR` handling in wakeup write

## Test plan

- [x] Verified with `strace -f -e trace=read,eventfd2` that eventfd
reads are fully eliminated after the change (0 reads on the eventfd fd)
- [x] Confirmed remaining 8-byte reads in traces are timerfd reads
(legitimate, required)
- [x] Stress tested with 50 concurrent async tasks (1000 total
`Bun.sleep(1)` iterations) — all completed correctly
- [x] `LinuxWaker.wait()` (used by `BundleThread` as a blocking sleep)
is intentionally unchanged

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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2026-02-09 00:36:30 -08:00
SUZUKI Sosuke
0f43ea9bec perf(structuredClone): add fast path for root-level dense arrays (#26814)
## Summary

Add a fast path for `structuredClone` and `postMessage` when the root
value is a dense array of primitives or strings. This bypasses the full
`CloneSerializer`/`CloneDeserializer` machinery by keeping data in
native C++ structures instead of serializing to a byte stream.

**Important:** This optimization only applies when the root value passed
to `structuredClone()` / `postMessage()` is an array. Nested arrays
within objects still go through the normal serialization path.

## Implementation

Three tiers of array fast paths, checked in order:

| Tier | Indexing Type | Strategy | Applies When |
|------|--------------|----------|--------------|
| **Tier 1** | `ArrayWithInt32` | `memcpy` butterfly data | Dense int32
array, no holes, no named properties |
| **Tier 2** | `ArrayWithDouble` | `memcpy` butterfly data | Dense
double array, no holes, no named properties |
| **Tier 3** | `ArrayWithContiguous` | Copy elements into
`FixedVector<variant<JSValue, String>>` | Dense array of
primitives/strings, no holes, no named properties |

All tiers fall through to the normal serialization path when:
- The array has holes that must forward to the prototype
- The array has named properties (e.g., `arr.foo = "bar"`) — checked via
`structure->maxOffset() != invalidOffset`
- Elements contain non-primitive, non-string values (objects, arrays,
etc.)
- The context requires wire-format serialization (storage, cross-process
transfer)

### Deserialization

- **Tier 1/2:** Allocate a new `Butterfly` via `vm.auxiliarySpace()`,
`memcpy` data back, create array with `JSArray::createWithButterfly()`.
Falls back to normal deserialization if `isHavingABadTime` (forced
ArrayStorage mode).
- **Tier 3:** Pre-convert elements to `JSValue` (including `jsString()`
allocation), then use `JSArray::tryCreateUninitializedRestricted()` +
`initializeIndex()`.

## Benchmarks

Apple M4 Max, comparing system Bun 1.3.8 vs this branch (release build):

| Benchmark | Before | After | Speedup |
|-----------|--------|-------|---------|
| `structuredClone([10 numbers])` | 308.71 ns | 40.38 ns | **7.6x** |
| `structuredClone([100 numbers])` | 1.62 µs | 86.87 ns | **18.7x** |
| `structuredClone([1000 numbers])` | 13.79 µs | 544.56 ns | **25.3x** |
| `structuredClone([10 strings])` | 642.38 ns | 307.38 ns | **2.1x** |
| `structuredClone([100 strings])` | 5.67 µs | 2.57 µs | **2.2x** |
| `structuredClone([10 mixed])` | 446.32 ns | 198.35 ns | **2.3x** |
| `structuredClone(nested array)` | 1.84 µs | 1.79 µs | 1.0x (not
eligible) |
| `structuredClone({a: 123})` | 95.98 ns | 100.07 ns | 1.0x (no
regression) |

Int32 arrays see the largest gains (up to 25x) since they use a direct
`memcpy` of butterfly memory. String/mixed arrays see ~2x improvement.
No performance regression on non-eligible inputs.

## Bug Fix

Also fixes a correctness bug where arrays with named properties (e.g.,
`arr.foo = "bar"`) would lose those properties when going through the
array fast path. Added a `structure->maxOffset() != invalidOffset` guard
to fall back to normal serialization for such arrays.

Fixed a minor double-counting issue in `computeMemoryCost` where
`JSValue` elements in `SimpleArray` were counted both by `byteSize()`
and individually.

## Test Plan

38 tests in `test/js/web/structured-clone-fastpath.test.ts` covering:

- Basic array types: empty, numbers, strings, mixed primitives, special
numbers (`-0`, `NaN`, `Infinity`)
- Large arrays (10,000 elements)
- Tier 2: double arrays, Int32→Double transition
- Deep clone independence verification
- Named properties on Int32, Double, and Contiguous arrays
- `postMessage` via `MessageChannel` for Int32, Double, and mixed arrays
- Edge cases: frozen/sealed arrays, deleted elements (holes), `length`
extension, single-element arrays
- Prototype modification (custom prototype, indexed prototype properties
with holes)
- `Array` subclass identity loss (per spec)
- `undefined`-only and `null`-only arrays
- Multiple independent clones from the same source

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-02-08 21:36:59 -08:00
Jarred Sumner
0889897a1c Revert "feat(bundler): add configurable CJS→ESM unwrapping via unwrapCJSToESM"
This reverts commit e3c25260ed.
2026-02-08 19:49:26 -08:00
Jarred Sumner
68f2ea4b95 Fix release script 2026-02-08 01:39:10 -08:00
Jarred Sumner
d4ebfd9771 Bump 2026-02-08 01:32:25 -08:00
Jarred Sumner
e3c25260ed feat(bundler): add configurable CJS→ESM unwrapping via unwrapCJSToESM
Add `minify.unwrapCJSToESM` JS API option and `--unwrap-cjs-to-esm` CLI
flag to force CJS-to-ESM conversion for specific packages, eliminating
the `__commonJS` wrapper. Supports wildcard patterns (e.g. `"@scope/*"`).
User entries extend the default React family list.

Also removes the react/react-dom version check that gated conversion,
and fixes `packageName()` to handle scoped packages (`@scope/pkg`).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 01:32:10 -08:00
Alistair Smith
1bded85718 types: Enable --splitting with compile (#26796)
### What does this PR do?

Enables --splitting with compile

### How did you verify your code works?

Bun types integration test fixture updates
2026-02-07 13:39:18 -08:00
Dylan Conway
cf6cdbbbad Revert "Mimalloc v3 update (#26379)" (#26783)
This reverts commit c63415c9c9.

### What does this PR do?

### How did you verify your code works?
bun-v1.3.9
2026-02-06 18:05:17 -08:00
Dylan Conway
a118ed1851 2026-02-05 23:06:06 -08:00
robobun
89d2b1cd0b fix(websocket): add missing incPendingActivityCount() in blob binaryType case (#26670)
## Summary

- Fix crash ("Pure virtual function called!") when WebSocket client
receives binary data with `binaryType = "blob"` and no event listener
attached
- Add missing `incPendingActivityCount()` call before `postTask` in the
Blob case of `didReceiveBinaryData`
- Add regression test for issue #26669

## Root Cause

The Blob case in `didReceiveBinaryData` (WebSocket.cpp:1324-1331) was
calling `decPendingActivityCount()` inside the `postTask` callback
without a matching `incPendingActivityCount()` beforehand. This bug was
introduced in #21471 when Blob support was added.

The ArrayBuffer and NodeBuffer cases correctly call
`incPendingActivityCount()` before `postTask`, but the Blob case was
missing this call.

## Test plan

- [x] New regression test verifies WebSocket with `binaryType = "blob"`
doesn't crash on ping frames
- [x] `bun bd test test/regression/issue/26669.test.ts` passes

Fixes #26669

🤖 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>
Co-authored-by: Ciro Spaciari MacBook <ciro@anthropic.com>
2026-02-05 20:39:19 -08:00
Jarred Sumner
2019a1b11d Bump WebKit 2026-02-05 20:09:39 -08:00
SUZUKI Sosuke
6c70ce2485 Update WebKit to 7bc2f97e28353062bb54776ce01e4c2ff24c35cc (#26769)
### What does this PR do?

### How did you verify your code works?
2026-02-05 17:58:30 -08:00
SUZUKI Sosuke
0e386c4168 fix(stringWidth): correct width for Thai/Lao spacing vowels (#26728)
## Summary

`Bun.stringWidth` was incorrectly treating Thai SARA AA (U+0E32), SARA
AM (U+0E33), and their Lao equivalents (U+0EB2, U+0EB3) as zero-width
characters.

## Root Cause

In `src/string/immutable/visible.zig`, the range check for Thai/Lao
combining marks was too broad:
- Thai: `0xe31 <= cp <= 0xe3a` included U+0E32 and U+0E33
- Lao: `0xeb1 <= cp <= 0xebc` included U+0EB2 and U+0EB3

According to Unicode (UCD Grapheme_Break property), these are **spacing
vowels** (Grapheme_Base), not combining marks.

## Changes

- **`src/string/immutable/visible.zig`**: Exclude U+0E32, U+0E33,
U+0EB2, U+0EB3 from zero-width ranges
- **`test/js/bun/util/stringWidth.test.ts`**: Add tests for Thai and Lao
spacing vowels

## Before/After

| Character | Before | After |
|-----------|--------|-------|
| `\u0E32` (SARA AA) | 0 | 1 |
| `\u0E33` (SARA AM) | 0 | 1 |
| `คำ` (common Thai word) | 1 | 2 |
| `\u0EB2` (Lao AA) | 0 | 1 |
| `\u0EB3` (Lao AM) | 0 | 1 |

---------

Co-authored-by: Claude Opus 4.5 <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>
2026-02-05 17:31:15 -08:00
Dylan Conway
44defe3c43 2026-02-05 17:30:23 -08:00
Dylan Conway
582bf80c6c 2026-02-05 16:58:38 -08:00
Dylan Conway
65af35706b fix: propagate CMP0197 policy to sub-projects on Windows ARM64 2026-02-05 16:22:30 -08:00
Dylan Conway
5246c7ab27 fix: skip ReleaseSafe Zig compiler on Windows ARM64 due to LLVM SEH bug 2026-02-05 12:26:03 -08:00
Dylan Conway
82e279f894 2026-02-05 12:04:22 -08:00