## Summary
Fixes a panic that occurred when passing `NumberObject` or
`BooleanObject` as MySQL query parameters.
**Panic message:** `A JavaScript exception was thrown, but it was
cleared before it could be read.`
## Root Cause
The `FieldType.fromJS` function in `src/sql/mysql/MySQLTypes.zig` was
returning `error.JSError` without throwing a JavaScript exception first
for:
- `NumberObject` (created via `new Number(42)`)
- `BooleanObject` (created via `new Boolean(true)`)
- Non-indexable types
This violated the contract that `error.JSError` means "an exception has
already been thrown and is ready to be taken."
## Call Chain
1. User executes `await sql\`SELECT ${new Number(42)} as value\``
2. `FieldType.fromJS()` detects `.NumberObject` and returns
`error.JSError` without throwing
3. Error propagates to `MySQLQuery.runPreparedQuery()`
4. Code checks `hasException()` → returns false (no exception exists!)
5. Calls `mysqlErrorToJS(globalObject, "...", error.JSError)`
6. `mysqlErrorToJS` tries to `takeException(error.JSError)` but there's
no exception
7. **PANIC**
## Fix
The fix throws a proper exception with a helpful message before
returning `error.JSError`:
- `"Cannot bind NumberObject to query parameter. Use a primitive number
instead."`
- `"Cannot bind BooleanObject to query parameter. Use a primitive
boolean instead."`
- `"Cannot bind this type to query parameter"`
## Test Plan
Added regression tests in `test/js/sql/sql-mysql.test.ts`:
- Test passing `NumberObject` as parameter
- Test passing `BooleanObject` as parameter
Both tests verify that a proper error is thrown instead of crashing.
Verified manually with local MySQL server that:
- ✅ NumberObject now throws proper error (was crashing)
- ✅ BooleanObject now throws proper error (was crashing)
- ✅ Primitive numbers still work correctly
🤖 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>
## Summary
Adds a new `--only-failures` flag to `bun test` that only displays test
failures, similar to `--dots` but without printing dots for each test.
## Motivation
When running large test suites or in CI environments, users often only
care about test failures. The existing `--dots` reporter reduces
verbosity by showing dots, but still requires visual scanning to find
failures. The `--only-failures` flag provides a cleaner output by
completely suppressing passing tests.
## Changes
- Added `--only-failures` CLI flag in `Arguments.zig`
- Added `only_failures` boolean to the test reporters struct in
`cli.zig`
- Updated test output logic in `test_command.zig` to skip non-failures
when flag is set
- Updated `jest.zig` and `bun_test.zig` to handle the new flag
- Added comprehensive tests in `only-failures.test.ts`
## Usage
```bash
bun test --only-failures
```
Example output (only shows failures):
```
test/example.test.ts:
(fail) failing test
error: expect(received).toBe(expected)
Expected: 3
Received: 2
5 pass
1 skip
2 fail
Ran 8 tests across 1 file.
```
## Test Plan
- Verified `--only-failures` flag only shows failing tests
- Verified normal test output still works without the flag
- Verified `--dots` reporter still works correctly
- Added regression tests with snapshot comparisons
🤖 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: pfg <pfg@pfg.pw>
Fixes#23569
## Summary
HTML imports require bundling to work correctly, as they need to process
and transform linked assets (JS/CSS). When `--no-bundle` is used, no
bundling or transformation happens, which causes a crash.
This change adds validation to detect HTML entrypoints when
`--no-bundle` is used and provides a clear error message explaining that
"HTML imports are only supported when bundling".
## Changes
- Added validation in `src/cli/build_command.zig` to check for HTML
entrypoints when `--no-bundle` flag is used
- Shows clear error message: "HTML imports are only supported when
bundling"
- Added regression tests in `test/regression/issue/23569.test.ts`
## Test Plan
### Before
```bash
$ bun build ./index.html --no-bundle
# Crashes without helpful error
```
### After
```bash
$ bun build ./index.html --no-bundle
error: HTML imports are only supported when bundling
```
### Tests
- ✅ Test with `--no-bundle` flag errors correctly
- ✅ Test with `--no-bundle --outdir` errors correctly
- ✅ Test without `--no-bundle` works normally
- ✅ All 3 regression tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
### What does this PR do?
Fixes#23521
### How did you verify your code works?
Added 3 previously failing tests for `"bin"`, `"directories.bin"`, and
deduplicating entry in both `"bin.directories"` and `"files"`
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## Summary
Fixes two critical bugs in Bun Shell:
1. **Memory leaks & incorrect GC reporting**: Shell objects weren't
reporting their memory usage to JavaScriptCore's garbage collector,
causing memory to accumulate unchecked. Also fixes a leak where
`ShellArgs` wasn't being freed in `Interpreter.finalize()`.
2. **Blocking I/O on macOS**: Fixes a bug where writing large amounts of
data (>1MB) to pipes would block the main thread on macOS. The issue:
`sendto()` with `MSG_NOWAIT` flag blocks on macOS despite the flag, so
we now avoid the socket fast path unless the socket is already
non-blocking.
## Changes
- Adds `memoryCost()` and `estimatedSize()` implementations across shell
AST nodes, interpreter, and I/O structures
- Reports estimated memory size to JavaScriptCore GC via
`vm.heap.reportExtraMemoryAllocated()`
- Fixes missing `this.args.deinit()` call in interpreter finalization
- Fixes `BabyList.memoryCost()` to return bytes, not element count
- Conditionally uses socket fast path in IOWriter based on platform and
socket state
## Test plan
- [x] New test: `shell-leak-args.test.ts` - validates memory doesn't
leak during parsing/execution
- [x] New test: `shell-blocking-pipe.test.ts` - validates large pipe
writes don't block the main thread
- [x] Existing shell tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
## Summary
Fixes a panic that occurred when formatting deeply nested objects with
many properties in test output.
## Problem
The `writeIndent()` function in `pretty_format.zig:648` performed
`written * 2` which triggered integer overflow checking in debug builds
when formatting complex nested structures.
**Original crash:**
```
panic: integer overflow
writeIndent at bun.js/test/pretty_format.zig:648
```
**Platform:** Windows x86_64_baseline, Bun v1.3.0
## Solution
Changed from:
```zig
try writer.writeAll(buf[0 .. written * 2]);
```
To:
```zig
const byte_count = @min(buf.len, written *% 2);
try writer.writeAll(buf[0..byte_count]);
```
- Used wrapping multiplication (`*%`) to prevent overflow panic
- Added bounds checking with `@min(buf.len, ...)` for safety
- Maintains correct behavior while preventing crashes
## Test
Added regression test at
`test/js/bun/test/pretty-format-overflow.test.ts` that:
- Creates deeply nested objects (500 levels with 50 properties each)
- Verifies no panic/overflow/crash occurs when formatting
- Uses exact configuration that triggered the original crash
## Verification
- ✅ Test passes with the fix
- ✅ Test would crash without the fix (in debug builds)
- ✅ No changes to behavior, only safety improvement
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
### What does this PR do?
Fixes printing `import.meta.url` and others with `--bytecode`. Fixes
#14954.
Fixes printing `__toESM` when output module format is CJS and input
module format is ESM.
The key change is that `__toESM`'s `isNodeMode` parameter now depends on
the **input module type** (whether the importing file uses ESM syntax
like `import`/`export`) rather than the output format. This matches
Node.js ESM behavior where importing CommonJS from `.mjs` files always
wraps the entire `module.exports` object as the default export, ignoring
`__esModule` markers.
### How did you verify your code works?
Added comprehensive test suite in `test/bundler/bundler_cjs.test.ts`
with **23 tests** covering:
#### Core Behaviors:
- ✅ Files using `import` syntax always get `isNodeMode=1`, which
**ignores `__esModule`** markers and wraps the entire CJS module as
default
- ✅ This matches Node.js ESM semantics for importing CJS from `.mjs`
files
- ✅ Different CJS export patterns (`exports.x`, `module.exports = ...`,
functions, primitives)
- ✅ Named, default, and namespace (`import *`) imports
- ✅ Different targets (node, browser, bun) - all behave the same
- ✅ Different output formats (esm, cjs) - format doesn't affect the
behavior
- ✅ `.mjs` files re-exporting from `.cjs`
- ✅ Deep re-export chains
- ✅ Edge cases (non-boolean `__esModule`, `__esModule=false`, etc.)
#### Test Results:
- **With this PR's changes**: All 23 tests pass ✅
- **Without this PR (system bun)**: 22 pass, 1 fails (the one testing
that `__esModule` is ignored with import syntax + CJS format)
The failing test with system bun demonstrates the bug being fixed:
currently, format=cjs with import syntax still respects `__esModule`,
but it should ignore it (matching Node.js behavior).
---------
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
### What does this PR do?
### How did you verify your code works?
---------
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: Claude <noreply@anthropic.com>
## Summary
Fixed an unsigned integer underflow in the bounds check for
`writeBigInt64LE`, `writeBigInt64BE`, `writeBigUInt64LE`, and
`writeBigUInt64BE` methods.
## Problem
When `byteLength < 8`, the bounds check `offset > byteLength - 8` would
cause unsigned integer underflow (since both are `size_t`), resulting in
a large positive number that would pass the check. This allowed
out-of-bounds writes and caused ASAN use-after-poison errors.
**Reproduction:**
```js
const buf = Buffer.from("Hello World");
const slice = buf.slice(0, 5);
slice.writeBigUInt64BE(4096n, 10000); // ASAN error!
```
## Solution
Added an explicit `byteLength < 8` check before the subtraction to
prevent the underflow. The fix is applied to all four functions:
- `writeBigInt64LE` (src/bun.js/bindings/JSBuffer.cpp:2464)
- `writeBigInt64BE` (src/bun.js/bindings/JSBuffer.cpp:2504)
- `writeBigUInt64LE` (src/bun.js/bindings/JSBuffer.cpp:2543)
- `writeBigUInt64BE` (src/bun.js/bindings/JSBuffer.cpp:2582)
## Test plan
- Added comprehensive regression tests covering all edge cases
- Verified the original reproduction case now throws a proper RangeError
instead of crashing
- All tests 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: Jarred Sumner <jarred@jarredsumner.com>
## Summary
Fixes an assertion failure that occurred when `URLSearchParams.toJSON()`
was called with numeric string keys.
## The Problem
When using numeric string keys (e.g., `"39208"`, `"0"`, `"100"`),
calling `toJSON()` would trigger:
```
ASSERTION FAILED: !parseIndex(propertyName)
cache/webkit-6d0f3aac0b817cc0/include/JavaScriptCore/JSObjectInlines.h:444
```
Reproduction:
```javascript
const params = new URLSearchParams();
params.set("39208", "updated");
params.toJSON(); // crashes
```
## Root Cause
The `getInternalProperties` function in `JSURLSearchParams.cpp` was
using `putDirect()` to add properties to the result object. However,
`putDirect()` cannot be used with property names that can be parsed as
array indices - JSC expects such properties to use indexed storage
instead.
## The Fix
- Replace `putDirect()` with `putDirectMayBeIndex()`, which
automatically handles both regular properties and numeric indices
- Replace `getDirect()` with `get()` to properly retrieve values for
both types of properties
## Test Plan
Added comprehensive tests to `test/js/web/html/URLSearchParams.test.ts`:
- ✅ Single numeric string keys
- ✅ Multiple numeric keys
- ✅ Mixed numeric and non-numeric keys
- ✅ Duplicate numeric keys
- ✅ Extra arguments (original crash case)
All tests pass, and the original crash no longer occurs.
🤖 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>
### What does this PR do?
Fixes#23621.
Note that the quality of this code is quite low, but since Redis is
getting a rewrite, this is a stop-gap. The tests are what really matters
here.
This whole PR is claude.
### How did you verify your code works?
CI.
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## Summary
Fixes a panic that occurred when `console.log()` tried to format a Set
or Map instance with a non-numeric `size` property.
## Issue
When a Set or Map subclass overrides the `size` property with a
non-numeric value (like a constructor function, string, or other
object), calling `console.log()` on the instance would trigger a panic:
```javascript
class C1 extends Set {
constructor() {
super();
Object.defineProperty(this, "size", {
writable: true,
enumerable: true,
value: Set
});
console.log(this); // panic!
}
}
new C1();
```
## Root Cause
In `src/bun.js/ConsoleObject.zig`, the Map and Set formatting code
called `toInt32()` directly on the `size` property value. This function
asserts that the value is not a Cell (objects/functions), causing a
panic when `size` was overridden with non-numeric values.
## Solution
Changed both Map and Set formatting to use `coerce(i32, globalThis)`
instead of `toInt32()`. This properly handles non-numeric values using
JavaScript's standard type coercion rules and propagates any coercion
errors appropriately.
## Test Plan
Added regression tests to `test/js/bun/util/inspect.test.js` that verify
Set and Map instances with overridden non-numeric `size` properties can
be inspected without panicking.
🤖 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>
## Summary
This PR adds support for the `--pass-with-no-tests` CLI flag to the test
runner, addressing issue #20814.
With the latest v1.2.8 release, the test runner now fails when no tests
match a filter. While this is useful for agentic coding workflows, there
are legitimate cases where the previous behavior is preferred, such as
in monorepos where a standard test file pattern is used as a filter but
not all packages contain tests.
This flag makes the test runner behave like Jest and Vitest, exiting
with code 0 when no tests are found.
## Changes
- Added `--pass-with-no-tests` flag to CLI arguments in
`src/cli/Arguments.zig`
- Added `pass_with_no_tests` field to `TestOptions` struct in
`src/cli.zig`
- Updated test runner logic in `src/cli/test_command.zig` to respect the
flag
- Added comprehensive tests in
`test/cli/test/pass-with-no-tests.test.ts`
## Test Plan
All new tests pass:
- ✅ `--pass-with-no-tests` exits with 0 when no test files found
- ✅ `--pass-with-no-tests` exits with 0 when filters match no tests
- ✅ Without flag, still exits with 1 when no tests found (preserves
existing behavior)
- ✅ `--pass-with-no-tests` still fails when actual tests fail
Closes#20814🤖 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: pfg <pfg@pfg.pw>
### What does this PR do?
reduce memory usage when streaming (this should be a temporary solution
until owned_and_done is fixed)
### How did you verify your code works?
Added a test that should not be flaky in CI
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### What does this PR do?
Fixes unhelpful FFI error messages that made debugging extremely
difficult. The user reported that when dlopen fails, the error doesn't
tell you which library failed or why.
**Before:**
```
Failed to open library. This is usually caused by a missing library or an invalid library path.
```
**After:**
```
Failed to open library "libnonexistent.so": /path/libnonexistent.so: cannot open shared object file: No such file or directory
```
### How did you verify your code works?
1. **Cross-platform compilation verified**
- Ran `bun run zig:check-all` - all platforms compile successfully
(Windows, macOS x86_64/arm64, Linux x86_64/arm64 glibc/musl)
2. **Added comprehensive regression tests**
(`test/regression/issue/dlopen-missing-symbol-error.test.ts`)
- ✅ Tests dlopen error shows library name when it can't be opened
- ✅ Tests dlopen error shows symbol name when symbol isn't found
- ✅ Tests linkSymbols shows helpful error when ptr is missing
- ✅ Tests handle both glibc and musl libc systems
3. **Manually tested error messages**
- Missing library: Shows full path and "No such file or directory"
- Invalid library: Shows "invalid ELF header"
- Missing symbol: Shows symbol and library name
- linkSymbols without ptr: Shows helpful explanation
### Implementation Details
1. **Created cross-platform getDlError() helper**
(src/bun.js/api/ffi.zig:8-21)
- On POSIX: Calls `std.c.dlerror()` to get actual system error message
- On Windows: Returns generic message (detailed errors handled in C++
layer via `GetLastError()` + `FormatMessageW()`)
- Follows the pattern established in `BunProcess.cpp` for dlopen error
handling
2. **Improved error messages**
- dlopen errors now include library name and system error details
- linkSymbols errors explain the ptr field requirement clearly
- Symbol lookup errors already showed both symbol and library name
3. **Fixed linkSymbols error propagation** (src/js/bun/ffi.ts:529)
- Added missing `if (Error.isError(result)) throw result;` check
- Now consistent with dlopen which already had this check
### Example Error Messages
- **Missing library:** `Failed to open library "libnonexistent.so":
cannot open shared object file: No such file or directory`
- **Invalid library:** `Failed to open library "/etc/passwd": invalid
ELF header`
- **Missing symbol:** `Symbol "nonexistent_func" not found in
"libc.so.6"`
- **Missing ptr:** `Symbol "myFunc" is missing a "ptr" field. When using
linkSymbols() or CFunction()...`
Fixes the issue mentioned in:
https://fxtwitter.com/hassanalinali/status/1977710104334963015🤖 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>
## Summary
Adds comprehensive support to `generate-classes.ts` for JavaScript
classes that need both named WriteBarrier members (like callbacks) and a
dynamic array of JSValues, all properly tracked by the garbage
collector. This replaces error-prone manual `protect()/unprotect()`
calls with proper GC integration.
## Motivation
The shell interpreter was using `JSValue.protect()/unprotect()` to keep
JavaScript objects alive, which caused memory leaks when cleanup paths
didn't properly unprotect values. This is a common pattern that needed a
better solution.
## What Changed
### Code Generator (`generate-classes.ts`)
When a class has both `values: ["resolve", "reject"]` and `valuesArray:
true`:
**Generated C++ class gets:**
- `WTF::FixedVector<JSC::WriteBarrier<JSC::Unknown>> jsvalueArray`
member for dynamic array
- Individual `JSC::WriteBarrier<JSC::Unknown> m_resolve, m_reject`
members for named values
- 4 `create()` overloads covering all combinations:
1. Basic: `create(vm, globalObject, structure, ptr)`
2. Array only: `create(..., FixedVector<WriteBarrier<Unknown>>&&)`
3. Named values: `create(..., JSValue resolve, JSValue reject)`
4. Both: `create(..., FixedVector&&, JSValue resolve, JSValue reject)`
**Constructor overloads using `WriteBarrierEarlyInit`:**
```cpp
JSShellInterpreter(VM& vm, Structure* structure, void* ptr,
JSValue resolve, JSValue reject)
: Base(vm, structure)
, m_resolve(resolve, JSC::WriteBarrierEarlyInit) // ← Key technique
, m_reject(reject, JSC::WriteBarrierEarlyInit)
{
m_ctx = ptr;
}
```
The `WriteBarrierEarlyInit` tag allows initializing WriteBarriers in the
constructor initializer list before the object is fully constructed,
which is required for proper GC integration.
**Extern C bridge functions:**
- `TypeName__createWithValues(globalObject, ptr, markedArgumentBuffer*)`
- `TypeName__createWithInitialValues(globalObject, ptr, resolve,
reject)`
- `TypeName__createWithValuesAndInitialValues(globalObject, ptr,
buffer*, resolve, reject)`
**Zig convenience wrappers:**
- `toJSWithValues(this, globalObject, markedArgumentBuffer)`
- `toJSWithInitialValues(this, globalObject, resolve, reject)`
- `toJSWithValuesAndInitialValues(this, globalObject, buffer, resolve,
reject)`
### Shell Interpreter Memory Leak Fix
**Before:**
```zig
const js_value = JSShellInterpreter.toJS(interpreter, globalThis);
resolve.protect(); // Manual reference counting
reject.protect();
// ... later in cleanup ...
resolve.unprotect(); // Easy to forget/miss in error paths
reject.unprotect();
```
**After:**
```zig
const js_value = Bun__createShellInterpreter(
globalThis,
interpreter,
parsed_shell_script,
resolve, // Stored with WriteBarrierEarlyInit
reject, // GC tracks automatically
);
// No manual memory management needed!
```
### Supporting Changes
- Added `MarkedArgumentBuffer.wrap()` helper in Zig for safe
MarkedArgumentBuffer usage
- Created `ShellBindings.cpp` with `Bun__createShellInterpreter()` using
the new API
- Removed all `protect()/unprotect()` calls from shell interpreter
- Applied pattern to both `ShellInterpreter` and `ShellArgs` classes
## Benefits
1. **No memory leaks**: GC tracks all references automatically
2. **Safer**: Cannot forget to unprotect values
3. **Cleaner code**: No manual reference counting
4. **Reusable**: Pattern works for any class needing to store JSValues
5. **Performance**: Same cost as manual protect/unprotect but safer
## Testing
Existing shell tests verify the functionality. The pattern is already
used throughout JavaScriptCore for similar cases (see
`JSWrappingFunction`, `AsyncContextFrame`, `JSModuleMock`, etc.)
## When to Use This Pattern
Use `values` + `valuesArray` + `WriteBarrierEarlyInit` when:
- Your C++ class needs to keep JavaScript values alive
- You have both known named callbacks AND dynamic arrays of values
- You want the GC to track references instead of manual
protect/unprotect
- Your class extends `JSDestructibleObject`
🤖 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>
## Summary
Fixes flaky tests in `test/cli/inspect/BunFrontendDevServer.test.ts` by
resolving a race condition where tests would miss the `clientConnected`
event.
## Problem
Two tests were failing intermittently (~30% failure rate):
- `should notify on clientNavigated events`
- `should notify on consoleLog events`
Both tests would timeout after 5000ms waiting for the `clientConnected`
event that never arrived.
## Root Cause
In `src/bake/DevServer/HmrSocket.zig:30-41`, when a WebSocket connection
opens, the `onOpen()` handler immediately sends the `clientConnected`
inspector event.
The flaky tests had this problematic sequence:
1. Create WebSocket with `await createHMRClient()`
2. Server's `onOpen()` fires instantly and emits `clientConnected` event
3. Test then calls
`session.waitForEvent("BunFrontendDevServer.clientConnected")`
4. **Race condition**: Event already sent, test waits forever and times
out
## Solution
Set up event listeners **before** creating the WebSocket connection,
matching the pattern from the working test "should receive
clientConnected and clientDisconnected events":
```typescript
// Set up listener FIRST
const connectedEventPromise = session.waitForEvent("BunFrontendDevServer.clientConnected");
// Then create WebSocket
const ws = await createHMRClient();
// Now await the event
const connectedEvent = await connectedEventPromise;
```
## Testing
Verified with 30 consecutive test runs:
- **Before fix**: ~30% failure rate
- **After fix**: 100% pass rate (30/30 passes)
Tested with both:
- Debug build: `bun bd test`
- System bun v1.3.0: `bun test`
🤖 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>
## Summary
Adds `BUN_WATCHER_TRACE` environment variable that logs all file watcher
events to a JSON file for debugging. When set, the watcher appends
detailed event information to the specified file path.
## Motivation
Debugging watch-related issues (especially with `bun --watch` and `bun
--hot`) can be difficult without visibility into what the watcher is
actually seeing. This feature provides detailed trace logs showing
exactly which files are being watched and what events are triggered.
## Implementation
- **Isolated module** (`src/watcher/WatcherTrace.zig`) - All trace logic
in separate file
- **No locking needed** - Watcher runs on its own thread, no mutex
required
- **Append-only mode** - Traces persist across multiple runs for easier
debugging
- **Silent errors** - Won't break functionality if trace file can't be
created
- **JSON format** - Easy to parse and analyze
## Usage
```bash
BUN_WATCHER_TRACE=/tmp/watch.log bun --watch script.js
BUN_WATCHER_TRACE=/tmp/hot.log bun --hot server.ts
```
## JSON Output Format
Each line is a JSON object with:
```json
{
"timestamp": 1760280923269,
"index": 0,
"path": "/path/to/watched/file.js",
"delete": false,
"write": true,
"rename": false,
"metadata": false,
"move_to": false,
"changed_files": ["script.js"]
}
```
## Testing
All tests use stdout streaming to wait for actual reloads (no
sleeps/timeouts):
- Tests with `--watch` flag
- Tests with `fs.watch` API
- Tests that trace file appends across multiple runs
- Tests validation of JSON format and event details
```
✅ 4 pass
❌ 0 fail
📊 52 expect() calls
```
## Files Changed
- `src/Watcher.zig` - Minimal integration with WatcherTrace module
- `src/watcher/WatcherTrace.zig` - New isolated trace implementation
- `src/watcher/KEventWatcher.zig` - Calls writeTraceEvents before
onFileUpdate
- `src/watcher/INotifyWatcher.zig` - Calls writeTraceEvents before
onFileUpdate
- `src/watcher/WindowsWatcher.zig` - Calls writeTraceEvents before
onFileUpdate
- `test/cli/watch/watcher-trace.test.ts` - Comprehensive tests
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## Summary
This PR implements support for `localAddress` and `localPort` options in
TCP connections, allowing users to bind outgoing connections to a
specific local IP address and port.
This addresses issue #6888 and implements Node.js-compatible behavior
for these options.
## Changes
### C Layer (uSockets)
- **`bsd.c`**: Modified `bsd_create_connect_socket()` to accept a
`local_addr` parameter and call `bind()` before `connect()` when a local
address is specified
- **`context.c`**: Updated `us_socket_context_connect()` and
`start_connections()` to parse and pass local address parameters through
the connection flow
- **`libusockets.h`**: Updated public API signatures to include
`local_host` and `local_port` parameters
- **`internal.h`**: Added `local_host` and `local_port` fields to
`us_connecting_socket_t` structure
- **`openssl.c`**: Updated SSL connection function to match the new
signature
### Zig Layer
- **`SocketContext.zig`**: Updated `connect()` method to accept and pass
through `local_host` and `local_port` parameters
- **`socket.zig`**: Modified `connectAnon()` to handle local address
binding, including IPv6 bracket removal and proper memory management
- **`Handlers.zig`**: Added `localAddress` and `localPort` fields to
`SocketConfig` and implemented parsing from JavaScript options
- **`Listener.zig`**: Updated connection structures to store and pass
local binding information
- **`socket.zig` (bun.js/api/bun)**: Modified `doConnect()` to extract
and pass local address options
- Updated all other call sites (HTTP, MySQL, PostgreSQL, Valkey) to pass
`null, 0` for backward compatibility
### JavaScript Layer
- **`net.ts`**: Enabled `localAddress` and `localPort` support by
passing these options to `doConnect()` and removing TODO comments
### Tests
- **`06888-localaddress.test.ts`**: Added comprehensive tests covering:
- IPv4 local address binding
- IPv4 local address and port binding
- IPv6 local address binding (loopback)
- Backward compatibility (connections without local address)
## Test Results
All tests pass successfully:
```
✓ TCP socket can bind to localAddress - IPv4
✓ TCP socket can bind to localAddress and localPort - IPv4
✓ TCP socket can bind to localAddress - IPv6 loopback
✓ TCP socket without localAddress works normally
4 pass, 0 fail
```
## API Usage
```typescript
import net from "net";
// Connect with a specific local address
const client = net.createConnection({
host: "example.com",
port: 80,
localAddress: "192.168.1.100", // Bind to this local IP
localPort: 0, // Let system assign port (optional)
});
```
## Implementation Details
The implementation follows the same flow as Node.js:
1. JavaScript options are parsed in `Handlers.zig`
2. Local address/port are stored in the connection configuration
3. The Zig layer processes and passes them to the C layer
4. The C layer parses the local address and calls `bind()` before
`connect()`
5. Both IPv4 and IPv6 addresses are supported
Memory management is handled properly throughout the stack, with
appropriate allocation/deallocation at each layer.
Closes#6888🤖 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>
### What does this PR do?
Makes sure strings are doubled quoted when they start with flow
indicators and `:`.
Fixes#23502
### How did you verify your code works?
Added tests for each indicator in flow and block context
### 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 <noreply@anthropic.com>
### What does this PR do?
Add missing error handling for directory entries errors
The code was missing a check for .err
### How did you verify your code works?
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Fixes#23474
## Summary
When `request.cookies.set()` is called before `server.upgrade()`, the
cookies are now properly included in the WebSocket upgrade response
headers.
## Problem
Previously, cookies set on the request via `req.cookies.set()` were only
written for regular HTTP responses but were ignored during WebSocket
upgrades. Users had to manually pass cookies via the `headers` option:
```js
server.upgrade(req, {
headers: {
"Set-Cookie": `SessionId=${sessionId}`,
},
});
```
## Solution
Modified `src/bun.js/api/server.zig` to check for and write cookies to
the WebSocket upgrade response after the "101 Switching Protocols"
status is set but before the actual upgrade is performed.
The fix handles both cases:
- When `upgrade()` is called without custom headers
- When `upgrade()` is called with custom headers
## Testing
Added comprehensive regression tests in
`test/regression/issue/23474.test.ts` that:
- Verify cookies are set in the upgrade response without custom headers
- Verify cookies are set in the upgrade response with custom headers
- Use `Promise.withResolvers()` for efficient async handling (no
arbitrary timeouts)
Tests confirmed:
- ❌ Fail with system bun v1.2.23 (without fix)
- ✅ Pass with debug build v1.3.0 (with fix)
## Manual verification
```bash
curl -i -H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
-H "Sec-WebSocket-Version: 13" \
http://localhost:3000/ws
```
Response now includes:
```
HTTP/1.1 101 Switching Protocols
Set-Cookie: test=123; Path=/; SameSite=Lax
Upgrade: websocket
Connection: Upgrade
...
```
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Fixes test\regression\issue\23316-long-path-spawn.test.ts
The problem was ``await Bun.write(join(deepPath, "test.js"),
`console.log("hello");`);`` was failing because the name was too long,
but it failed before refConcurrently was called and it called
unrefConcurrently after failing. so then when the subprocess spawned it
didn't ref.
---------
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>