This change completes the migration of FetchTasklet fields to MainThreadData
and SharedData structs, eliminating duplicate field storage and clarifying
data ownership and thread safety boundaries.
Changes:
- Removed 27 duplicate fields from FetchTasklet struct
- Updated 100+ access sites to use this.main_thread.X or this.shared.X
- Main thread fields: promise, global_this, vm, response, streams, abort handling
- Shared fields: HTTP client, buffers, result, metadata, ref_count, mutex
- Net reduction of 61 lines of code
All data now explicitly separated into main-thread-only and thread-safe zones,
providing clear boundaries for concurrent access patterns.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This change replaces boolean state flags with state machine checks, eliminating
scattered flag management and improving code clarity.
Changes:
- Removed is_waiting_request_stream_start flag
→ Replaced with request_stream_state == .waiting_start checks
- Removed is_waiting_abort flag
→ Replaced with abort_requested atomic loads
- Removed ignore_data flag
→ Replaced with shouldIgnoreBodyData() helper calls
- Fixed 4 broken this.lifecycle accesses to use this.shared.lifecycle
- Added proper mutex locking for lifecycle transitions in toResponse()
All state tracking now goes through the FetchLifecycle and RequestStreamState
enums, providing clear state transitions and better thread safety.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This change introduces MainThreadData and SharedData struct instances to
FetchTasklet alongside existing fields, without changing behavior. This
establishes the foundation for gradual migration to thread-safe data access.
Changes:
- Added main_thread: MainThreadData field (inline storage)
- Added shared: *SharedData field (heap-allocated)
- Added lockShared() and assertMainThread() helper methods
- Initialized both fields in get() function
- Added proper cleanup in clearData()
All existing fields remain unchanged - this is a non-breaking change that
enables incremental migration in subsequent steps.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add lifecycle and request_stream_state fields to SharedData struct with
proper default values. This is a non-breaking change that introduces the
new state machine alongside existing boolean flags.
Added:
- lifecycle: FetchLifecycle = .created (in SharedData)
- request_stream_state: RequestStreamState = .none (in SharedData)
All existing boolean flags preserved:
- ignore_data
- is_waiting_body
- is_waiting_request_stream_start
The dual state tracking allows gradual migration. Future steps will
replace boolean flag checks with state machine checks incrementally.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add FetchError union to consolidate scattered error handling into a single
type with clear precedence rules. Currently errors are spread across
result.fail, abort_reason, and body errors. This union provides a
foundation for unified error management.
Added:
- FetchError union with 5 variants (none, http_error, abort_error, js_error, tls_error)
- set() method that frees old error before setting new (prevents leaks)
- toJS() method to convert error to JSValue for promise rejection
- isAbort() helper for special abort handling
- Single deinit() cleanup for all variants
Error precedence documented:
1. abort_error (highest - user initiated)
2. js_error (JavaScript callback errors)
3. tls_error (TLS validation failures)
4. http_error (lowest - network/protocol errors)
This is a non-breaking addition. Future phases will migrate existing
error handling to use this unified storage.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive documentation for request body streaming cleanup with
explicit ownership transfer semantics. The previously obscure "initExactRefs(2)"
pattern is now fully documented with clear ref counting explanations.
Changes:
- Enhanced startRequestStream() with detailed ref counting documentation
- Renamed clearSink() to clearRequestStreaming() with full ref docs
- Added clearSink() compatibility alias for backward compatibility
- Documented which refs are held by sink, stream, and buffer
- Explained ownership transfer at each step
The "initExactRefs(2)" pattern is now explicit:
- Ref 1: FetchTasklet.sink field (our ownership)
- Ref 2: Consumed when stream pipes to sink
All cleanup paths are documented with inline comments explaining
which refs are being dropped and which remain held by other owners.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add 4 new functions to separate body streaming logic into clear state-based
handlers, replacing the complex conditionals in onBodyReceived. This makes
the code more maintainable and prepares for future SharedData migration.
Added functions:
- processBodyDataInitial() - Decides whether to buffer or stream initially
- streamBodyToJS() - Streams body data to ReadableStream (dual-path support)
- bufferBodyData() - Buffers body in memory and creates InternalBlob when done
- bufferBodyDataDirect() - Directly appends data to buffer with OOM handling
These functions are currently unused (will be integrated in next step) but
compile successfully. They handle all 3 existing code paths: direct stream,
Response body stream, and buffering.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace manual hostname buffer management with bun.ptr.Owned wrapper for
automatic RAII cleanup and explicit ownership tracking. The hostname buffer
is optionally allocated for TLS certificate validation with custom
checkServerIdentity.
Changes:
- Changed hostname field from ?[]u8 to ?bun.ptr.Owned([]u8)
- Wrap allocation with bun.ptr.Owned([]u8).fromRaw()
- Replace manual free() calls with automatic hostname.deinit()
- Unwrap with .get() when passing to HTTP client
- Add comprehensive documentation explaining ownership model
The wrapper ensures automatic cleanup via RAII pattern, eliminating manual
memory management and reducing risk of leaks in future code paths.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add AbortHandling wrapper struct to centralize abort signal lifecycle
management and eliminate scattered ref/unref operations that can cause
leaks or use-after-free bugs.
Added:
- AbortHandling struct with private state tracking fields
- #signal, #has_pending_activity_ref, #has_listener fields
- attachSignal() method for atomic setup (ref, listener, pending activity)
- detach() method for proper cleanup in correct order
- Single deinit() method calling detach()
- onAbortCallback() static method for abort event handling
The wrapper ensures all ref/unref operations are paired correctly in
single code paths. Private fields prevent external code from bypassing
lifecycle management, making ownership explicit and preventing common
memory safety bugs.
Note: onAbortCallback() retains reason parameter as required by
AbortSignal.listen() API constraint.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive documentation for url_proxy_buffer ownership pattern
instead of adding bun.ptr.Owned wrapper. The current pattern is already
clear and explicit, so documentation makes it even more obvious without
adding abstraction overhead.
Added documentation:
- Complete ownership model with 5-step lifecycle
- Buffer layout explanation (URL + optional proxy concatenated)
- Cross-references to creation site (fetch.zig) and cleanup site
- Alternative considered section explaining bun.ptr.Owned approach
- Rationale for choosing explicit pattern over wrapper
- Single cleanup path documentation in clearData()
The ownership transfer pattern (setting to "" after transfer) already
prevents double-free. Documentation makes this implicit pattern explicit
for future maintainers.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add HTTPRequestBodyV2 union to make request body ownership explicit and
eliminate fragile ref counting patterns. Each variant tracks its own
ownership state with private fields.
Added:
- HTTPRequestBodyV2 union with 4 variants (Empty, AnyBlob, Sendfile, ReadableStream)
- AnyBlob variant with nested #store_ref struct tracking blob store refs
- Sendfile variant with #owns_fd field to track FD ownership
- ReadableStream variant with #transferred_to_sink field documenting initExactRefs(2)
- transferToSink() method for explicit ownership transfer to sink
- Single deinit() method dispatching to variant-specific cleanup
- Helper methods store() and refStore() for blob ref counting
The union makes ownership clear at creation time and provides variant-specific
cleanup logic. The ReadableStream variant explicitly documents the ref counting
pattern to prevent double-retain bugs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add ResponseMetadataHolder wrapper struct to make metadata ownership
transfer explicit and ensure metadata is only transferred once to the
Response object. This prevents use-after-free and double-free bugs.
Added:
- ResponseMetadataHolder struct with private metadata and certificate fields
- takeMetadata() method for one-time ownership transfer
- takeCertificate() method for one-time ownership transfer
- setMetadata() method that frees old metadata before storing new
- setCertificate() method that frees old certificate before storing new
- Single deinit() method with null-safe cleanup
The take semantics ensure metadata can only be consumed once, with
subsequent calls returning null. This makes the ownership contract
explicit and prevents common memory safety bugs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add RequestHeaders wrapper struct to make header ownership explicit and
eliminate scattered conditional cleanup logic. This is a non-breaking
addition that encapsulates the "do I need to free this?" decision.
Added:
- RequestHeaders struct with private owned field
- initEmpty() factory for non-owned headers (no cleanup needed)
- initFromFetchHeaders() factory for owned headers (requires cleanup)
- Single deinit() method that checks ownership flag
- borrow() method to access underlying Headers
The wrapper makes ownership clear at creation time and provides a single
cleanup path, eliminating error-prone conditional logic.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Improve thread safety and reduce lock contention in HTTP thread callbacks by
making the handoff from HTTP thread to main thread explicit and minimizing
work done under lock.
HTTP Thread Improvements:
- Add fast-path abort check before acquiring lock (atomic read only)
- Move duplicate scheduling check before lock (reduces contention)
- Keep critical section brief (just data copying)
- Handle OOM gracefully under lock
- Add ref() before enqueueTaskConcurrent for proper lifetime management
Main Thread Improvements:
- Dramatically reduce lock holding time (from ~150 lines to ~30 lines)
- Copy state out under lock, then release before JS work
- Perform all JS interactions without lock (certificate validation, promise resolution)
- Add unconditional deref() at start to balance HTTP thread ref()
- Re-acquire lock briefly only when needed
Reference Counting:
- HTTP thread: ref() before each enqueue to main thread
- Main thread: unconditional defer deref() at callback start
- Prevents use-after-free and reference leaks
- Matches existing onWriteRequestDataDrain/resumeRequestDataStream pattern
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix potential memory leak and use-after-free in derefFromThread when VM is
shutting down. The function now checks VM shutdown status before enqueueing
cleanup tasks, intentionally leaking memory (detected by ASAN) rather than
risking use-after-free crashes.
Changes:
- Add VM shutdown pre-check before enqueueTaskConcurrent
- Add debug logging for intentional leaks during shutdown
- Add deinitFromMainThread helper with main thread assertion
- Early return on shutdown to prevent unsafe cleanup attempts
This is safer than attempting cleanup during VM shutdown and makes the
memory safety trade-off explicit: leak detection over undefined behavior.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix critical race condition in Bun__FetchResponse_finalize where the function
accessed shared state without acquiring the mutex, racing with the HTTP
thread's callback() which does lock the mutex.
Changes:
- Add mutex acquisition at function entry with RAII defer unlock
- Inline ignoreRemainingResponseBody logic under lock protection
- Set abort flag atomically with .release ordering
- Clear buffers safely under lock (prevents concurrent modification)
- Invert promise check logic for clearer control flow
The dual ownership pattern (response_weak + native_response) is intentional
and remains unchanged for finalization tracking.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add thread safety architecture with clear separation between main-thread-only
data and shared data that requires mutex protection. This is a non-breaking
change that adds new types without modifying existing FetchTasklet logic.
Added:
- MainThreadData struct with 12 fields for main thread only data
- SharedData struct with 14 fields for cross-thread shared data
- LockedSharedData RAII wrapper for safe mutex handling
- Comprehensive documentation of ownership and thread safety patterns
All structs are at module level for architectural clarity.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add FetchLifecycle and RequestStreamState enums to replace boolean flag
soup with explicit multi-dimensional state tracking. This is a non-breaking
change that adds new types without modifying existing logic.
Added:
- FetchLifecycle enum with 10 states and 4 helper methods
- RequestStreamState enum with 4 states
- transitionLifecycle() helper with debug assertions
- shouldIgnoreBodyData() computed property
- Comprehensive documentation and state transition examples
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Summary
Extract `FetchTasklet` struct from `src/bun.js/webcore/fetch.zig` into
its own file at `src/bun.js/webcore/fetch/FetchTasklet.zig` to improve
code organization and modularity.
## Changes
- Moved `FetchTasklet` struct definition (1336 lines) to new file
`src/bun.js/webcore/fetch/FetchTasklet.zig`
- Added all necessary imports to the new file
- Updated `fetch.zig` line 61 to import `FetchTasklet` from the new
location: `pub const FetchTasklet =
@import("./fetch/FetchTasklet.zig").FetchTasklet;`
- Verified compilation succeeds with `bun bd`
## Impact
- No functional changes - this is a pure refactoring
- Improves code organization by separating the large `FetchTasklet`
implementation
- Makes the codebase more maintainable and easier to navigate
- Reduces `fetch.zig` from 2768 lines to 1433 lines
## Test plan
- [x] Built successfully with `bun bd`
- [x] No changes to functionality - pure code organization refactor
🤖 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: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### What does this PR do?
fixes#23901
### How did you verify your code works?
with a test
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## Summary
Fixes a bug where `bun update --interactive` only updated `package.json`
but didn't actually install the updated packages. Users had to manually
run `bun install` afterwards.
## Root Cause
The bug was in `savePackageJson()` in
`src/cli/update_interactive_command.zig`:
1. The function wrote the updated `package.json` to disk
2. But it **didn't update the in-memory cache**
(`WorkspacePackageJSONCache`)
3. When `installWithManager()` ran, it called `getWithPath()` which
returned the **stale cached version**
4. So the installation proceeded with the old dependencies
## The Fix
Update the cache entry after writing to disk (line 116):
```zig
package_json.*.source.contents = new_package_json_source;
```
This matches the behavior in `updatePackageJSONAndInstall.zig` line 269.
## Test Plan
Added comprehensive regression tests in
`test/cli/update_interactive_install.test.ts`:
- ✅ Verifies that `package.json` is updated
- ✅ Verifies that `node_modules` is updated (this was failing before the
fix)
- ✅ Tests both normal update and `--latest` flag
- ✅ Compares installed version to confirm packages were actually
installed
Run tests with:
```bash
bun bd test test/cli/update_interactive_install.test.ts
```
🤖 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
Adds a `subscriptions` getter to `ServerWebSocket` that returns an array
of all topics the WebSocket is currently subscribed to.
## Implementation
- Added `getTopicsCount()` and `iterateTopics()` helpers to uWS
WebSocket
- Implemented C++ function `uws_ws_get_topics_as_js_array` that:
- Uses `JSC::MarkedArgumentBuffer` to protect values from GC
- Constructs JSArray directly in C++ for efficiency
- Uses template pattern for SSL/TCP variants
- Properly handles iterator locks with explicit scopes
- Exposed as `subscriptions` getter property on ServerWebSocket
- Returns empty array when WebSocket is closed (not null)
## API
```typescript
const server = Bun.serve({
websocket: {
open(ws) {
ws.subscribe("chat");
ws.subscribe("notifications");
console.log(ws.subscriptions); // ["chat", "notifications"]
ws.unsubscribe("chat");
console.log(ws.subscriptions); // ["notifications"]
}
}
});
```
## Test Coverage
Added 5 comprehensive test cases covering:
- Basic subscription/unsubscription flow
- All subscriptions removed
- Behavior after WebSocket close
- Duplicate subscriptions (should only appear once)
- Multiple subscribe/unsubscribe cycles
All tests pass with 24 assertions.
🤖 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: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### What does this PR do?
Allows optional peers to resolve to package if possible.
Optional peers aren't auto-installed, but they should still be given a
chance to resolve. If they're always left unresolved it's possible for
multiple dependencies on the same package to result in different peer
resolutions when they should be the same. For example, this bug this
could cause monorepos using elysia to have corrupt node_modules because
there might be more than one copy of elysia in `node_modules/.bun` (or
more than the expected number of copies).
fixes#23725
most likely fixes#23895
fixes ENG-21411
### How did you verify your code works?
Added a test for optional peers and non-optional peers that would
previously trigger this bug.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Improved resolution of optional peer dependencies during isolated
installations, with better propagation across package hierarchies.
* **Tests**
* Added comprehensive test suite covering optional peer dependency
scenarios in isolated workspaces.
* Added test fixtures for packages with peer and optional peer
dependencies.
* Enhanced lockfile migration test verification using snapshot-based
assertions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### What does this PR do?
When `napi_create_external_buffer` receives empty input, the returned
buffer should be detached.
This fixes the remaining tests in `ref-napi` other than three that use a
few uv symbols
<img width="329" height="159" alt="Screenshot 2025-11-01 at 8 38 01 PM"
src="https://github.com/user-attachments/assets/2c75f937-79c5-467a-bde3-44e45e05d9a0"
/>
### How did you verify your code works?
Added tests for correct values from `napi_get_buffer_info`,
`napi_get_arraybuffer_info`, and `napi_is_detached_arraybuffer` when
given an empty buffer from `napi_create_external_buffer`
---------
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
## Summary
Adds comprehensive documentation explaining how Bun's event loop works,
including task draining, microtasks, process.nextTick, and I/O polling
integration.
## What does this document?
- **Task draining algorithm**: Shows the exact flow for processing each
task (run → release weak refs → drain microtasks → deferred tasks)
- **Process.nextTick ordering**: Explains batching behavior - all
nextTick callbacks in current batch run, then microtasks drain
- **Microtask integration**: How JavaScriptCore's microtask queue and
Bun's nextTick queue interact
- **I/O polling**: How uSockets epoll/kqueue events integrate with the
event loop
- **Timer ordering**: Why setImmediate runs before setTimeout
- **Enter/Exit mechanism**: How the counter prevents excessive microtask
draining
## Visual aids
Includes ASCII flowcharts showing:
- Main tick flow
- autoTick flow (with I/O polling)
- Per-task draining sequence
## Code references
All explanations include specific file paths and line numbers for
verification:
- `src/bun.js/event_loop/Task.zig`
- `src/bun.js/event_loop.zig`
- `src/bun.js/bindings/ZigGlobalObject.cpp`
- `src/js/builtins/ProcessObjectInternals.ts`
- `packages/bun-usockets/src/eventing/epoll_kqueue.c`
## Examples
Includes JavaScript examples demonstrating:
- nextTick vs Promise ordering
- Batching behavior when nextTick callbacks schedule more nextTicks
- setImmediate vs setTimeout ordering
🤖 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?
If the input was empty `ArrayBuffer::createFromBytes` would create a
buffer that `JSUint8Array::create` would see as detached, so it would
throw an exception. This would likely cause crashes in `node-addon-api`
because finalize data is freed if `napi_create_external_buffer` fails,
and we already setup the finalizer.
Example of creating empty buffer:
a7f62a4caa/src/binding.cc (L687)fixes#6737fixes#10965fixes#12331fixes#12937fixes#13622
most likely fixes#14822
### How did you verify your code works?
Manually and added tests.
## Summary
Removes the `MemoryReportingAllocator` wrapper and simplifies
`fetch.zig` to use `bun.default_allocator` directly. The
`MemoryReportingAllocator` was wrapping `bun.default_allocator` to track
memory usage for reporting to the VM, but this added unnecessary
complexity and indirection without meaningful benefit.
## Changes
**Deleted:**
- `src/allocators/MemoryReportingAllocator.zig` (96 lines)
**Modified `src/bun.js/webcore/fetch.zig`:**
- Removed `memory_reporter: *bun.MemoryReportingAllocator` field from
`FetchTasklet` struct
- Removed `memory_reporter: *bun.MemoryReportingAllocator` field from
`FetchOptions` struct
- Replaced all `this.memory_reporter.allocator()` calls with
`bun.default_allocator`
- Removed all `this.memory_reporter.discard()` calls (no longer needed)
- Simplified `fetch()` function by removing memory reporter
allocation/wrapping/cleanup code
- Updated `deinit()` and `clearData()` to use `bun.default_allocator`
directly
**Cleanup:**
- Removed `MemoryReportingAllocator` export from `src/allocators.zig`
- Removed `MemoryReportingAllocator` export from `src/bun.zig`
- Removed `bun.MemoryReportingAllocator.isInstance()` check from
`src/safety/alloc.zig`
## Testing
- ✅ Builds successfully with `bun bd`
- All fetch operations now use `bun.default_allocator` directly
## Impact
- **Net -116 lines** of code
- Eliminates allocator wrapper overhead in fetch operations
- Simplifies memory management code
- No functional changes to fetch behavior
🤖 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?
calling `loadConfigPath` could allow loading bunfig more than once.
### How did you verify your code works?
manually
---------
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
## Summary
This PR makes `bun list` an alias for `bun pm ls`, allowing users to
list their dependency tree with a shorter command.
## Changes
- Updated `src/cli.zig` to route `list` command to
`PackageManagerCommand` instead of `ReservedCommand`
- Modified `src/cli/package_manager_command.zig` to detect when `bun
list` is invoked directly and treat it as `ls`
- Updated help text in `bun pm --help` to show both `bun list` and `bun
pm ls` as valid options
## Implementation Details
The implementation follows the same pattern used for `bun whoami`, which
is also a direct alias to a pm subcommand. When `bun list` is detected,
it's internally converted to the `ls` subcommand.
## Testing
Tested locally:
- ✅ `bun list` shows the dependency tree
- ✅ `bun list --all` works correctly with the `--all` flag
- ✅ `bun pm ls` continues to work (backward compatible)
## Test Output
```bash
$ bun list
/tmp/test-bun-list node_modules (3)
└── react@18.3.1
$ bun list --all
/tmp/test-bun-list node_modules
├── js-tokens@4.0.0
├── loose-envify@1.4.0
└── react@18.3.1
$ bun pm ls
/tmp/test-bun-list node_modules (3)
└── react@18.3.1
```
🤖 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
Fixes CPU profiler generating invalid timestamps that Chrome DevTools
couldn't parse (though VSCode's profiler viewer accepted them).
## The Problem
CPU profiles generated by `--cpu-prof` had timestamps that were either:
1. Negative (in the original broken profile from the gist)
2. Truncated/corrupted (after initial timestamp calculation fix)
Example from the broken profile:
```json
{
"startTime": -822663297,
"endTime": -804820609
}
```
After initial fix, timestamps were positive but still wrong:
```json
{
"startTime": 1573519100, // Should be ~1761784720948727
"endTime": 1573849434
}
```
## Root Cause
**Primary Issue**: `WTF::JSON::Object::setInteger()` has precision
issues with large values (> 2^31). When setting timestamps like
`1761784720948727` (microseconds since Unix epoch - 16 digits), the
method was truncating/corrupting them.
**Secondary Issue**: The timestamp calculation logic needed
clarification - now explicitly uses the earliest sample's wall clock
time as startTime and calculates a consistent wallClockOffset.
## The Fix
### src/bun.js/bindings/BunCPUProfiler.cpp
Changed from `setInteger()` to `setDouble()` for timestamp
serialization:
```cpp
// Before (broken):
json->setInteger("startTime"_s, static_cast<long long>(startTime));
json->setInteger("endTime"_s, static_cast<long long>(endTime));
// After (fixed):
json->setDouble("startTime"_s, startTime);
json->setDouble("endTime"_s, endTime);
```
JSON `Number` type can precisely represent integers up to 2^53 (~9
quadrillion), which is far more than needed for microsecond timestamps
(~10^15 for current dates).
Also clarified the timestamp calculation to use `wallClockStart`
directly as the profile's `startTime` and calculate a `wallClockOffset`
for converting stopwatch times to wall clock times.
### test/cli/run/cpu-prof.test.ts
Added validation that timestamps are:
- Positive
- In microseconds (> 1000000000000000, < 3000000000000000)
- Within valid Unix epoch range
## Testing
```bash
bun bd test test/cli/run/cpu-prof.test.ts
```
All tests pass ✅
Generated profile now has correct timestamps:
```json
{
"startTime": 1761784720948727.2,
"endTime": 1761784721305814
}
```
## Why VSCode Worked But Chrome DevTools Didn't
- **VSCode**: Only cares about relative timing (duration = endTime -
startTime), doesn't validate absolute timestamp ranges
- **Chrome DevTools**: Expects timestamps in microseconds since Unix
epoch (positive, ~16 digits), fails validation when timestamps are
negative, too small, or out of valid range
## References
- Gist with CPU profile format documentation:
https://gist.github.com/Jarred-Sumner/2c12da481845e20ce6a6175ee8b05a3e
- Chrome DevTools Protocol - Profiler:
https://chromedevtools.github.io/devtools-protocol/tot/Profiler/🤖 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>
## Summary
Implements the `--cpu-prof` CLI flag for Bun to profile CPU usage and
save results in Chrome CPU Profiler JSON format, compatible with Chrome
DevTools and VSCode.
## Implementation Details
- Uses JSC's `SamplingProfiler` to collect CPU samples during execution
- Converts samples to Chrome CPU Profiler JSON format on exit
- Supports `--cpu-prof-name` to customize output filename
- Supports `--cpu-prof-dir` to specify output directory
- Default filename: `CPU.YYYYMMDD.HHMMSS.PID.0.001.cpuprofile`
## Key Features
✅ **Chrome DevTools Compatible** - 100% compatible with Node.js CPU
profile format
✅ **Absolute Timestamps** - Uses wall clock time (microseconds since
epoch)
✅ **1ms Sampling** - Matches Node.js sampling frequency for comparable
granularity
✅ **Thread-Safe** - Properly shuts down background sampling thread
before processing
✅ **Memory-Safe** - Uses HeapIterationScope and DeferGC for safe heap
access
✅ **Cross-Platform** - Compiles on Windows, macOS, and Linux with proper
path handling
## Technical Challenges Solved
1. **Heap Corruption** - Fixed by calling `profiler->shutdown()` before
processing traces
2. **Memory Safety** - Added `HeapIterationScope` and `DeferGC` when
accessing JSCells
3. **Timestamp Accuracy** - Explicitly start stopwatch and convert to
absolute wall clock time
4. **Path Handling** - Used `bun.path.joinAbsStringBufZ` with proper cwd
resolution
5. **Windows Support** - UTF-16 path conversion for Windows
compatibility
6. **Atomic Writes** - Used `bun.sys.File.writeFile` with ENOENT retry
## Testing
All tests pass (4/4):
- ✅ Generates profile with default name
- ✅ `--cpu-prof-name` sets custom filename
- ✅ `--cpu-prof-dir` sets custom directory
- ✅ Profile captures function names
Verified format compatibility:
- JSON structure matches Node.js exactly
- All samples reference valid nodes
- Timestamps use absolute microseconds since epoch
- Cross-platform compilation verified with `bun run zig:check-all`
## Example Usage
```bash
# Basic usage
bun --cpu-prof script.js
# Custom filename
bun --cpu-prof --cpu-prof-name my-profile.cpuprofile script.js
# Custom directory
bun --cpu-prof --cpu-prof-dir ./profiles script.js
```
Output can be opened in Chrome DevTools (Performance → Load Profile) or
VSCode's CPU profiling viewer.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
This PR improves snapshot error messages when running tests in CI
environments to make debugging easier by showing exactly what snapshot
was being created and what value was attempted.
## Changes
### 1. Inline Snapshot Errors
**Before:**
```
Updating inline snapshots is disabled in CI environments unless --update-snapshots is used.
```
**After:**
```
Inline snapshot creation is not allowed in CI environments unless --update-snapshots is used.
If this is not a CI environment, set the environment variable CI=false to force allow.
Received: this is new
```
- Changed message to say "creation" instead of "updating" (more
accurate)
- Shows the received value that was attempted using Jest's pretty
printer
### 2. Snapshot File Errors
**Before:**
```
Snapshot creation is not allowed in CI environments unless --update-snapshots is used
If this is not a CI environment, set the environment variable CI=false to force allow.
Received: this is new
```
**After:**
```
Snapshot creation is not allowed in CI environments unless --update-snapshots is used
If this is not a CI environment, set the environment variable CI=false to force allow.
Snapshot name: "new snapshot 1"
Received: this is new
```
- Now shows the snapshot name that was being looked for
- Shows the received value using Jest's pretty printer
## Implementation Details
- Added `last_error_snapshot_name` field to `Snapshots` struct to pass
snapshot name from `getOrPut()` to error handler
- Removed unreachable code path for inline snapshot updates (mismatches
error earlier with diff)
- Updated test expectations in `ci-restrictions.test.ts`
## Test Plan
```bash
# Test inline snapshot creation in CI
cd /tmp/snapshot-test
echo 'import { test, expect } from "bun:test";
test("new inline snapshot", () => {
expect("this is new").toMatchInlineSnapshot();
});' > test.js
GITHUB_ACTIONS=1 bun test test.js
# Test snapshot file creation in CI
echo 'import { test, expect } from "bun:test";
test("new snapshot", () => {
expect("this is new").toMatchSnapshot();
});' > test2.js
GITHUB_ACTIONS=1 bun test test2.js
```
Both should show improved error messages with the received values and
snapshot name.
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: pfg <pfg@pfg.pw>
## Summary
Fix the source index bounds check in `src/sourcemap/Mapping.zig` to
correctly validate indices against the range `[0, sources_count)`.
## Changes
- Changed the bounds check condition from `source_index > sources_count`
to `source_index >= sources_count` on line 452
- This prevents accepting `source_index == sources_count`, which would
be out of bounds when indexing into the sources array
## Test plan
- [x] Built successfully with `bun bd`
- The existing test suite should continue to 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>
## Summary
Fixes#24147
- Fixed EventEmitter crash when `removeAllListeners()` is called from
within an event handler while a `removeListener` meta-listener is
registered
- Added undefined check before iterating over listeners array to match
Node.js behavior
- Added comprehensive regression tests
## Bug Description
When `removeAllListeners(type)` was called:
1. From within an event handler
2. While a `removeListener` meta-listener was registered
3. For an event type with no listeners
It would crash with: `TypeError: undefined is not an object (evaluating
'this._events')`
## Root Cause
The `removeAllListeners` function tried to access `listeners.length`
without checking if `listeners` was defined first. When called with an
event type that had no listeners, `events[type]` returned `undefined`,
causing the crash.
## Fix
Added a check `if (listeners !== undefined)` before iterating, matching
the behavior in Node.js core:
https://github.com/nodejs/node/blob/main/lib/events.js#L768
## Test plan
- ✅ Created regression test in `test/regression/issue/24147.test.ts`
- ✅ Verified test fails with `USE_SYSTEM_BUN=1 bun test` (reproduces
bug)
- ✅ Verified test passes with `bun bd test` (confirms fix)
- ✅ Test covers the exact reproduction case from the issue
- ✅ Additional tests for edge cases (actual listeners, nested calls)
🤖 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
Fixes the comparator function in `src/sourcemap/Mapping.zig` to use
strict weak ordering as required by sort algorithms.
## Changes
- Changed `<=` to `<` in the column comparison to ensure strict ordering
- Refactored the comparator to use clearer if-statement structure
- Added index comparison as a tiebreaker for stable sorting when both
line and column positions are equal
## Problem
The original comparator used `<=` which would return true for equal
elements, violating the strict weak ordering requirement. This could
lead to undefined behavior in sorting.
**Before:**
```zig
return a.lines.zeroBased() < b.lines.zeroBased() or (a.lines.zeroBased() == b.lines.zeroBased() and a.columns.zeroBased() <= b.columns.zeroBased());
```
**After:**
```zig
if (a.lines.zeroBased() != b.lines.zeroBased()) {
return a.lines.zeroBased() < b.lines.zeroBased();
}
if (a.columns.zeroBased() != b.columns.zeroBased()) {
return a.columns.zeroBased() < b.columns.zeroBased();
}
return a_index < b_index;
```
## Test plan
- [x] Verified compilation with `bun bd`
- The sort now properly follows strict weak ordering semantics
🤖 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 refactors the sourcemap module by extracting large structs from
`src/sourcemap/sourcemap.zig` into their own dedicated files, improving
code organization and maintainability.
## Changes
- **Extracted `ParsedSourceMap` struct** to
`src/sourcemap/ParsedSourceMap.zig`
- Made `SourceContentPtr` and related methods public
- Made `standaloneModuleGraphData` public for external access
- **Extracted `Chunk` struct** to `src/sourcemap/Chunk.zig`
- Added import for `appendMappingToBuffer` from parent module
- Includes all nested types: `VLQSourceMap`, `NewBuilder`, `Builder`
- **Extracted `Mapping` struct** to `src/sourcemap/Mapping.zig`
- Added necessary imports: `assert`, `ParseResult`, `debug`
- Includes nested types: `MappingWithoutName`, `List`, `Lookup`
- **Updated `src/sourcemap/sourcemap.zig`**
- Replaced struct definitions with imports:
`@import("./StructName.zig")`
- Maintained all public APIs
All structs now follow the `const StructName = @This()` pattern for
top-level declarations.
## Testing
- ✅ Compiled successfully with `bun bd`
- ✅ All existing functionality preserved
- ✅ No API changes - fully backwards compatible
## Before
- Single 2000+ line file with multiple large structs
- Difficult to navigate and maintain
## After
- Modular structure with separate files for each major struct
- Easier to find and modify specific functionality
- Better code organization
🤖 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: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Should fix https://github.com/oven-sh/bun/issues/24104
### What does this PR do?
This PR is changing `ERR_BODY_ALREADY_USED` to be TypeError instead of
Error.
### How did you verify your code works?
A test case added to verify that request call correctly throws a
TypeError after another request call on the same Request, confirming the
fix addresses the issue.
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## Summary
Fixes#19111
This PR fixes a bug where `fs.createReadStream().pipe(ServerResponse)`
would fail to transfer data when ServerResponse had no handle
(standalone usage). This affected Vite's static file serving and other
middleware adapters using the connect-to-web pattern.
## Root Cause
The bug was in the `ServerResponse.writableNeedDrain` getter at line
1529 of `_http_server.ts`:
```typescript
return !this.destroyed && !this.finished && (this[kHandle]?.bufferedAmount ?? 1) !== 0;
```
When `ServerResponse` had no handle (which is common in middleware
scenarios), the nullish coalescing operator defaulted `bufferedAmount`
to **1** instead of **0**. This caused `writableNeedDrain` to always
return `true`.
## Impact
When `pipe()` checks `dest.writableNeedDrain === true`, it immediately
pauses the source stream to handle backpressure. With the bug,
standalone ServerResponse instances always appeared to need draining,
causing piped streams to pause and never resume.
## Fix
Changed the default value from `1` to `0`:
```typescript
return !this.destroyed && !this.finished && (this[kHandle]?.bufferedAmount ?? 0) !== 0;
```
## Test Plan
- ✅ Added regression test in `test/regression/issue/19111.test.ts`
- ✅ Verified fix with actual Vite middleware reproduction
- ✅ Confirmed behavior matches Node.js
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
Refactors `Subprocess` to use explicit strong/weak reference management
via `JSRef` instead of the `hasPendingActivity` mechanism that relies on
JSC's internal `WeakHandleOwner`.
## Changes
### Core Refactoring
- **JSRef.zig**: Added `update()` method to update references in-place
- **subprocess.zig**: Changed `this_jsvalue: JSValue` to `this_value:
JSRef`
- **subprocess.zig**: Renamed `hasPendingActivityNonThreadsafe()` to
`computeHasPendingActivity()`
- **subprocess.zig**: Updated `updateHasPendingActivity()` to
upgrade/downgrade `JSRef` based on pending activity
- **subprocess.zig**: Removed `hasPendingActivity()` C callback function
- **subprocess.zig**: Updated `finalize()` to call
`this_value.finalize()`
- **BunObject.classes.ts**: Set `hasPendingActivity: false` for
Subprocess
- **Writable.zig**: Updated references from `this_jsvalue` to
`this_value.tryGet()`
- **ipc.zig**: Updated references from `this_jsvalue` to
`this_value.tryGet()`
## How It Works
**Before**: Used `hasPendingActivity: true` which created a `JSC::Weak`
reference with a `JSC::WeakHandleOwner` that kept the object alive as
long as the C callback returned true.
**After**: Uses `JSRef` with explicit lifecycle management:
1. Starts with a **weak** reference when subprocess is created
2. Immediately calls `updateHasPendingActivity()` after creation
3. **Upgrades to strong** reference when `computeHasPendingActivity()`
returns true:
- Subprocess hasn't exited
- Has active stdio streams
- Has active IPC connection
4. **Downgrades to weak** reference when all activity completes
5. GC can collect the subprocess once it's weak and no other references
exist
## Benefits
- Explicit control over subprocess lifecycle instead of relying on JSC's
internal mechanisms
- Clearer semantics: strong reference = "keep alive", weak reference =
"can be GC'd"
- Removes dependency on `WeakHandleOwner` callback overhead
## Testing
- ✅ `test/js/bun/spawn/spawn.ipc.test.ts` - All 4 tests pass
- ✅ `test/js/bun/spawn/spawn-stress.test.ts` - All tests pass (100
iterations)
- ⚠️ `test/js/bun/spawn/spawnSync.test.ts` - 3/6 pass (3 pre-existing
timing-based failures unrelated to this change)
Manual testing confirms:
- Subprocess is kept alive without user reference while running
- Subprocess can be GC'd after completion
- IPC keeps subprocess alive correctly
- No crashes or memory leaks
🤖 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: Jarred Sumner <jarred@jarredsumner.com>
## Summary
- Adds detailed documentation explaining JSRef's intended usage
- Includes a complete example showing common patterns
- Explains the three states (weak, strong, finalized)
- Provides guidelines on when to use strong vs weak references
- References real examples from the codebase (ServerWebSocket,
UDPSocket, MySQLConnection, ValkeyClient)
## Motivation
JSRef is a critical type for managing JavaScript object references from
native code, but it lacked comprehensive documentation explaining its
usage patterns and lifecycle management. This makes it clearer how to
properly use JSRef to:
- Safely maintain references to JS objects from native code
- Control whether references prevent garbage collection
- Manage the upgrade/downgrade pattern based on object activity
## Test plan
Documentation-only change, no functional changes.
🤖 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>