Added detailed doc comment explaining:
- Function purpose: accepts already-open FD and attaches to server
- Parameters: file descriptor number via callframe.argument(0)
- Return behavior: js_undefined on success, throws bun.JSError on error
- Common use cases: systemd socket activation, Unix domain socket FD passing
- Error semantics: all validation checks and preconditions
Documentation follows project style with triple-slash comments and clearly
highlights what callers must provide and what errors to expect.
Addresses CodeRabbit review feedback.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced callframe.argumentsAsArray(1)[0] with callframe.argument(0)
to avoid unnecessary array allocation. Since we already validate
argumentsCount() >= 1, we can safely use direct argument access.
This is more efficient and follows the validation-then-access pattern
recommended by CodeRabbit.
All 7 tests pass with 281 expect() assertions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Addresses CodeRabbit review feedback:
1. Added explicit argument count validation using callframe.argumentsCount()
before accessing argumentsAsArray(1)[0]. This ensures proper error
handling when no arguments are provided.
2. Removed unreachable upper bound check (fd > maxInt(u32)). Since fd is
of type i32, it can never exceed maxInt(u32) (4294967295), making that
check redundant and unreachable.
The code now properly validates:
- Argument count (must have at least 1 argument)
- Argument type (must be a number)
- FD value (must be >= 0)
All 7 tests pass with 281 expect() assertions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Addresses CodeRabbit review feedback:
1. Added upper bound validation (maxInt(u32)) before @intCast to prevent
potential issues with extremely large i32 values
2. Simplified error message to be truthful about what we actually validate
locally. Changed from claiming to validate "socket FD" and "SSL
configuration" to simply reporting "Failed to accept file descriptor {d}"
The actual validation of socket type and SSL compatibility happens in
the C++ layer (us_socket_from_fd), not in this Zig code. The error
message now accurately reflects what this function checks.
All 7 tests pass with 281 expect() assertions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhanced the error message to include:
- The actual file descriptor number that failed
- Guidance on common failure causes (invalid socket FD, SSL mismatch)
This helps users diagnose issues more quickly when accept() fails.
Addresses CodeRabbit review comment.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced .arguments_old() with .argumentsAsArray() to comply with
codebase ban on .arguments_old().
- Simplified argument handling by using argumentsAsArray(1)[0]
- Removed manual length check (argumentsAsArray handles this)
- Clarified SSL documentation wording slightly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented complete SSL support following the same pattern used
throughout libuwsockets.cpp:
- Added SSL branch that uses uWS::SSLApp and HttpContext<true>
- Uses HttpResponseData<true> for SSL sockets
- Properly initializes SSL socket extensions with placement new
- Triggers on_open callback for SSL connections
- Updated documentation to reflect SSL/TLS support
Both SSL and non-SSL sockets now work correctly with server.accept().
The implementation follows the exact same pattern as all other
uWebSockets operations in libuwsockets.cpp (get, post, listen, etc).
All 7 tests pass with 281 expect() assertions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed potential hang in binary upload test if server closes connection
before 256 bytes are received:
- Added close handler that resolves promise if not already resolved
- Ensures test proceeds deterministically even if connection closes early
- Allows assertions to run and fail appropriately if response is incomplete
All 7 tests pass with 281 expect() calls.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed two more potentially flaky tests that were resolving on first data
chunk instead of waiting for complete HTTP responses:
1. Basic HTTP request test:
- Now parses Content-Length header
- Waits until full response body is received before resolving
- Adds close handler as fallback for Connection: close
2. Keep-Alive multiple requests test:
- Implements proper HTTP response parser that handles pipelined responses
- Maintains buffer and extracts complete responses one at a time
- Supports multiple responses in single data chunk
- Each response is pushed to array only when complete (headers + body)
- Properly handles Connection: close by pushing remaining buffer
Both tests now robustly handle:
- Multi-chunk responses
- Pipelined responses
- Content-Length parsing
- Connection close scenarios
All 7 tests pass with 281 expect() calls.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The large POST test was resolving as soon as headers arrived, which could
cause flaky assertions if the body hadn't fully arrived yet.
Fixed by:
- Parsing Content-Length header from response
- Tracking body bytes received
- Only resolving promise when body length matches Content-Length
- Adding close handler as fallback for Connection: close responses
This ensures assertions always run against complete HTTP responses,
eliminating potential race conditions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added detailed comment explaining why we use pointer arithmetic to
access the private httpContext member of uWS::App:
- uWebSockets is a vendored library, so we can't easily add accessors
- The approach is consistent with patterns in App.h (lines 115, 127, 132)
- The TemplatedApp memory layout (httpContext as first member) is stable
- Similar pointer arithmetic is used elsewhere in libuwsockets.cpp
This addresses the code review suggestion to use an accessor function,
while acknowledging the constraint that modifying the vendored library
is not practical. The comment documents the technical debt for future
maintainers.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
1. Remove unused #include <stdio.h> from socket.c
- stdio.h was added for debug logging but is no longer needed
2. Fix SSL branch in uws_app_accept()
- SSL/TLS socket acceptance now properly rejects with -1
- Added comment explaining proper implementation would require
us_socket_wrap_with_tls() + us_socket_open() for TLS handshake
- Avoids unsafe construction of TLS HttpResponseData on non-TLS socket
3. Add #include <new> for placement new
- Required for placement new syntax used in HttpResponseData initialization
4. Update accept() documentation with ownership semantics
- Clarifies that app takes FD ownership on success
- Caller retains ownership and must close FD on failure
- Notes that SSL/TLS is not currently supported
5. Optimize test buffer allocations
- Large POST body: Use Buffer.alloc(10000, 0x78).toString() instead of "x".repeat(10000)
- Binary upload: Use Buffer.allocUnsafe(1000) and i & 0xff for faster allocation
All tests still pass (7 pass, 279 expect() calls).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The server.accept() API is not supported on Windows because
us_socket_from_fd() is not implemented there (it returns 0 in the
Windows/libuv code path).
Socket pair tests now skip on Windows with test.todoIf(isWindows).
The API validation tests that don't use socket pairs still run on all
platforms.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds comprehensive test to verify both sending and receiving binary data
through an accepted file descriptor:
- Uploads 1000 bytes of binary data (sequential 0-255 pattern)
- Server processes the binary data and calculates checksum
- Server responds with 256 bytes of binary data (0-255)
- Client verifies binary integrity in both directions
This ensures that file descriptor acceptance works correctly for real-world
use cases like file uploads and binary protocol handling.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The issue was that server.accept() was casting the uWebSockets App
pointer directly to us_socket_context_t*, but the App is a wrapper that
contains HttpContext as its first member. The HttpContext IS the socket
context, not the App itself.
This caused the event loop file descriptor to be read incorrectly (showing
as 0 instead of the actual epoll FD), which then caused epoll_ctl to fail
with EINVAL when trying to add socket pair file descriptors.
The fix accesses the httpContext pointer (first member of App struct) via
pointer arithmetic, then casts that to us_socket_context_t*. This gives us
the correct socket context with a properly initialized event loop.
All 6 tests now pass, including tests for:
- Basic HTTP request handling
- Multiple requests with Keep-Alive
- Large POST body handling
- Invalid file descriptor handling
- Argument validation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements server.accept(fd) method that allows accepting a file descriptor
number and integrating it as an HTTP connection to the server. This enables
use cases where file descriptors are obtained externally and need to be
handled by Bun's HTTP server.
Changes:
- Add accept() method to server.classes.ts
- Implement doAccept() in server.zig with FD validation
- Add uws_app_accept() C++ wrapper in libuwsockets.cpp
- Add Zig bindings in App.zig
- Create socket from FD using us_socket_from_fd()
- Initialize HttpResponseData and trigger on_open callback
- Add basic tests in server-accept.test.ts
The method validates the file descriptor, creates a socket from it,
and runs it through the same initialization code path as regular
connections, ensuring proper HTTP request handling.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Summary
Fixes a race condition on macOS where editing the entrypoint with vim's
atomic save causes "Module not found" errors during hot reload.
## Root Cause
On macOS, kqueue watches file descriptors/inodes, not paths. Vim's
atomic save sequence:
1. Rename `a.js` to `a.js~` → kqueue reports `NOTE_RENAME` on watched fd
2. Hot reloader immediately triggers reload
3. New file hasn't been created yet → `ENOENT` error
4. Vim re-creates `a.js`, and writes file contents into it
5. Directory gets `NOTE_WRITE` but file already removed from watchlist
```
rename("a.js", "a.js~") = 0
openat(AT_FDCWD, "a.js", O_WRONLY|O_CREAT, 0664) = 3
ftruncate(3, 0) = 0
write(3, "foobar\n", 7) = 7
close(3) = 0
```
This is macOS-specific because:
- **kqueue**: watches inodes, fd becomes stale when inode deleted
- **inotify (Linux)**: watches paths, gets `IN.MOVED_TO` (not
`IN.MOVE_SELF`), so files stay in watchlist
## Solution
When the entrypoint receives `NOTE_RENAME` on macOS:
1. Set `is_waiting_for_dir_change` flag
2. Skip immediate reload
3. Wait for parent directory `NOTE_WRITE` event
4. Use `faccessat()` to verify file exists
5. Trigger reload
This only applies to the entrypoint because dependencies have buffering
time during import graph traversal.
## Test Plan
Manual testing with vim on macOS:
1. Run `bun --hot entrypoint.js`
2. Edit entrypoint with vim (`:w`)
3. Verify no "Module not found" errors
4. Verify hot reload succeeds
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: taylor.fish <contact@taylor.fish>
### 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>
Co-authored-by: Michael H <git@riskymh.dev>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Marko Vejnovic <marko@bun.com>
### What does this PR do?
Handles EXDEV correctly after first clonefile fails with ENOENT
Fixes#23579Fixes#23577
### How did you verify your code works?
Manually
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## Summary
This PR moves error-related functions from `bindings.cpp` into a new
dedicated file `ZigException.cpp` for better code organization.
## Changes
Moved the following functions to `ZigException.cpp`:
- `populateStackFrameMetadata`
- `populateStackFramePosition`
- `populateStackFrame`
- `populateStackTrace`
- `fromErrorInstance`
- `exceptionFromString`
- `JSC__JSValue__toZigException`
- `ZigException__collectSourceLines`
- `JSC__Exception__getStackTrace`
Also moved helper functions and types:
- `V8StackTraceIterator` class
- `getNonObservable`
- `PopulateStackTraceFlags` enum
- `StringView_slice` helper
- `SYNTAX_ERROR_CODE` macro
## Test plan
- Built successfully with `bun bd`
- All exception handling functions are properly exported
- No functional changes, pure refactoring
🤖 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?
Fixes the crash handler failing to capture and display stack traces on
Linux ARM64 systems.
**Before:**
```
============================================================
panic(main thread): cast causes pointer to be null
```
No stack trace shown.
**After:**
```
============================================================
panic(main thread): cast causes pointer to be null
bun.js.api.FFIObject.Reader.u8
/workspace/bun/src/bun.js/api/FFIObject.zig:67:41
bun.js.jsc.host_fn.toJSHostCall__anon_2545765
/workspace/bun/src/bun.js/jsc/host_fn.zig:93:5
```
Full stack trace with source locations.
#### Root Cause
- Zig's `std.debug.captureStackTrace` uses `StackIterator.init()` which
falls back to frame pointer-based unwinding when no context is provided
- Frame pointer-based unwinding doesn't work reliably on ARM64, even
with `-fno-omit-frame-pointer` enabled
- This resulted in 0 frames being captured (`trace.index == 0`)
#### Changes
1. **Use glibc's backtrace() on Linux**: On Linux with glibc (not musl),
always use glibc's `backtrace()` function instead of Zig's
StackIterator. glibc's implementation properly uses DWARF unwinding
information from `.eh_frame` sections.
2. **Skip crash handler frames**: After capturing with `backtrace()`,
find the desired `begin_addr` in the trace (within 128 byte tolerance)
and filter out crash handler internal frames for cleaner output. If
`begin_addr` is not found, use the complete backtrace.
3. **Preserve existing behavior**:
- Non-debug builds: Use WTF printer (fast, no external deps)
- Debug builds: Fall through to llvm-symbolizer (detailed source info)
### How did you verify your code works?
Reproduced the crash:
```bash
bun-debug --print 'Bun.FFI.read.u8(0)'
```
Verified that:
- ✅ Stack traces now appear on Linux ARM64 with proper source locations
- ✅ Crash handler frames are properly filtered out
- ✅ llvm-symbolizer integration works for debug builds
- ✅ WTF printer is used for release builds
- ✅ When begin_addr is not found, complete backtrace is used
🤖 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?
Fixes a race condition where multiple threads could attempt to
initialize JavaScriptCore concurrently when the bundler's thread pool
processes files with macros.
Fixes#23540
## How did you verify your code works?
Reproduced the segfault with the Brisa project build and verified the
fix resolves it:
```bash
git clone https://github.com/brisa-build/brisa
cd brisa
bun install
bun run build
```
Before the fix: Segmentation fault with assertion failure
After the fix: Build proceeds without crashing
## Root Cause
The previous implementation used a simple boolean flag `has_loaded_jsc`
without synchronization. When multiple bundler threads tried to execute
macros simultaneously, they could race through the initialization check
before `JSC::initialize()` finished finalizing options on another
thread.
This caused crashes with:
```
ASSERTION FAILED: g_jscConfig.options.allowUnfinalizedAccess || g_jscConfig.options.isFinalized
vendor/WebKit/Source/JavaScriptCore/runtime/Options.h(146) : static OptionsStorage::Bool &JSC::Options::forceTrapAwareStackChecks()
```
## The Fix
Replace the boolean flag with `std::call_once`, which provides:
- Thread-safe initialization
- Guaranteed exactly-once execution
- Proper memory barriers to ensure visibility across threads
The initialization code is now wrapped in a lambda passed to
`std::call_once`, capturing the necessary parameters (`evalMode`,
`envp`, `envc`, `onCrash`).
🤖 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
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>