### What does this PR do?
Refactored NAPI property and element access to use inline methods and
improved error handling. Added comprehensive tests for default value
behavior and numeric string key operations in NAPI, ensuring correct
handling of missing properties, integer keys, and property deletion.
Updated TypeScript tests to cover new scenarios.
### How did you verify your code works?
Tests
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Summary
Fixes 100% CPU usage on idle WebSocket servers between bun-v1.2.23 and
bun-v1.3.0.
Many users reported WebSocket server CPU usage jumping to 100% on idle
connections after upgrading to v1.3.0. Investigation revealed a missing
`poll_ref.unref()` call in the WebSocket upgrade path.
## Root Cause
In commit 625e537f5d (#23348), the `OnBeforeOpen` callback mechanism was
removed as part of refactoring the WebSocket upgrade process. However,
this callback contained a critical cleanup step:
```zig
defer ctx.this.poll_ref.unref(ctx.globalObject.bunVM());
```
When a `NodeHTTPResponse` is created, `poll_ref.ref()` is called (line
314) to keep the event loop alive while handling the HTTP request. After
a WebSocket upgrade, the HTTP response object is no longer relevant and
its `poll_ref` must be unref'd to indicate the request processing is
complete.
Without this unref, the event loop maintains an active reference even
after the upgrade completes, causing the CPU to spin at 100% waiting for
events on what should be an idle connection.
## Changes
- Added `poll_ref.unref()` call in `NodeHTTPResponse.upgrade()` after
setting the `upgraded` flag
- Added regression test to verify event loop properly exits after
WebSocket upgrade
## Test Plan
- [x] Code compiles successfully
- [x] Existing WebSocket tests pass
- [x] Manual testing confirms CPU usage returns to normal on idle
WebSocket connections
## Related Issues
Fixes issue reported by users between bun-v1.2.23 and bun-v1.3.0
regarding 100% CPU usage on idle WebSocket servers.
🤖 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>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### What does this PR do?
Restore call to us_get_default_ca_certificates, and
X509_STORE_set_default_paths
Fixes https://github.com/oven-sh/bun/issues/23735
### How did you verify your code works?
Manually test running:
```bash
bun -e "await fetch('https://secure-api.eloview.com').then(res => res.t
ext()).then(console.log);"
```
should not result in:
```js
error: unable to get local issuer certificate
path: "https://secure-api.eloview.com/",
errno: 0,
code: "UNABLE_TO_GET_ISSUER_CERT_LOCALLY"
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Enhanced system root certificate handling to ensure consistent
validation across all secure connections.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
### What does this PR do?
Adds `"configVersion"` to bun.lock(b). The version will be used to keep
default settings the same if they would be breaking across bun versions.
fixes ENG-21389
fixes ENG-21388
### How did you verify your code works?
TODO:
- [ ] new project
- [ ] existing project without configVersion
- [ ] existing project with configVersion
- [ ] same as above but with bun.lockb
- [ ] configVersion@0 defaults to hoisted linker
- [ ] new projects use isolated linker
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
## Summary
This PR introduces a new postinstall optimization system that
significantly reduces the need to run lifecycle scripts for certain
packages by intelligently handling their requirements at install time.
## Key Features
### 1. Native Binlink Optimization
When packages like `esbuild` ship platform-specific binaries as optional
dependencies, we now:
- Detect the native binlink pattern (enabled by default for `esbuild`)
- Find the matching platform-specific dependency based on target CPU/OS
- Link binaries directly from the platform-specific package (e.g.,
`@esbuild/darwin-arm64`)
- Fall back gracefully if the platform-specific package isn't found
**Result**: No postinstall scripts needed for esbuild and similar
packages.
### 2. Lifecycle Script Skipping
For packages like `sharp` that run heavy postinstall scripts:
- Skip lifecycle scripts entirely (enabled by default for `sharp`)
- Prevents downloading large binaries or compiling native code
unnecessarily
- Reduces install time and potential failures in restricted environments
## Configuration
Both features can be configured via `package.json`:
```json
{
"nativeDependencies": ["esbuild", "my-custom-package"],
"ignoreScripts": ["sharp", "another-package"]
}
```
Set to empty arrays to disable defaults:
```json
{
"nativeDependencies": [],
"ignoreScripts": []
}
```
Environment variable overrides:
- `BUN_FEATURE_FLAG_DISABLE_NATIVE_DEPENDENCY_LINKER=1` - disable native
binlink
- `BUN_FEATURE_FLAG_DISABLE_IGNORE_SCRIPTS=1` - disable script ignoring
## Implementation Details
### Core Components
- **`postinstall_optimizer.zig`**: New file containing the optimizer
logic
- `PostinstallOptimizer` enum with `native_binlink` and `ignore`
variants
- `List` type to track optimization strategies per package hash
- Defaults for `esbuild` (native binlink) and `sharp` (ignore)
- **`Bin.Linker` changes**: Extended to support separate target paths
- `target_node_modules_path`: Where to find the actual binary
- `target_package_name`: Name of the package containing the binary
- Fallback logic when native binlink optimization fails
### Modified Components
- **PackageInstaller.zig**: Checks optimizer before:
- Enqueueing lifecycle scripts
- Linking binaries (with platform-specific package resolution)
- **isolated_install/Installer.zig**: Similar checks for isolated linker
mode
- `maybeReplaceNodeModulesPath()` resolves platform-specific packages
- Retry logic without optimization on failure
- **Lockfile**: Added `postinstall_optimizer` field to persist
configuration
## Changes Included
- Updated `esbuild` from 0.21.5 to 0.25.11 (testing with latest)
- VS Code launch config updates for debugging install with new flags
- New feature flags in `env_var.zig`
## Test Plan
- [x] Existing install tests pass
- [ ] Test esbuild install without postinstall scripts running
- [ ] Test sharp install with scripts skipped
- [ ] Test custom package.json configuration
- [ ] Test fallback when platform-specific package not found
- [ ] Test feature flag overrides
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Native binlink optimization: installs platform-specific binaries when
available, with a safe retry fallback and verbose logging option.
* Per-package postinstall controls to optionally skip lifecycle scripts.
* New feature flags to disable native binlink optimization and to
disable lifecycle-script ignoring.
* **Tests**
* End-to-end tests and test packages added to validate native binlink
behavior across install scenarios and linker modes.
* **Documentation**
* Bench README and sample app migrated to a Next.js-based setup.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
Fixes Next.js 16 + React Compiler build failure when using Bun runtime.
## Issue
When `Module._resolveFilename` was overridden (e.g., by Next.js's
require-hook), Bun was not passing the `options` parameter (which
contains `paths`) to the override function. This caused resolution
failures when the override tried to use custom resolution paths.
Additionally, when `Module._resolveFilename` was called directly with
`options.paths`, Bun was ignoring the paths parameter and using default
resolution.
## Root Causes
1. In `ImportMetaObject.cpp`, when calling an overridden
`_resolveFilename` function, the options object with paths was not being
passed as the 4th argument.
2. In `NodeModuleModule.cpp`, `jsFunctionResolveFileName` was calling
`Bun__resolveSync` without extracting and using the `options.paths`
parameter.
## Solution
1. In `ImportMetaObject.cpp`: When `userPathList` is provided, construct
an options object with `{paths: userPathList}` and pass it as the 4th
argument to the overridden `_resolveFilename` function.
2. In `NodeModuleModule.cpp`: Extract `options.paths` from the 4th
argument and call `Bun__resolveSyncWithPaths` when paths are provided,
instead of always using `Bun__resolveSync`.
## Reproduction
Before this fix, running:
```bash
bun --bun next build --turbopack
```
on a Next.js 16 app with React Compiler enabled would fail with:
```
Cannot find module './node_modules/babel-plugin-react-compiler'
```
## Testing
- Added comprehensive tests for `Module._resolveFilename` with
`options.paths`
- Verified Next.js 16 + React Compiler + Turbopack builds successfully
with Bun
- All 5 new tests pass with the fix, 3 fail without it
- All existing tests continue to pass
## Files Changed
- `src/bun.js/bindings/ImportMetaObject.cpp` - Pass options to override
- `src/bun.js/modules/NodeModuleModule.cpp` - Handle options.paths in
_resolveFilename
- `test/js/node/module/module-resolve-filename-paths.test.js` - New test
suite
🤖 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
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?
### How did you verify your code works?
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved HTTP connection handling during write failures to ensure more
reliable timeout behavior and connection state management.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
### 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
Fixed two bugs in the auto-close-duplicates bot:
- **Respect 👎 reactions from ANY user**: Previously only the issue
author's thumbs down would prevent auto-closing. Now any user can
indicate disagreement with the duplicate detection.
- **Don't re-close reopened issues**: The bot now checks if an issue was
previously reopened and skips auto-closing to respect user intent.
## Changes
1. Modified `fetchAllReactions` call to check all reactions, not just
the author's
2. Changed `authorThumbsDown` logic to `hasThumbsDown` (checks any
user's reaction)
3. Added `wasIssueReopened()` function to query issue events timeline
4. Added check to skip issues with "reopened" events in their history
## Test plan
- [ ] Manually test the script doesn't close issues with 👎 reactions
from non-authors
- [ ] Verify reopened issues are not auto-closed again
- [ ] Check that legitimate duplicates without objections still get
closed properly
🤖 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
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>
exploratory look into https://github.com/oven-sh/bun/issues/1524
this still leaves that far off from being closed but an important first
step
this is important because this script is used to spawn our base images
for CI and will provide boxes for local testing
not sure how far i'll get but a rough "road to freebsd" map for anyone
reading:
- [x] this
- [ ] ensure `bootstrap.sh` can run successfully
- [ ] ensure WebKit can build from source
- [ ] ensure other dependencies can build from source
- [ ] add freebsd to our WebKit fork releases
- [ ] add freebsd to our Zig fork releases
- [ ] ensure bun can build from source
- [ ] run `[build images]` and add freebsd to CI
- [ ] fix runtime test failures
<img width="2072" height="956" alt="image"
src="https://github.com/user-attachments/assets/ea1acf45-b746-4ffa-8043-be674b87bb60"
/>
## 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>
### What does this PR do?
Previously, `JSC__JSPromise__wrap` would call
`JSC::JSPromise::resolvedPromise(globalObject, result)` without checking
if an exception was thrown during promise resolution. This
could happen in certain edge cases, such as when the result value is a
thenable that triggers stack overflow, or when the promise resolution
mechanism itself encounters an error.
When such exceptions occurred, they would escape back to the Zig code,
causing the CatchScope assertion to fail with "ASSERTION FAILED:
Unexpected exception observed on thread"
instead of being properly handled.
This PR adds an exception check immediately after calling
`JSC::JSPromise::resolvedPromise()` and before the `RELEASE_AND_RETURN`
macro. If an exception is detected, the function
now clears it and returns a rejected promise with the exception value,
ensuring consistent error handling behavior. This matches the pattern
already used earlier in the function
for the initial function call exception handling.
### How did you verify your code works?
new and existing tests
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## Summary
Fixes#23133
This PR fixes a bug where lifecycle hooks (`beforeAll`, `beforeEach`,
`afterAll`, `afterEach`) would throw an error when called with a
function and options object:
```typescript
beforeAll(() => {
console.log("beforeAll")
}, { timeout: 10_000 })
```
Previously, this would throw: `error: beforeAll() expects a function as
the second argument`
## Root Cause
The issue was in `ScopeFunctions.parseArguments()` at
`src/bun.js/test/ScopeFunctions.zig:342`. When parsing two arguments, it
always treated them as `(description, callback)` instead of checking if
they could be `(callback, options)`.
## Solution
Updated the two-argument parsing logic to check if the first argument is
a function and the second is not a function. In that case, treat them as
`(callback, options)` instead of `(description, callback)`.
## Changes
- Modified `src/bun.js/test/ScopeFunctions.zig` to handle `(callback,
options)` case
- Added regression test at `test/regression/issue/23133.test.ts`
## Testing
✅ Verified the fix works with the reproduction case from the issue
✅ Added comprehensive regression test covering all lifecycle hooks with
both object and numeric timeout options
✅ All existing jest-hooks tests still pass
✅ Test fails with `USE_SYSTEM_BUN=1` and passes with the fixed build
🤖 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>
## Summary
Fixes#20689
Previously, `@layer` blocks were not being processed through the CSS
minifier, which meant that `color-scheme` properties inside `@layer`
blocks would not get the required `--buncss-light`/`--buncss-dark`
variable injections needed for browsers that don't support the
`light-dark()` function.
## Changes
- Implemented proper minification for `LayerBlockRule` in
`src/css/rules/rules.zig:218-221`
- Added recursive call to `minify()` on nested rules, matching the
behavior of other at-rules like `@media` and `@supports`
- Added comprehensive tests for `color-scheme` inside `@layer` blocks
## Test Plan
Added three new test cases in `test/js/bun/css/css.test.ts`:
1. Simple `@layer` with `color-scheme: dark`
2. Named layers (`@layer shm.colors`) with multiple rules
3. Anonymous `@layer` with `color-scheme: light dark` (generates media
query)
All tests pass:
```bash
bun bd test test/js/bun/css/css.test.ts -t "color-scheme"
```
## Before
```css
/* Input */
@layer shm.colors {
body.theme-dark {
color-scheme: dark;
}
}
/* Output (broken - no variables) */
@layer shm.colors {
body.theme-dark {
color-scheme: dark;
}
}
```
## After
```css
/* Input */
@layer shm.colors {
body.theme-dark {
color-scheme: dark;
}
}
/* Output (fixed - variables injected) */
@layer shm.colors {
body.theme-dark {
--buncss-light: ;
--buncss-dark: initial;
color-scheme: dark;
}
}
```
🤖 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>
## Summary
Implements `onTestFinished()` for `bun:test`, which runs after all
`afterEach` hooks have completed.
## Implementation
- Added `onTestFinished` export to the test module in `jest.zig`
- Modified `genericHook` in `bun_test.zig` to handle `onTestFinished` as
a special case that:
- Can only be called inside a test (not in describe blocks or preload)
- Appends hooks at the very end of the execution sequence
- Added comprehensive tests covering basic ordering, multiple callbacks,
async callbacks, and interaction with other hooks
## Execution Order
When called inside a test:
1. Test body executes
2. `afterAll` hooks (if added inside the test)
3. `afterEach` hooks
4. `onTestFinished` hooks ✨
## Test Plan
- ✅ All new tests pass with `bun bd test`
- ✅ Tests correctly fail with `USE_SYSTEM_BUN=1` (feature not in
released version)
- ✅ Verifies correct ordering with `afterEach`, `afterAll`, and multiple
`onTestFinished` calls
- ✅ Tests async `onTestFinished` callbacks
🤖 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>
Co-authored-by: pfg <pfg@pfg.pw>
### What does this PR do?
This PR adds documentation comments to `src/env_var.zig` that explain
the silent error handling behavior for environment variable
deserialization, based on the documentation from the closed PR #24036.
The comments clarify:
1. **Module-level documentation**: Environment variables may fail to
parse silently. When they do, the default behavior is to show a debug
warning and treat them as not set. This is intentional to avoid panics
from environment variable pollution.
2. **Inline documentation**: Deserialization errors cannot panic. Users
needing more robust configuration mechanisms should consider
alternatives to environment variables.
This documentation complements the behavior change introduced in commit
0dd6aa47ea which replaced panic with debug_warn.
### How did you verify your code works?
Ran `bun bd` successfully - the build completed without errors.
🤖 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?
When `process.nextTick` is overwritten, segv will be occured via
internal `processTick` call.
This patch fixes it.
### How did you verify your code works?
Tests.
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
When using `fs.watch()` with `recursive: true`, the callback receives a
relative path from the watched directory (e.g., `'subdir/file.txt'`),
not just a filename.
Renaming the parameter from `filename` to `relativePath` makes this
behavior immediately clear to developers.
**Before:**
```ts
(event, filename) => {
console.log(`Detected ${event} in ${filename}`);
}
```
**After:**
```ts
(event, relativePath) => {
console.log(`Detected ${event} in ${relativePath}`);
}
```
This is a documentation-only change that improves clarity without
altering any functionality.
Co-authored-by: Braden Wong <git@bradenwong.com>
### Description
This PR fixes a crash caused by integer underflow in
`OKPacket.decodeInternal`.
Previously, when `read_size` exceeded `packet_size`, the subtraction
`packet_size - read_size` wrapped around, producing a huge `count` value
passed into `reader.read()`. This led to an integer overflow panic at
runtime.
### What does this PR do
- Added a safe subtraction guard in `decodeInternal` to clamp
`remaining` to `0`
when `read_size >= packet_size`.
- Ensures empty or truncated OK packets no longer cause crashes.
- Behavior for valid packets remains unchanged.
### Impact
Prevents integer overflow panics in MySQL OK packet parsing, improving
stability when handling short or empty responses (e.g., queries that
return no rows or minimal metadata).
### How did you verify your code works?
Tested with proof of concept:
https://github.com/Lillious/Bun-MySql-Integer-Overflow-PoC
---------
Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
`ZigString.Slice.cloneIfNeeded` does *not* guarantee that the returned
slice will have been allocated by the provided allocator, which makes it
very easy to use this method incorrectly.
(For internal tracking: fixes ENG-21284)
### What does this PR do?
Adds missing null checking for `Bun.CookieMap#delete`.
### How did you verify your code works?
Tests
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
fixes: oven-sh/bun#23717
### What does this PR do?
- Align ProxyTunnel.onClose with
[HTTPClient.onClose](https://github.com/oven-sh/bun/blob/bun-v1.3.0/src/http.zig#L223-L241):
when a tunneled HTTPS response is in-progress and either
- parsing chunked trailers (trailer-line states), or
- transfer-encoding is identity with content_length == null while in
.body,
treat EOF as end-of-message and complete the request, rather than
ECONNRESET.
- Schedule proxy deref instead of deref inside callbacks to avoid
lifetime hazards.
### How did you verify your code works?
- `test/js/bun/http/proxy.test.ts`: raw TLS origin returns
close-delimited 200 OK; verified no ECONNRESET and body delivered.
- Test suite passes under bun bd test.
## Risk/compat
- Only affects CONNECT/TLS path. Direct HTTP/HTTPS unchanged. Behavior
mirrors existing
[HTTPClient.onClose](https://github.com/oven-sh/bun/blob/bun-v1.3.0/src/http.zig#L223-L241).
## Repro (minimal)
See issue; core condition is no Content-Length and no Transfer-Encoding
(close-delimited).
Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
## Summary
This PR refactors `NapiEnv` to use `bun.ptr.ExternalShared` instead of
manual `ref()`/`deref()` calls, fixing a use-after-free bug in the NAPI
implementation.
## Bug Fixed
The original issue was in `ThreadSafeFunction.deinit()`:
1. `maybeQueueFinalizer()` schedules a task that holds a pointer to
`this` (which includes `this.env`)
2. The task will eventually call `onDispatch()` → `deinit()`
3. But `deinit()` immediately calls `this.env.deref()` before the task
completes
4. This could cause the `NapiEnv` reference count to go to 0 while the
pointer is still in use
## Changes
### Core Changes
- Added `NapiEnv.external_shared_descriptor` and `NapiEnv.EnvRef` type
alias
- Changed struct fields from `*NapiEnv` to `NapiEnv.EnvRef` where
ownership is required:
- `ThreadSafeFunction.env`
- `napi_async_work.env`
- `Finalizer.env` (now `NapiEnv.EnvRef.Optional`)
### API Changes
- Use `.get()` to access the raw `*NapiEnv` pointer from `EnvRef`
- Use `.cloneFromRaw(env)` when storing `env` in long-lived structs
- Use `EnvRef.deinit()` instead of manual `env.deref()`
- Removed manual `env.ref()` calls (now handled automatically by
`cloneFromRaw`)
### Safety Improvements
- Reference counting is now managed by the `ExternalShared` wrapper
- Prevents manual ref/deref mistakes
- Ensures proper cleanup even when operations are cancelled or fail
- No more use-after-free risks from premature deref
## Testing
Built successfully with `bun bd`. NAPI tests pass (66/83 tests, with 17
timeouts that appear to be pre-existing issues).
## Implementation Notes
Following the pattern from `Blob.zig` and `array_buffer.zig`, structs
that own a reference use `NapiEnv.EnvRef`, while functions that only
borrow temporarily continue to use `*NapiEnv` parameters.
The `ExternalShared` interface ensures:
- `.clone()` increments the ref count
- `.deinit()` decrements the ref count
- No direct access to the internal ref/deref functions
This makes the ownership semantics explicit and type-safe.
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: taylor.fish <contact@taylor.fish>
## Summary
Fixes `Buffer.isEncoding('')` to return `false` instead of `true`,
matching Node.js behavior.
## Description
Previously, `Buffer.isEncoding('')` incorrectly returned `true` in Bun,
while Node.js correctly returns `false`. This was caused by
`parseEnumerationFromView` in `JSBufferEncodingType.cpp` treating empty
strings (length 0) as valid utf8 encoding.
The fix modifies the switch statement to return `std::nullopt` for empty
strings, along with other invalid short strings.
## Changes
- Modified `src/bun.js/bindings/JSBufferEncodingType.cpp` to return
`std::nullopt` for empty strings
- Added regression test `test/regression/issue23966.test.ts`
## Test Plan
- [x] Test fails with `USE_SYSTEM_BUN=1 bun test
test/regression/issue23966.test.ts` (confirms bug exists)
- [x] Test passes with `bun bd test test/regression/issue23966.test.ts`
(confirms fix works)
- [x] Verified behavior matches Node.js v24.3.0
- [x] All test cases for valid/invalid encodings pass
Fixes#23966🤖 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?
Replaces raw napi_env pointers with WTF::Ref<NapiEnv> for improved
memory management and safety. Updates related classes, function
signatures, and finalizer handling to use reference counting. Adds
ref/deref methods to NapiEnv and integrates them in Zig and C++ code
paths, ensuring proper lifecycle management for N-API environments.
### How did you verify your code works?
found with https://github.com/oven-sh/bun/pull/21663 again
case found in `test/js/bun/net/socket.test.ts`
test `"should throw when a socket from a file descriptor has a bad file
descriptor"`
## Summary
Fixed a bug in the Windows bunx fast path code where UTF-8 byte length
was incorrectly used instead of UTF-16 code unit length when calculating
buffer offsets.
## Details
In `run_command.zig:1565`, the code was using `target_name.len` (UTF-8
byte length) instead of `encoded.len` (UTF-16 code unit length) when
calculating the total path length. This caused an index out of bounds
panic when package names contained multi-byte UTF-8 characters.
**Example scenario:**
- Package name contains character "中" (U+4E2D)
- UTF-8: 3 bytes (0xE4 0xB8 0xAD) → `target_name.len` counts as 3
- UTF-16: 1 code unit (0x4E2D) → `encoded.len` counts as 1
- Using the wrong length led to: `panic: index out of bounds: index 62,
len 60`
## Changes
- Changed line 1565 from `target_name.len` to `encoded.len`
## Test plan
- [x] Build compiles successfully
- [x] Code review confirms the fix addresses the root cause
- [ ] Windows-specific testing (if available)
Fixes the panic reported in Sentry/crash reports.
🤖 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?
toSlice has a bug
### How did you verify your code works?
---------
Co-authored-by: taylor.fish <contact@taylor.fish>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
`bun.String.toOwnedSliceReturningAllASCII` is supposed to return a
boolean indicating whether or not the string is entirely composed of
ASCII characters. However, the current implementation frequently
produces incorrect results:
* If the string is a `ZigString`, it always returns true, even though
`ZigString`s can be UTF-16 or Latin-1.
* If the string is a `StaticZigString`, it always returns false, even
though `StaticZigStrings` can be all ASCII.
* If the string is a 16-bit `WTFStringImpl`, it always returns false,
even though 16-bit `WTFString`s can be all ASCII.
* If the string is empty, it always returns false, even though empty
strings are valid ASCII strings.
`toOwnedSliceReturningAllASCII` is currently used in two places, both of
which assume its answer is accurate:
* `bun.webcore.Blob.fromJSWithoutDeferGC`
* `bun.api.ServerConfig.fromJS`
(For internal tracking: fixes ENG-21249)
### What does this PR do?
Adds missing exception check for ReadableStream.
### How did you verify your code works?
Tests
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### 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>
### What does this PR do?
Fixes a small bug I found in https://github.com/oven-sh/bun/pull/23107
which caused `SlicedString` not to correctly provide us with subslices.
This would have been a **killer** use-case for the interval utility we
decided to reject in https://github.com/oven-sh/bun/pull/23882. Consider
how nice the code could've been:
```zig
pub inline fn sub(this: SlicedString, input: string) SlicedString {
const buf_r = bun.math.interval.fromSlice(this.buf);
const inp_r = bun.math.interval.fromSlice(this.input);
if (Environment.allow_assert) {
if (!buf_r.superset(inp_r)) {
bun.Output.panic("SlicedString.sub input [{}, {}) is not a substring of the " ++
"slice [{}, {})", .{ start_i, end_i, start_buf, end_buf });
}
}
return SlicedString{ .buf = this.buf, .slice = input };
}
```
That's a lot more readable than the middle-school algebra we have here,
but here we are.
### How did you verify your code works?
CI
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
### What does this PR do?
msvc doesn't support c23 yet
### How did you verify your code works?
---------
Co-authored-by: Marko Vejnovic <marko@bun.com>
## Summary
This PR adds a Claude Code-powered issue deduplication system to help
reduce duplicate issues in the Bun repository.
### What's included:
1. **`/dedupe` slash command** (`.claude/commands/dedupe.md`)
- Claude Code command to find up to 3 duplicate issues for a given
GitHub issue
- Uses parallel agent searches with diverse keywords
- Filters out false positives
2. **Automatic dedupe on new issues**
(`.github/workflows/claude-dedupe-issues.yml`)
- Runs automatically when a new issue is opened
- Can also be triggered manually via workflow_dispatch
- Uses the Claude Code base action to run the `/dedupe` command
3. **Auto-close workflow**
(`.github/workflows/auto-close-duplicates.yml`)
- Runs daily to close issues marked as duplicates after 3 days
- Only closes if:
- Issue has a duplicate detection comment from bot
- Comment is 3+ days old
- No comments or activity after duplicate comment
- Author hasn't reacted with 👎 to the duplicate comment
4. **Auto-close script** (`scripts/auto-close-duplicates.ts`)
- TypeScript script that handles the auto-closing logic
- Fetches open issues and checks for duplicate markers
- Closes issues with proper labels and notifications
### How it works:
1. When a new issue is opened, the workflow runs Claude Code to analyze
it
2. Claude searches for duplicates and comments on the issue if any are
found
3. Users have 3 days to respond if they disagree
4. After 3 days with no activity, the issue is automatically closed
### Requirements:
- `ANTHROPIC_API_KEY` secret needs to be set in the repository settings
for the dedupe workflow to run
## Test plan
- [x] Verified workflow files have correct syntax
- [x] Verified script references correct repository (oven-sh/bun)
- [x] Verified slash command matches claude-code implementation
- [ ] Test workflow manually with workflow_dispatch (requires
ANTHROPIC_API_KEY)
- [ ] Monitor initial runs to ensure proper behavior
🤖 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 a bug where the `Bun.build()` API with `compile: true` did not
properly apply sourcemaps, even when `sourcemap: "inline"` was
specified. This resulted in error stack traces showing bundled virtual
paths (`/$bunfs/root/`) instead of actual source file names and line
numbers.
## Problem
The CLI `bun build --compile --sourcemap` worked correctly, but the
equivalent API call did not:
```javascript
// This did NOT work (before fix)
await Bun.build({
entrypoints: ['./app.js'],
compile: true,
sourcemap: "inline" // <-- Was ignored/broken
});
```
Error output showed bundled paths:
```
error: Error from helper module
at helperFunction (/$bunfs/root/app.js:4:9) // ❌ Wrong path
at main (/$bunfs/root/app.js:9:17) // ❌ Wrong line numbers
```
## Root Cause
The CLI explicitly overrides any sourcemap type to `.external` when
compile mode is enabled (in `/workspace/bun/src/cli/Arguments.zig`):
```zig
// when using --compile, only `external` works
if (ctx.bundler_options.compile) {
opts.source_map = .external;
}
```
The API implementation in `JSBundler.zig` was missing this override.
## Solution
Added the same sourcemap override logic to `JSBundler.zig` when compile
mode is enabled:
```zig
// When using --compile, only `external` sourcemaps work, as we do not
// look at the source map comment. Override any other sourcemap type.
if (this.source_map != .none) {
this.source_map = .external;
}
```
Now error output correctly shows source file names:
```
error: Error from helper module
at helperFunction (helper.js:2:9) // ✅ Correct file
at main (app.js:4:3) // ✅ Correct line numbers
```
## Tests
Added comprehensive test coverage in
`/workspace/bun/test/bundler/bun-build-compile-sourcemap.test.ts`:
- ✅ `sourcemap: "inline"` works
- ✅ `sourcemap: true` works
- ✅ `sourcemap: "external"` works
- ✅ Multiple source files show correct file names
- ✅ Without sourcemap, bundled paths are shown (expected behavior)
All tests:
- ✅ Fail with `USE_SYSTEM_BUN=1` (confirms bug exists)
- ✅ Pass with `bun bd test` (confirms fix works)
- ✅ Use `tempDir()` to avoid disk space issues
🤖 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?
Adds support for `publicHoistPattern` in `bunfig.toml` and
`public-hoist-pattern` from `.npmrc`. This setting allows you to select
transitive packages to hoist to the root node_modules making them
available for all workspace packages.
```toml
[install]
# can be a string
publicHoistPattern = "@types*"
# or an array
publicHoistPattern = [ "@types*", "*eslint*" ]
```
`publicHoistPattern` only affects the isolated linker.
---
Adds `hoistPattern`. `hoistPattern` is the same as `publicHoistPattern`,
but applies to the `node_modules/.bun/node_modules` directory instead of
the root node_modules. Also the default value of `hoistPattern` is `*`
(everything is hoisted to `node_modules/.bun/node_modules` by default).
---
Fixes a determinism issue constructing the
`node_modules/.bun/node_modules` directory.
---
closes#23481closes#6160closes#23548
### How did you verify your code works?
Added tests for
- [x] only include patterns
- [x] only exclude patterns
- [x] mix of include and exclude
- [x] errors for unexpected expression types
- [x] excluding direct dependency (should still include)
- [x] match all with `*`
- [x] string and array expression types
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### What does this PR do?
`short` is signed in C++ by default and not unsigned. Switched to
`uint16_t` so it's unambiguous.
### How did you verify your code works?
There is a test
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Fixes#19652
## Summary
Fixes a crash that occurred when using the `--production` flag with `bun
build`, particularly on Windows where assertions are enabled in release
builds.
## Root Cause
The crash occurred because an assertion for `jsx.development` was
running **before** `jsx.development` was properly configured. The
problematic sequence was:
1. Set `NODE_ENV=production` in env map
2. Call `configureDefines()` which reads `NODE_ENV` and calls
`setProduction(true)`, setting `jsx.development=false`
3. ❌ **Assert `jsx.development` is false** (assertion fired here, before
line 203 below)
4. Set `jsx.development = !production` on line 203 (too late)
## Changes
This PR reorders the code to move the assertion **after**
`jsx.development` is properly set:
1. Set both `BUN_ENV` and `NODE_ENV` to `"production"` in env map
2. Call `configureDefines()`
3. Set `jsx.development = !production` (now happens first)
4. ✅ **Assert `jsx.development` is false** (now runs after it's set)
Also adds `BUN_ENV=production` to match the behavior of setting
`NODE_ENV`.
## Test Plan
Added regression test in `test/regression/issue/19652.test.ts` that
verifies `bun build --production` doesn't crash.
The test:
- ✅ Passes on this branch
- ❌ Would fail on main (assertion failure)
🤖 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>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
### What does this PR do?
Fixes a bug preventing workspace self dependencies from getting
symlinked to the workspace node_modules
Fixes#23605
### How did you verify your code works?
Added a test for normal `"workspace:*"` deps, and `"workspace:."` under
a different name.
---------
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
### What does this PR do?
We read and write this field on multiple threads. Let's add a mutex.
Fixes BUN-MGB
### How did you verify your code works?
---------
Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
## Summary
Fixed an off-by-one error in buffer allocation for several path module
functions when handling paths longer than `PATH_SIZE` (typically 4096
bytes on most platforms).
## Changes
- `normalizeJS_T`: Added +1 to buffer allocation for null terminator
- `relativeJS_T`: Added +1 to buffer allocation for null terminator
- `toNamespacedPathJS_T`: Added +9 bytes (8 for possible UNC prefix + 1
for null terminator)
## Test plan
- Added tests for `path.normalize()` with paths up to 100,000 characters
- Added tests for `path.relative()` with very long paths
- All existing path tests continue to pass
The issue occurred because when a path is exactly equal to or longer
than `PATH_SIZE`, the buffer was allocated with size equal to the path
length, but then a null terminator was written at `buf[bufSize]`, which
was out of bounds.
🤖 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 a panic that occurred when a WebSocket close frame's payload was
split across multiple TCP packets.
## The Bug
The panic occurred at `websocket_client.zig:681`:
```
panic: index out of bounds: index 24, len 14
```
This happened when:
- A close frame had a payload of 24 bytes (2 byte code + 22 byte reason)
- The first TCP packet contained 14 bytes (header + partial payload)
- The code tried to access `data[2..24]` causing the panic
## Root Causes
1. **Bounds checking issue**: The code assumed all close frame data
would arrive in one packet and tried to `@memcpy` without verifying
sufficient data was available.
2. **Premature flag setting**: `close_received = true` was set
immediately upon entering the close state. This prevented `handleData`
from being called again when the remaining bytes arrived (early return
at line 354).
## The Fix
Implemented proper fragmentation handling for close frames, following
the same pattern used for ping frames:
- Added `close_frame_buffering` flag to track buffering state
- Buffer incoming data incrementally using the existing
`ping_frame_bytes` buffer
- Track total expected length and bytes received so far
- Only set `close_received = true` after all bytes are received
- Wait for more data if the frame is incomplete
## Testing
- Created two regression tests that fragment close frames across
multiple packets
- All existing WebSocket tests pass (`test/js/web/websocket/`)
- Verified the original panic no longer occurs
## Related
This appears to be the root cause of crashes reported on Windows when
WebSocket connections close, particularly when close frames have reasons
that get fragmented by the network stack.
---
🤖 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>
### What does this PR do?
Let MySQL unref when idle and make sure that is behaving like this.
Only set up the timers after all status changes are complete since the
timers rely on the status to determine timeouts, this was causing the
CPU usage spike to 100% (thats why only happened in TLS)
CPU usage it self will be improved in
https://github.com/oven-sh/bun/pull/23700 not in this PR
Fixes: https://github.com/oven-sh/bun/issues/23273
Fixes: https://github.com/oven-sh/bun/issues/23256
### How did you verify your code works?
Test
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
### What does this PR do?
This PR implements support for the `email` field in `.npmrc` files for
registry scope authentication. Some private registries (particularly
Nexus) require the email field to be specified in the registry
configuration alongside username/password or token authentication.
The email field can now be specified in `.npmrc` files like:
```ini
//registry.example.com/:email=user@example.com
//registry.example.com/:username=myuser
//registry.example.com/:_password=base64encodedpassword
```
### How did you verify your code works?
1. **Built Bun successfully** - Confirmed the code compiles without
errors using `bun bd --debug`
2. **Wrote comprehensive unit tests** - Added two test cases to
`test/cli/install/npmrc.test.ts`:
- Test for standalone email field parsing
- Test for email combined with username/password authentication
3. **Verified tests pass** - Ran `bun bd test
test/cli/install/npmrc.test.ts -t "email"` and confirmed both tests
pass:
```
✓ 2 pass
✓ 0 fail
✓ 6 expect() calls
```
4. **Code changes include**:
- Added `email` field to `NpmRegistry` struct in `src/api/schema.zig`
- Updated `encode()` and `decode()` methods to handle the email field
- Modified `ini.zig` to parse and store the email field from `.npmrc`
- Removed email from the unsupported options warning (certfile and
keyfile remain unsupported)
- Updated all `NpmRegistry` struct initializations to include the email
field
- Updated `loadNpmrcFromJS` test API to return the email field
🤖 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#23489
The YAML parser was incorrectly treating `...` inside double-quoted
strings as document end markers, causing parse errors for strings
containing ellipsis, particularly affecting internationalized text.
### Example of the bug:
```yaml
balance: "👛 لا تمتلك محفظة... !"
```
This would fail with: `error: Unexpected document end`
### Root cause:
The bug was introduced in commit fcbd57ac48 which attempted to optimize
document marker detection by using `self.line_indent == .none` instead
of tracking newlines with a local flag. However, this check was
incomplete - it didn't track whether we had just processed a newline
character.
### The fix:
Restored the `nl` (newline) flag pattern from the single-quoted scanner
and combined it with the `line_indent` check. Document markers `...` and
`---` are now only recognized when **all** of these conditions are met:
1. We're after a newline (`nl == true`)
2. We're at column 0 (`self.line_indent == .none`)
3. Followed by whitespace or EOF
This allows `...` to appear freely in double-quoted strings while still
correctly recognizing actual document end markers at the start of lines.
### How did you verify your code works?
1. Reproduced the original issue from #23489
2. Applied the fix and verified all test cases pass:
- Original Arabic text with emoji: `"👛 لا تمتلك محفظة... !"`
- Various `...` positions: start, middle, end
- Both single and double quotes
- Multiline strings with indented `...` (issue #22392)
3. Created regression test in `test/regression/issue/23489.test.ts`
4. Verified existing YAML tests still pass (514 pass, up from 513)
cc @dylan-conway for review
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
## 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>
### 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>
## 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
Adds debug logging that prints the name of each test when it starts
running, controlled by the `BUN_DEBUG_jest=1` environment variable.
## Changes
- Modified `src/bun.js/test/Execution.zig` to add logging in the
`onEntryStarted()` function
- Added a scoped logger using `bun.Output.scoped(.jest, .visible)`
- When `BUN_DEBUG_jest=1` is set, prints: `[jest] Running test: <test
name>`
## Testing
Manually tested with various test files:
**Without BUN_DEBUG_jest:**
```
$ bun bd test /tmp/test-jest-log.test.ts
bun test v1.3.1 (642d04b9)
3 pass
0 fail
3 expect() calls
Ran 3 tests across 1 file. [2.90s]
```
**With BUN_DEBUG_jest=1:**
```
$ BUN_DEBUG_jest=1 bun bd test /tmp/test-jest-log.test.ts
bun test v1.3.1 (642d04b9)
[jest] Running test: first test
[jest] Running test: second test
[jest] Running test: third test
3 pass
0 fail
3 expect() calls
Ran 3 tests across 1 file. [2.77s]
```
Also tested with nested describe blocks and all test names are logged
correctly.
## Notes
- This feature is only available in debug builds (not release builds)
- No tests were added as this is a debug-only feature
- Helps with debugging test execution flow and understanding when tests
start running
---------
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: pfg <pfg@pfg.pw>
## 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>
## Summary
The `EventLoopTimer.Arm` result from `EventLoopTimer.fire()` was being
ignored at both call sites. This PR removes the unused return type and
simplifies the code.
## Changes
- Changed `EventLoopTimer.fire()` to return `void` instead of `Arm`
- Updated all 15 timer callback functions to return `void`
- Removed the `Arm` type definition
- Simplified the `drainTimers()` loop that was ignoring the return value
- Updated both call sites in `Timer.zig`
## Details
The `.rearm` functionality was unused - timers that need to reschedule
themselves (like DNS resolver) handle this by calling
`addTimer()`/`update()` directly rather than relying on the return
value.
This change removes:
- The `Arm` union enum type (3 lines)
- All `return .disarm` and `return .{ .rearm = ... }` statements
- The switch statement in `drainTimers()` that did nothing with the
return value
Net result: **-58 lines** of dead code removed.
## Testing
- [x] Bun builds successfully with `bun bd`
🤖 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?
### 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>
Bun bundler documentation duplicated the "linked" type for sourcemap.
### What does this PR do?
Fix documentation mistake.
### How did you verify your code works?
No code changes have been made.
## 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>
Avoid calling into C++ in `jsc.JSValue.asCell`.
(For internal tracking: fixes ENG-20820)
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.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>
### 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>
## Summary
Fixes Clang 19 detection in the Nix flake environment by explicitly
setting CMAKE compiler environment variables in the shellHook.
## Problem
When using `nix develop` or `nix print-dev-env`, CMake was unable to
detect the Clang 19 compiler because the `CMAKE_C_COMPILER` and
`CMAKE_CXX_COMPILER` environment variables were not being set, even
though the compiler was available in the environment.
The `shell.nix` file correctly sets these variables (lines 80-87), but
`flake.nix` was missing them.
## Solution
Updated `flake.nix` shellHook to export the same compiler environment
variables as `shell.nix`:
- `CC`, `CXX`, `AR`, `RANLIB`
- `CMAKE_C_COMPILER`, `CMAKE_CXX_COMPILER`, `CMAKE_AR`, `CMAKE_RANLIB`
This ensures consistent compiler detection across both Nix entry points
(`nix develop` with flakes and `nix-shell` with shell.nix).
## Testing
Verified that all compiler variables are now properly set:
```bash
nix develop --accept-flake-config --impure --command bash -c 'echo "CMAKE_C_COMPILER=$CMAKE_C_COMPILER"'
# Output: CMAKE_C_COMPILER=/nix/store/.../clang-wrapper-19.1.7/bin/clang
```
Also tested with the profile workflow:
```bash
nix develop --accept-flake-config --impure --profile ./dev-profile --command true
eval "$(nix print-dev-env ./dev-profile --accept-flake-config --impure)"
echo "CMAKE_C_COMPILER=$CMAKE_C_COMPILER"
# Output: CMAKE_C_COMPILER=/nix/store/.../clang
```
🤖 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 fixes incorrect WriteBarrier initialization patterns throughout
the Bun codebase where `.set()` or `.setEarlyValue()` was being called
in the constructor body or in `finishCreation()`. According to
JavaScriptCore's `WriteBarrier.h`, WriteBarriers that are initialized
during construction should use the `WriteBarrierEarlyInit` constructor
in the initializer list to avoid triggering unnecessary write barriers.
## Changes
The following classes were updated to properly initialize WriteBarrier
fields:
1. **InternalModuleRegistry** - Initialize internal fields in
constructor using `setWithoutWriteBarrier()` instead of calling `.set()`
in `finishCreation()`
2. **AsyncContextFrame** - Pass callback and context to constructor and
use `WriteBarrierEarlyInit`
3. **JSCommonJSModule** - Pass id, filename, dirname to constructor and
use `WriteBarrierEarlyInit`
4. **JSMockImplementation** - Pass initial values to constructor and use
`WriteBarrierEarlyInit`
5. **JSConnectionsList** - Pass connection sets to constructor and use
`WriteBarrierEarlyInit`
6. **JSBunRequest** - Pass params to constructor and initialize both
`m_params` and `m_cookies` using `WriteBarrierEarlyInit`
7. **JSNodeHTTPServerSocket** - Initialize `currentResponseObject` using
`WriteBarrierEarlyInit` instead of calling `setEarlyValue()`
## Why This Matters
From JavaScriptCore's `WriteBarrier.h`:
```cpp
enum WriteBarrierEarlyInitTag { WriteBarrierEarlyInit };
// Constructor for early initialization during object construction
WriteBarrier(T* value, WriteBarrierEarlyInitTag)
{
this->setWithoutWriteBarrier(value);
}
```
Using `WriteBarrierEarlyInit` during construction:
- Avoids triggering write barriers when they're not needed
- Is the correct pattern for initializing WriteBarriers before the
object is fully constructed
- Aligns with JavaScriptCore best practices
## Testing
- ✅ Full debug build completes successfully
- ✅ Basic functionality tested (CommonJS modules, HTTP requests, Node
HTTP servers)
- ✅ No regressions observed
## Note on generate-classes.ts
The code generator (`generate-classes.ts`) does not need updates because
generated classes intentionally leave WriteBarrier fields (callbacks,
cached fields, values) uninitialized. They start with
default-constructed WriteBarriers and are populated later by Zig code
via setter functions, which is the correct pattern for those fields.
---------
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>
### What does this PR do?
Fixes `bun -p "process.stderr.write('Hello' +
String.fromCharCode(0xd800))"`.
Also fixes potential index out of bounds if there are many invalid
sequences.
This also affects `TextEncoder`.
### How did you verify your code works?
Added tests for edgecases
---------
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Provides a Nix flake as an alternative to `scripts/bootstrap.sh` for
setting up the Bun development environment.
## What's included:
- **flake.nix**: Full development environment with all dependencies from
bootstrap.sh
- LLVM 19, CMake 3.30+, Node.js 24, Rust, Go
- Build tools: ninja, ccache, pkg-config, make
- Chromium dependencies for Puppeteer testing
- gdb for core dump debugging
- **shell.nix**: Simple wrapper for `nix-shell` usage
- **cmake/CompilerFlags.cmake**: Nix compatibility fixes
- Disable zstd debug compression (Nix's LLVM not built with zstd)
- Set _FORTIFY_SOURCE=0 for -O0 debug builds
- Downgrade _FORTIFY_SOURCE warning to not error
## Usage:
```bash
nix-shell
export CMAKE_SYSTEM_PROCESSOR=$(uname -m)
bun bd
```
## Verified working:
✅ Successfully compiles Bun debug build
✅ Binary tested: `./build/debug/bun-debug --version` → 1.2.24-debug
✅ All dependencies from bootstrap.sh included
## Advantages:
- Fully isolated (no sudo required)
- 100% reproducible dependency versions
- Fast setup with binary caching
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
### What does this PR do?
Calls `uncork()` after flushing response headers to ensure data is sent
as soon as possible, improving responsiveness.
This behavior still works correctly even without the explicit `uncork()`
call, due to the deferred uncork logic implemented here:
6e3359dd16/packages/bun-uws/src/Loop.h (L57-L64)
A test already covers this scenario in
`test/js/node/test/parallel/test-http-flush-response-headers.js`.
### How did you verify your code works?
CI
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### What does this PR do?
fix tests failing because of example.com
### How did you verify your code works?
CI
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Fixes#23333, Fixes#13978
### 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>
Co-authored-by: pfg <pfg@pfg.pw>
Co-authored-by: Zack Radisic <zack@theradisic.com>
## What does this PR do?
Bumps Bun version from 1.2.24 to 1.3.0, marking the start of the 1.3.x
release series.
## Changes
- **`package.json`**: Updated version from `1.2.24` to `1.3.0`
- **`LATEST`**: Updated from `1.2.23` to `1.3.0` (used by installation
scripts)
- **`test/bundler/bundler_bun.test.ts`**: Updated version check to
include `1.3.x` so export conditions tests continue to run
## Verification
✅ Debug build successful showing version `1.3.0-debug`
✅ All platforms compile successfully via `bun run zig:check-all` (49/49
steps)
✅ Bundler tests pass with updated version check
## Additional Notes
- CI workflow Bun versions (e.g., `1.2.3`, `1.2.0` in
`.github/workflows/release.yml`) are intentionally left unchanged -
these are pinned versions used to run the release tooling, not the
version being released
- Docker images use `ARG BUN_VERSION` passed at build time and don't
need updates
- The actual release version comes from git tags via `${{
env.BUN_VERSION }}`
---
🤖 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>
Fixes#23380 - this is a use-case for the `--only` flag that I missed
Adds back the `--only` flag. When running `bun test` on a full test
suite, without this flag it will run only that test in its file, but it
will run all other tests from other files. With this flag, it will not
run things from other files.
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: pfg <pfg@pfg.pw>
## Summary
Modernizes `test/bundler/bundler_promiseall_deadcode.test.ts` to use the
`itBundled` test helper instead of manual temp directory creation and
spawning. This makes the test more concise, maintainable, and consistent
with other bundler tests.
## Changes
- Replace `tempDirWithFiles` + manual `Bun.spawn` with `itBundled`
- Use `files` object for test fixtures instead of creating a temp
directory
- Use `onAfterBundle` callback for bundled output assertions
- Use `run.validate` for runtime stderr validation
- Use `run.partialStdout` for stdout verification
- Preserve all original test assertions and behavior
## Test Results
All 3 tests pass with identical functional behavior:
```
3 pass
0 fail
2 snapshots, 23 expect() calls
Ran 3 tests across 1 file. [8.95s]
```
## Verification
All original assertions are preserved:
- ✅ Build success validation
- ✅ Bundled output snapshots (updated paths to match itBundled format)
- ✅ `__esm` and `__promiseAll` presence/absence checks
- ✅ Runtime execution validation (exit code 0)
- ✅ Runtime stderr validation (no async syntax errors)
- ✅ Runtime stdout validation (contains expected output)
The test is now more concise (407 insertions vs 514 deletions) while
maintaining full test coverage.
🤖 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 isolated installs the default install strategy for projects with
workspaces in Bun v1.3.
Also fixes creating patches with `bun patch` and `--linker isolated`
Fixes#22693
### How did you verify your code works?
Added tests for node_modules renaming `bun patch` with isolated install.
---------
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>
Alternative to #15596 where it now only impacts `bun run` for the same
cwd dir. This does not effect `bunx` ([even though according to code it
should load
it](7830e15650/src/cli.zig (L2597-L2628))),
and isnt as fancy as `bun install` where it ensures to check the bunfig
in `package.json` dir.
This shouldn't have any performance issues because its already loading
the file, but now its loading earlier so it can use `run.bun` option.
Fixes#11445, (as well as fixes#15484, fixes#15483, fixes#17064)
---------
Co-authored-by: pfg <pfg@pfg.pw>
## Summary
This PR improves the correctness of bin linking by atomically
normalizing `\r\n` to `\n` in shebang lines when linking bins.
### Changes
- **Refactored shebang normalization in `src/install/bin.zig`**:
- Extracted logic into separate `tryNormalizeShebang` function
- Changed from in-place file modification to atomic file replacement
- Reads entire file, creates temporary file with corrected shebang, then
atomically renames
- Properly cleans up temporary files on errors
- **Added test coverage**:
- New test file `test/cli/install/shebang-normalize.test.ts` verifies
CRLF normalization works correctly
- Modified existing test in `bun-link.test.ts` to use Python script with
CRLF shebang
### Why
The previous implementation modified files in-place by seeking to the
`\r` position and overwriting with `\n`. This could potentially corrupt
files if interrupted mid-write. The new atomic approach ensures file
integrity by writing to a temporary file first, then renaming it to
replace the original.
## Test plan
- ✅ `bun bd test test/cli/install/shebang-normalize.test.ts` - passes
- ✅ Verified bins with CRLF shebangs are normalized to LF during linking
- ✅ Code compiles successfully
🤖 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?
BeforeOpen code is not necessary since we have `setOnSocketUpgraded`
callback now,and we should NOT convert websocket to a response, make
sure that no closed socket is passed to `JSNodeHTTPServerSocket`, change
isIdle to be inside AsyncSocketData to be more reliable (works for
websocket and normal sockets)
### How did you verify your code works?
CI
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## Summary
Fixed a race condition where calling `pause()` followed by `resume()` on
`process.stdin` would prevent data from being received, causing the
process to exit immediately instead of listening for input.
## Root Cause
The issue was in the pause/resume event handling logic in
`ProcessObjectInternals.ts`:
1. When `pause()` is called, the "pause" event handler schedules a
`disown()` call for the next tick
2. When `resume()` is called immediately after, it calls `own()` to
acquire a stream reader
3. On the next tick, the scheduled `disown()` from step 1 executes and
incorrectly releases the reader that was just acquired in step 2
This race condition left the stream without a reader, so no data could
be received.
## Solution
Added a `pendingDisown` flag that:
- Gets set to `true` when scheduling a disown operation
- Gets cleared to `false` when `own()` is called (during resume)
- Prevents the scheduled disown from executing if it has been cancelled
by a subsequent `own()` call
## Test Plan
- [x] Added regression tests in
`test/regression/issue/stdin-pause-resume.test.ts`
- [x] Verified fix with original reproduction case
- [x] Existing stdin/tty tests still pass
(`tty-readstream-ref-unref.test.ts`,
`tty-reopen-after-stdin-eof.test.ts`)
## Reproduction
Before this fix, the following code would exit immediately:
```ts
process.stdin.on("data", chunk => {
process.stdout.write(chunk);
});
process.stdin.pause();
process.stdin.resume();
```
After the fix, it correctly waits for and processes input.
🤖 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 data loss when reading large amounts of data from subprocess pipes
on Windows, a regression introduced by the libuv 1.51.0 upgrade in
commit e3783c244f.
### The Problem
When piping large data through a subprocess on Windows (e.g.,
`process.stdin.pipe(process.stdout)`), Bun randomly loses ~73KB of data
out of 1MB, receiving only ~974KB instead of the full 1048576 bytes.
The subprocess correctly receives all 1MB on stdin, but the parent
process loses data when reading from the subprocess stdout.
### Root Cause Analysis
#### libuv 1.51.0 Change
The libuv 1.51.0 upgrade (commit
[libuv/libuv@727ee723](727ee7237e))
changed Windows pipe reading behavior:
**Before:** libuv would call `PeekNamedPipe` to check available bytes,
then read exactly that amount.
**After:** libuv attempts immediate non-blocking reads (up to 65536
bytes) before falling back to async reads. If less data is available
than requested, it returns what's available and signals `more=0`,
causing the read loop to break.
This optimization introduces **0-byte reads** when data isn't
immediately available, which are delivered to Bun's read callback.
#### The Race Condition
When Bun's `WindowsBufferedReader` called `onRead(.drained)` for these
0-byte reads, it created a race condition. Debug logs clearly show the
issue:
**Error case (log.txt):**
```
Line 79-80: onStreamRead = 0 (drained)
Line 81: filesink closes (stdin closes)
Line 85: onStreamRead = 6024 ← Should be 74468!
Line 89: onStreamRead = -4095 (EOF)
```
**Success case (success.log.txt):**
```
Line 79-80: onStreamRead = 0 (drained)
Line 81: filesink closes (stdin closes)
Line 85: onStreamRead = 74468 ← Full chunk!
Line 89-90: onStreamRead = 0 (drained)
Line 91: onStreamRead = 6024
Line 95: onStreamRead = -4095 (EOF)
```
When stdin closes while a 0-byte drained read is pending, the next read
returns truncated data (6024 bytes instead of 74468 bytes).
### The Fix
Two changes to `WindowsBufferedReader` in `src/io/PipeReader.zig`:
#### 1. Ignore 0-byte reads (line 937-940)
Don't call `onRead(.drained)` for 0-byte reads. Just return and let
libuv queue the next read. This prevents the race condition that causes
truncated reads.
```zig
0 => {
// With libuv 1.51.0+, calling onRead(.drained) here causes a race condition
// where subsequent reads return truncated data. Just ignore 0-byte reads.
return;
},
```
#### 2. Defer `has_inflight_read` flag clearing (line 827-839)
Clear the flag **after** the read callback completes, not before. This
prevents libuv from starting a new overlapped read operation while we're
still processing the current data buffer, which could cause memory
corruption per the libuv commit message:
> "Starting a new read after uv_read_cb returns causes memory corruption
on the OVERLAPPED read_req if uv_read_stop+uv_read_start was called
during the callback"
```zig
const result = onReadChunkFn(this.parent, buf, hasMore);
// Clear has_inflight_read after the callback completes
this.flags.has_inflight_read = false;
return result;
```
### How to Test
Run the modified test in
`test/js/bun/spawn/spawn-stdin-readable-stream.test.ts`:
```js
test("ReadableStream with very large chunked data", async () => {
const chunkSize = 64 * 1024; // 64KB chunks
const numChunks = 16; // 1MB total
const chunk = Buffer.alloc(chunkSize, "x");
const stream = new ReadableStream({
pull(controller) {
if (pushedChunks < numChunks) {
controller.enqueue(chunk);
pushedChunks++;
} else {
controller.close();
}
},
});
await using proc = spawn({
cmd: [bunExe(), "-e", `
let length = 0;
process.stdin.on('data', (data) => length += data.length);
process.once('beforeExit', () => console.error(length));
process.stdin.pipe(process.stdout)
`],
stdin: stream,
stdout: "pipe",
env: bunEnv,
});
const text = await proc.stdout.text();
expect(text.length).toBe(chunkSize * numChunks); // Should be 1048576
});
```
**Before fix:** Randomly fails with ~974KB instead of 1MB
**After fix:** Consistently passes with full 1MB
Run ~100 times to verify the race condition is fixed.
### Related Issues
This may also fix#23071 (Windows scripts hanging), though that issue
needs separate verification.
### Why Draft?
Marking as draft for Windows testing by the team. The fix is based on
detailed debug log analysis showing the exact race condition, but needs
verification on Windows CI.
🤖 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
When a snapshot is created in CI without `--update-snapshots`, the error
message now displays the received value that was attempting to be
snapshotted. This helps developers understand what value triggered the
error.
## Changes
- Modified the `SnapshotCreationNotAllowedInCI` error message in
`src/bun.js/test/expect.zig` to include the received value using the
same formatting pattern as other expect error messages
## 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.
```
## 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.
Received: <formatted value>
```
🤖 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>
Breaking changes:
- bun:test: disallow creating snapshots or using .only() in ci
- for users: hopefully this should only reveal existing bugs in tests,
not cause failures.
- general: enable calling unhandled rejection handlers for
ErrorBuilder.reject()
- for users: this might reveal some unhandled rejections that were not
visible before.
## Summary
- Clone `bunEnv` and delete `AGENTS` property in `beforeAll`
- Replace all `bunEnv` references with `testEnv` in test spawns
- Prevents parent process's `AGENTS` env var from leaking into tests
## Problem
The `claudecode-flag` test was using `bunEnv` directly, which includes
`...process.env`. When running in environments like Claude Code where
`AGENTS` may be set, this variable would leak into the test child
processes and potentially affect test behavior.
## Solution
Created a `testEnv` clone in `beforeAll` that explicitly deletes
`AGENTS`, ensuring consistent test behavior regardless of the parent
process's environment.
## Test plan
- [x] Test passes without `AGENTS` set
- [x] Test passes with `AGENTS=1` set in parent environment
🤖 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?
This PR upgrades the `react-shadcn` template:
- Upgrades to the new Tailwind v4 styles and components
- Updates the example components to use the new ones.
- Removed unused form component
- Fixed some a11y issues with the example component.
### How did you verify your code works?
- Ran `bun build` to test if the template builds with no errors.
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## Summary
Fixes#23206
When using `test.each` with object syntax and `$variable` interpolation,
string values were being quoted (e.g., `"apple"` instead of `apple`).
This didn't match the behavior of `%s` formatting or Jest's behavior.
## Changes
- Modified `formatLabel` in `src/bun.js/test/jest.zig` to check if the
value is a primitive string and use `toString()` instead of the
formatter with `quote_strings=true`
- Added regression test in `test/regression/issue/23206.test.ts`
## Example
**Before:**
```
test.each([
{ name: "apple" },
{ name: "banana" }
])("fruit #%# is $name", fruit => {
// Test names were:
// "fruit #0 is "apple""
// "fruit #1 is "banana""
});
```
**After:**
```
test.each([
{ name: "apple" },
{ name: "banana" }
])("fruit #%# is $name", fruit => {
// Test names are now:
// "fruit #0 is apple"
// "fruit #1 is banana"
});
```
## Test plan
- [x] Added regression test that verifies both `%s` and `$name` syntax
produce consistent output
- [x] Tested with `AGENT=0` - all tests pass
- [x] Verified other primitive types (numbers, booleans) still format
correctly
- [x] Verified complex objects still use proper formatting
This matches Jest's behavior after their fix:
https://github.com/jestjs/jest/issues/7689🤖 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>
Co-authored-by: pfg <pfg@pfg.pw>
### What does this PR do?
Fixes#23314 where `zlib.zstdCompress()` created data that caused an
out-of-memory error when decompressed with `Bun.zstdDecompressSync()`.
#### 1. `zlib.zstdCompress()` now sets `pledgedSrcSize`
The async convenience method now automatically sets the `pledgedSrcSize`
option to the input buffer size. This ensures the compressed frame
includes the content size in the header, making sync and async
compression produce identical output.
**Node.js compatibility**: `pledgedSrcSize` is a documented Node.js
option:
-
[`vendor/node/doc/api/zlib.md:754-758`](https://github.com/oven-sh/bun/blob/main/vendor/node/doc/api/zlib.md#L754-L758)
-
[`vendor/node/lib/zlib.js:893`](https://github.com/oven-sh/bun/blob/main/vendor/node/lib/zlib.js#L893)
-
[`vendor/node/src/node_zlib.cc:890-904`](https://github.com/oven-sh/bun/blob/main/vendor/node/src/node_zlib.cc#L890-L904)
#### 2. Added `bun.zstd.decompressAlloc()` - centralized safe
decompression
Created a new function in `src/deps/zstd.zig` that handles decompression
in one place with automatic safety features:
- **Handles unknown content sizes**: Automatically switches to streaming
decompression when the zstd frame doesn't include content size (e.g.,
from streams without `pledgedSrcSize`)
- **16MB safety limit**: For security, if the reported decompressed size
exceeds 16MB, streaming decompression is used instead of blindly
trusting the header
- **Fast path for small files**: Still uses efficient pre-allocation for
files < 16MB with known sizes
This centralized fix automatically protects:
- `Bun.zstdDecompressSync()` / `Bun.zstdDecompress()`
- `StandaloneModuleGraph` source map decompression
- Any other code using `bun.zstd` decompression
### How did you verify your code works?
**Before:**
```typescript
const input = "hello world";
// Async compression
const compressed = await new Promise((resolve, reject) => {
zlib.zstdCompress(input, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
// This would fail with "Out of memory"
const decompressed = Bun.zstdDecompressSync(compressed);
```
**Error**: `RangeError: Out of memory` (tried to allocate UINT64_MAX
bytes)
**After:**
```typescript
const input = "hello world";
// Async compression (now includes content size)
const compressed = await new Promise((resolve, reject) => {
zlib.zstdCompress(input, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
// ✅ Works! Falls back to streaming decompression if needed
const decompressed = Bun.zstdDecompressSync(compressed);
console.log(decompressed.toString()); // "hello world"
```
**Tests:**
- ✅ All existing tests pass
- ✅ New regression tests for async/sync compression compatibility
(`test/regression/issue/23314/zstd-async-compress.test.ts`)
- ✅ Test for large (>16MB) decompression using streaming
(`test/regression/issue/23314/zstd-large-decompression.test.ts`)
- ✅ Test for various input sizes and types
(`test/regression/issue/23314/zstd-large-input.test.ts`)
**Security:**
The 16MB safety limit protects against malicious zstd frames that claim
huge decompressed sizes in the header, preventing potential OOM attacks.
🤖 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>
Fixes#12095
Manually confirmed to fix the case, but it would be better to have an
automated test to compare default reporter output with lcov reporter
output.
---------
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?
Matches node behavior.
Fixes#20975
### How did you verify your code works?
Manually and added a test
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## What does this PR do?
Fixes#22003 by escaping tab characters in filenames when generating
sourcemap JSON.
When a filename contained a tab character (e.g., `file\ttab.js`), the
sourcemap JSON would contain a **literal tab byte** instead of the
escaped `\t`, producing invalid JSON that caused `error:
InvalidSourceMap`.
The root cause was in `src/bun.js/bindings/highway_strings.cpp` where
the scalar fallback path had:
```cpp
if (char_ >= 127 || (char_ < 0x20 && char_ != 0x09) || ...)
```
This **exempted tab characters** (0x09) from being detected as needing
escape, while the SIMD path correctly detected them. The fix removes the
`&& char_ != 0x09` exemption so both paths consistently escape tabs.
## How did you verify your code works?
Added regression test in `test/regression/issue/22003.test.ts` that:
- Creates a file with a tab character in its filename
- Builds it with sourcemap generation
- Verifies the sourcemap is valid JSON
- Checks that the tab is escaped as `\t` (not a literal byte)
The test **fails on system bun** (produces invalid JSON with literal
tab):
```bash
USE_SYSTEM_BUN=1 bun test test/regression/issue/22003.test.ts
# error: JSON Parse error: Unterminated string
```
The test **passes with the fix** (tab properly escaped):
```bash
bun bd test test/regression/issue/22003.test.ts
# ✓ 1 pass
```
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
### What does this PR do?
Sets the `reportViolationForUnsafeEval` global object method table
function pointer. JSC does not check if the pointer is null before
calling.
Fixes#23048Fixes#22000
### How did you verify your code works?
Manually, and added a test for codeGenerationOptions.
### What does this PR do?
Adds a new test which mirrors the _callback errors don't crash the
client_ test but doesn't rely on IPC.
### How did you verify your code works?
Hopefully, CI
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?
Three things:
- JSCommonJSExtensions.cpp `onAssign` was returning out of sync numbers
instead of `BunLoaderTypeJS`/`BunLoaderTypeNAPI`/...
- `bun.schema.api.Loader._none` was 255 instead of 254 like
`BunLoaderTypeNone`
- `Bun__transpileFile` used `bun.options.Loader.Optional` instead of
`bun.schema.api.Loader`. `bun.options.Loader` does not have a type kept
in sync in C++.
### How did you verify your code works?
Added tests that make sure the correct loader is used for modules
required with custom _extensions functions
## Summary
Fixes a macOS kernel (XNU) bug where `accept()` can return a valid
socket descriptor but with `addrlen=0`, indicating an already-dead
socket.
This occurs when an IPv4 connection to an IPv6 dual-stack listener is
immediately aborted (RST packet). The fix detects this condition on
Darwin and handles it intelligently - preserving buffered data when
present, discarding truly dead sockets when not.
## Background
This implements the equivalent of the bugfix from capnproto:
https://github.com/capnproto/capnproto/pull/2365
The issue manifests as:
1. IPv4 connection made to IPv6 dual-stack listener
2. Connection immediately aborted (sends RST packet)
3. `accept()` returns valid socket descriptor but `addrlen=0`
4. Socket may have buffered data from `connectx()` or be truly dead
## Enhanced Data-Preserving Solution
Unlike simple "close immediately" approaches, this fix **prevents data
loss** from the `connectx()` edge case:
**Race Condition Scenario:**
1. Client uses `connectx()` to send data immediately during connection
2. Network abort (RST) occurs after data is buffered but before full
connection establishment
3. Darwin kernel returns `socklen=0` but socket has buffered data
4. **Our fix preserves this data instead of losing it**
**Logic:**
```c
if (addr->len == 0) {
/* Check if there's any pending data before discarding the socket */
char peek_buf[1];
ssize_t has_data = recv(accepted_fd, peek_buf, 1, MSG_PEEK | MSG_DONTWAIT);
if (has_data <= 0) {
/* No data available, socket is truly dead - discard it */
bsd_close_socket(accepted_fd);
continue; /* Try to accept the next connection */
}
/* If has_data > 0, let the socket through - there's buffered data to read */
}
```
## XNU Kernel Source Analysis
After investigating the Darwin XNU kernel source code, I found this bug
affects **multiple system calls**, not just `accept()`. The bug is
rooted in the kernel's socket layer when protocol-specific functions
return NULL socket addresses.
### Affected System Calls
#### 1. accept() and accept_nocancel() ✅ FIXED
**Location:**
[`/bsd/kern/uipc_syscalls.c:596-605`](https://github.com/apple/darwin-xnu/blob/main/bsd/kern/uipc_syscalls.c#L596-L605)
```c
(void) soacceptlock(so, &sa, 0);
socket_unlock(head, 1);
if (sa == NULL) {
namelen = 0; // ← BUG: Returns socklen=0
if (uap->name) {
goto gotnoname;
}
error = 0;
goto releasefd;
}
```
#### 2. getsockname() ⚠️ ALSO AFFECTED
**Location:**
[`/bsd/kern/uipc_syscalls.c:2601-2603`](https://github.com/apple/darwin-xnu/blob/main/bsd/kern/uipc_syscalls.c#L2601-L2603)
```c
if (sa == 0) {
len = 0; // ← SAME BUG: Returns socklen=0
goto gotnothing;
}
```
#### 3. getpeername() ⚠️ ALSO AFFECTED
**Location:**
[`/bsd/kern/uipc_syscalls.c:2689-2691`](https://github.com/apple/darwin-xnu/blob/main/bsd/kern/uipc_syscalls.c#L2689-L2691)
```c
if (sa == 0) {
len = 0; // ← SAME BUG: Returns socklen=0
goto gotnothing;
}
```
### System Calls NOT Affected
#### connect() and connectx() ✅ SAFE
**Locations:**
-
[`/bsd/kern/uipc_syscalls.c:686-744`](https://github.com/apple/darwin-xnu/blob/main/bsd/kern/uipc_syscalls.c#L686-L744)
(connect)
-
[`/bsd/kern/uipc_syscalls.c:747+`](https://github.com/apple/darwin-xnu/blob/main/bsd/kern/uipc_syscalls.c#L747)
(connectx)
**Why they're safe:** These functions read socket addresses from
userspace via `getsockaddr()` and pass them to the protocol layer. They
don't receive socket addresses from the network stack, so they can't
encounter the `socklen=0` condition.
### Root Cause
The bug occurs when protocol layer functions (`pru_accept`,
`pru_sockaddr`, `pru_peeraddr`) return NULL socket addresses during
IPv4→IPv6 dual-stack connection race conditions. The kernel returns
`socklen=0` instead of treating it as an error case.
**Key XNU source reference:**
[`/bsd/kern/uipc_socket.c:1544`](https://github.com/apple/darwin-xnu/blob/main/bsd/kern/uipc_socket.c#L1544)
```c
error = (*so->so_proto->pr_usrreqs->pru_accept)(so, nam);
```
**Socket state vs buffered data:** From
[`/bsd/kern/uipc_socket2.c:2227`](https://github.com/apple/darwin-xnu/blob/main/bsd/kern/uipc_socket2.c#L2227):
```c
// Even with SS_CANTRCVMORE set, data can be buffered in so->so_rcv.sb_cc
return so->so_rcv.sb_cc >= so->so_rcv.sb_lowat ||
((so->so_state & SS_CANTRCVMORE) && cfil_sock_data_pending(&so->so_rcv) == 0)
```
## Changes
- Added Darwin-specific check in `bsd_accept_socket()` in
`packages/bun-usockets/src/bsd.c:708-720`
- When `addr->len == 0` after successful `accept()`:
1. Check for buffered data with `recv(MSG_PEEK | MSG_DONTWAIT)`
2. If data exists, let socket through normally (prevents data loss)
3. If no data, close socket and continue accepting
- Only applies to `__APPLE__` builds to avoid affecting other platforms
## Test plan
- [x] Debug build compiles successfully
- [x] Basic HTTP server operations work correctly (exercises accept
path)
- [x] Regression test covers IPv4→IPv6 dual-stack connection abort
scenarios
- [x] Test verifies server doesn't crash/hang when encountering
socklen=0 condition
- [x] Enhanced fix preserves buffered data from connectx() edge cases
The regression test
(`test/regression/issue/darwin-accept-socklen-zero.test.ts`) creates the
exact conditions that trigger this kernel bug:
1. IPv6 dual-stack server (`hostname: "::"`)
2. IPv4 connections (`127.0.0.1`) with immediate abort (RST packets)
3. Concurrent connection attempts to maximize race condition probability
4. Verification that server remains stable and responsive
## Impact Assessment
### For Bun's uSockets Implementation
- **accept() path:** ✅ FIXED with data loss prevention - This PR handles
the primary case affecting network servers
- **connect() path:** ✅ NOT VULNERABLE - connect() doesn't receive
kernel sockaddrs
- **connectx() path:** ✅ NOT VULNERABLE - connectx() doesn't receive
kernel sockaddrs
- **connectx() data:** ✅ PRESERVED - Enhanced fix prevents losing
buffered data from immediate sends
### Additional Considerations
While `getsockname()` and `getpeername()` have the same kernel bug,
they're less critical for server stability since servers primarily use
`accept()` for incoming connections.
🤖 Generated with [Claude Code](https://claude.ai/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>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
### What does this PR do?
Fixes a bug since Bun v1.0.15: `var f = ([1, 2], "hi");`
Fixes a regression since Bun v1.2.22: `var f = (new Array([1, 2]),
"hi");`
Fixes#23287
### How did you verify your code works?
Added a test
### What does this PR do?
fixes#22679
* includes a better error if a package cant be met because of the age
(but would normally)
* logs the resolved one in --verbose (which can be helpful in debugging
to show it does know latest but couldn't use)
* makes bun outdated show in the table when the package isn't true
latest
* includes a rudimentary "stability" check if a later version is in
blacked out time (but only up to 7 days as it goes back to latest with
min age)
For extended security we could also Last-Modified header of the tgz
download and then abort if too new (just like the hash)
| install error with no recent version | bun outdated respecting the
rule |
| --- | --- |
<img width="838" height="119" alt="image"
src="https://github.com/user-attachments/assets/b60916a8-27f6-4405-bfb6-57f9fa8bb0d6"
/> | <img width="609" height="314" alt="image"
src="https://github.com/user-attachments/assets/d8869ff4-8e16-492c-8e4c-9ac1dfa302ba"
/> |
For stable release we will make it use `3d` type syntax instead of magic
second numbers.
### How did you verify your code works?
tests & manual
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
### What does this PR do?
In Bun v1.2.22 a minification for `typeof x === "undefined"` → `typeof x
> "u"` was added. This introduced a regression causing `return (typeof x
!== "undefined", false)` to minify to invalid syntax when
`--minify-syntax` is enabled (this is also enabled for transpilation at
runtime).
This pr fixes the regression making sure `return (typeof x !==
"undefined", false);` minifies correctly to `return !1;`.
fixes#21137
### How did you verify your code works?
Added a regression test.
### What does this PR do?
A bug in our typescript parser was causing `module.foo = foo` to parse
as a typescript namespace. If it didn't end with a semicolon and there's
a statement on the next line it would cause a syntax error. Example:
```ts
module.foo = foo
foo.foo = foo
```
fixes#22929fixes#22883
### How did you verify your code works?
Added a regression test
### What does this PR do?
Adds `expect().toBe()` checks for anchors/aliases. Also adds git commit
the tests were translated from.
### How did you verify your code works?
Manually
adds environment variable for proper tmpdir setup
actual fix for
d2a4fb8124
(which was reverted)
this fixes flakyness in node:fs and node:cluster when using
scripts/runner.node.mjs locally with the --parallel flag
### What does this PR do?
Fixes bugs in the parser bringing it to 90% passing the official
[yaml-test-suite](https://github.com/yaml/yaml-test-suite) (362/400
passing tests)
Still missing from our parser: |- and |+ (about 5%), and cyclic
references.
Translates the yaml-test-suite to our tests.
fixes#22659fixes#22392fixes#22286
### How did you verify your code works?
Added tests for yaml-test-suite and each of the linked issues
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Fixes#23275
### What does this PR do?
This PR fixes a bug where `bunfig.toml` files starting with a UTF-8 BOM
(byte order mark, `U+FEFF` or bytes `0xEF 0xBB 0xBF`) would fail to
parse with an "Unexpected" error.
The fix uses Bun's existing `File.toSource()` function with
`convert_bom: true` option when loading config files. This properly
detects and strips the BOM before parsing, matching the behavior of
other file readers in Bun (like the JavaScript lexer which treats
`0xFEFF` as whitespace).
**Changes:**
- Modified `src/cli/Arguments.zig` to use `bun.sys.File.toSource()` with
BOM conversion instead of manually reading the file
- Simplified the config loading code by removing intermediate file
handle and buffer logic
### How did you verify your code works?
Added comprehensive regression tests in
`test/regression/issue/23275.test.ts` that verify:
1. ✅ `bunfig.toml` with UTF-8 BOM parses correctly without errors
2. ✅ `bunfig.toml` without BOM still works (regression test)
3. ✅ `bunfig.toml` with BOM and actual config content parses the content
correctly
All three tests pass with the debug build:
```
3 pass
0 fail
11 expect() calls
Ran 3 tests across 1 file. [6.41s]
```
🤖 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>
- Added `request` property to the `bun:app` module for better request handling.
- Updated `hmr-runtime-server.ts` to use `RouteMetadata` for improved type safety.
- Introduced `$ERR_SSR_RESPONSE_EXPECTED` error function in builtins for SSR response handling.
- Changed the version of `react-server-dom-bun` from a local link to a specific experimental version in `package.json` and `bun.lock`.
- Updated the import path for `renderToPipeableStream` to use the `.node` variant for compatibility.
- Added `neo-async` as a dependency for `react-server-dom-bun` to ensure proper functionality.
- Updated `HotReloadEvent.zig` to use duplicated strings for extra files to prevent potential memory issues.
- Commented out unused code related to "bun:app" in `bundle_v2.zig` to improve clarity and maintainability.
- Changed module imports from "react-server-dom-bun" to "react-server-dom-webpack" in multiple files.
- Updated dependencies in package.json and bun.lock to use newer versions of React, React DOM, React Refresh, and the new react-server-dom-webpack package.
- Adjusted type definitions in bun-types to reflect changes in import sources.
This update enhances compatibility with the latest React features and improves the overall framework structure.
This update adds detailed logging to the DevServer's initialization process, including framework configuration details such as server components, React Fast Refresh, and file system router types. Additionally, it logs the directory being scanned for routes and the number of routes found, improving visibility into the server's setup and routing process.
This update allows users to specify the `--app` flag when running Bun commands. If the flag is provided without additional arguments, the CLI will automatically look for and execute the bun.app.ts or bun.app.js file in the current working directory. If the file is not found, an error message will be displayed, guiding users to the expected file locations.
- Replace bun.outOfMemory() with bun.handleOom(err)
- Replace std.mem.indexOfAny with bun.strings.indexOfAny
- Replace arguments_old with argumentsAsArray
- Fix peechy version to be exact (remove ^)
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
The changes improve state handling during client-side navigation by: -
Using correct function updates with setAppState - Preserving abort
controller on updates - Adding proper cache ID comparison - Moving
router instance to constants
- Remove unused components and utility files from bun-framework-react.
- Update type definitions in bake.private.d.ts to allow for synthetic modules.
- Modify exports in package.json to simplify module access.
- Ensure proper registration of bun:app in the HMR module system.
- Update onServerSideReload function return type in rendering.d.ts to void.
- Modify onServerSideReload declaration in hmr-module.ts to accept both Promise<void> and void.
- Enhance type safety in registerSynthetic function with a generic interface for built-in modules.
Find up to 3 likely duplicate issues for a given GitHub issue.
To do this, follow these steps precisely:
1. Use an agent to check if the GitHub issue (a) is closed, (b) does not need to be deduped (eg. because it is broad product feedback without a specific solution, or positive feedback), or (c) already has a duplicate detection comment (check for the exact HTML marker `<!-- dedupe-bot:marker -->` in the issue comments - ignore other bot comments). If so, do not proceed.
2. Use an agent to view a GitHub issue, and ask the agent to return a summary of the issue
3. Then, launch 5 parallel agents to search GitHub for duplicates of this issue, using diverse keywords and search approaches, using the summary from Step 2. **IMPORTANT**: Always scope searches with `repo:owner/repo` to constrain results to the current repository only.
4. Next, feed the results from Steps 2 and 3 into another agent, so that it can filter out false positives, that are likely not actually duplicates of the original issue. If there are no duplicates remaining, do not proceed.
5. Finally, comment back on the issue with a list of up to three duplicate issues (or zero, if there are no likely duplicates)
Notes (be sure to tell this to your agents, too):
- Use `gh` to interact with GitHub, rather than web fetch
- Do not use other tools, beyond `gh` (eg. don't use other MCP servers, file edit, etc.)
- Make a todo list first
- Always scope searches with `repo:owner/repo` to prevent cross-repo false positives
- For your comment, follow the following format precisely (assuming for this example that you found 3 suspected duplicates):
---
Found 3 possible duplicate issues:
1. <link to issue>
2. <link to issue>
3. <link to issue>
This issue will be automatically closed as a duplicate in 3 days.
- If your issue is a duplicate, please close it and 👍 the existing issue instead
- To prevent auto-closure, add a comment or 👎 this comment
🤖 Generated with [Claude Code](https://claude.ai/code)
@@ -23,12 +23,15 @@ Tip: Bun is already installed and in $PATH. The `bd` subcommand is a package.jso
### Test Organization
If a test is for a specific numbered GitHub Issue, it should be placed in `test/regression/issue/${issueNumber}.test.ts`. Ensure the issue number is **REAL** and not a placeholder!
If no valid issue number is provided, find the best existing file to modify instead, such as;
-`test/js/bun/` - Bun-specific API tests (http, crypto, ffi, shell, etc.)
-`test/js/node/` - Node.js compatibility tests
-`test/js/web/` - Web API tests (fetch, WebSocket, streams, etc.)
// Assert the exit code last. This gives you a more useful error message on test failure.
expect(exitCode).toBe(0);
});
```
- Always use `port: 0`. Do not hardcode ports. Do not use your own random port number function.
- Use `normalizeBunSnapshot` to normalize snapshot output of the test.
- NEVER write tests that check for no "panic" or "uncaught exception" or similar in the test output. That is NOT a valid test.
- Use `tempDir` from `"harness"` to create a temporary directory. **Do not** use `tmpdirSync` or `fs.mkdtempSync` to create temporary directories.
- When spawning processes, tests should expect(stdout).toBe(...) BEFORE expect(exitCode).toBe(0). This gives you a more useful error message on test failure.
- **CRITICAL**: Do not write flaky tests. Do not use `setTimeout` in tests. Instead, `await` the condition to be met. You are not testing the TIME PASSING, you are testing the CONDITION.
- **CRITICAL**: Verify your test fails with `USE_SYSTEM_BUN=1 bun test <file>` and passes with `bun bd test <file>`. Your test is NOT VALID if it passes with `USE_SYSTEM_BUN=1`.
- **C++ code** (`src/bun.js/bindings/*.cpp`): JavaScriptCore bindings, Web APIs
- **TypeScript** (`src/js/`): Built-in JavaScript modules with special syntax (see JavaScript Modules section)
- **Generated code**: Many files are auto-generated from `.classes.ts` and other sources
- **Generated code**: Many files are auto-generated from `.classes.ts` and other sources. Bun will automatically rebuild these files when you make changes to them.
### Core Source Organization
@@ -178,3 +187,5 @@ Built-in JavaScript modules use special syntax and are organized as:
10.**Debug builds** - Use `BUN_DEBUG_QUIET_LOGS=1` to disable debug logging, or `BUN_DEBUG_<scopeName>=1` to enable specific `Output.scoped(.${scopeName}, .visible)`s
11.**Be humble & honest** - NEVER overstate what you got done or what actually works in commits, PRs or in messages to the user.
12.**Branch names must start with `claude/`** - This is a requirement for the CI to work.
**ONLY** push up changes after running `bun bd test <file>` and ensuring your tests pass.
@@ -2,7 +2,21 @@ Configuring a development environment for Bun can take 10-30 minutes depending o
If you are using Windows, please refer to [this guide](https://bun.com/docs/project/building-windows)
## Install Dependencies
## Using Nix (Alternative)
A Nix flake is provided as an alternative to manual dependency installation:
```bash
nix develop
# or explicitly use the pure shell
# nix develop .#pure
exportCMAKE_SYSTEM_PROCESSOR=$(uname -m)
bun bd
```
This provides all dependencies in an isolated, reproducible environment without requiring sudo.
## Install Dependencies (Manual)
Using your system's package manager, install Bun's dependencies:
@@ -149,7 +163,7 @@ Bun generally takes about 2.5 minutes to compile a debug build when there are Zi
- Batch up your changes
- Ensure zls is running with incremental watching for LSP errors (if you use VSCode and install Zig and run `bun run build` once to download Zig, this should just work)
- Prefer using the debugger ("CodeLLDB" in VSCode) to step through the code.
- Use debug logs. `BUN_DEBUG_<scope>=1` will enable debug logging for the corresponding `Output.scoped(.<scope>, false)` logs. You can also set `BUN_DEBUG_QUIET_LOGS=1` to disable all debug logging that isn't explicitly enabled. To dump debug lgos into a file, `BUN_DEBUG=<path-to-file>.log`. Debug logs are aggressively removed in release builds.
- Use debug logs. `BUN_DEBUG_<scope>=1` will enable debug logging for the corresponding `Output.scoped(.<scope>, .hidden)` logs. You can also set `BUN_DEBUG_QUIET_LOGS=1` to disable all debug logging that isn't explicitly enabled. To dump debug lgos into a file, `BUN_DEBUG=<path-to-file>.log`. Debug logs are aggressively removed in release builds.
- src/js/\*\*.ts changes are pretty much instant to rebuild. C++ changes are a bit slower, but still much faster than the Zig code (Zig is one compilation unit, C++ is many).
Requires [`hyperfine`](https://github.com/sharkdp/hyperfine). The goal of this benchmark is to compare installation performance of Bun with other package managers _when caches are hot_.
This is a [T3 Stack](https://create.t3.gg/) project bootstrapped with `create-t3-app`.
### With lockfile, online mode
## What's next? How do I make an app with this?
To run the benchmark with the standard "install" command for each package manager:
We try to keep this project as simple as possible, so you can start with just the scaffolding we set up for you, and add additional things later when they become necessary.
If you are not familiar with the different technologies used in this project, please refer to the respective docs. If you still are in the wind, please join our [Discord](https://t3.gg/discord) and ask for help.
### With lockfile, offline mode
- [Next.js](https://nextjs.org)
- [NextAuth.js](https://next-auth.js.org)
- [Prisma](https://prisma.io)
- [Drizzle](https://orm.drizzle.team)
- [Tailwind CSS](https://tailwindcss.com)
- [tRPC](https://trpc.io)
Even though all packages are cached, some tools may hit the npm API during the version resolution step. (This is not the same as re-downloading a package.) To entirely avoid network calls, the other package managers require `--prefer-offline/--offline` flag. To run the benchmark using "offline" mode:
Then visit [http://localhost:3000](http://localhost:3000).
Follow our deployment guides for [Vercel](https://create.t3.gg/en/deployment/vercel), [Netlify](https://create.t3.gg/en/deployment/netlify) and [Docker](https://create.t3.gg/en/deployment/docker) for more information.
Contextual `data` can be attached to a new WebSocket in the `.upgrade()` call. This data is made available on the `ws.data` property inside the WebSocket handlers.
To strongly type `ws.data`, add a `data` property to the `websocket` handler object. This types `ws.data` across all lifecycle hooks.
// TypeScript: specify the type of ws.data like this
data:{}asWebSocketData,
// handler called when a message is received
asyncmessage(ws,message){
// ws.data is now properly typed as WebSocketData
constuser=getUserFromToken(ws.data.authToken);
awaitsaveMessageToDatabase({
@@ -145,6 +150,10 @@ Bun.serve<WebSocketData>({
});
```
{% callout %}
**Note:** Previously, you could specify the type of `ws.data` using a type parameter on `Bun.serve`, like `Bun.serve<MyData>({...})`. This pattern was removed due to [a limitation in TypeScript](https://github.com/microsoft/TypeScript/issues/26242) in favor of the `data` property shown above.
{% /callout %}
To connect to this server from the browser, create a new `WebSocket`.
Bun's `ServerWebSocket` implementation implements a native publish-subscribe API for topic-based broadcasting. Individual sockets can `.subscribe()` to a topic (specified with a string identifier) and `.publish()` messages to all other subscribers to that topic (excluding itself). This topic-based broadcast API is similar to [MQTT](https://en.wikipedia.org/wiki/MQTT) and [Redis Pub/Sub](https://redis.io/topics/pubsub).
@@ -221,6 +221,38 @@ Bun uses a global cache at `~/.bun/install/cache/` to minimize disk usage. Packa
For complete documentation refer to [Package manager > Global cache](https://bun.com/docs/install/cache).
## Minimum release age
To protect against supply chain attacks where malicious packages are quickly published, you can configure a minimum age requirement for npm packages. Package versions published more recently than the specified threshold (in seconds) will be filtered out during installation.
```bash
# Only install package versions published at least 3 days ago
$ bun add @types/bun --minimum-release-age 259200 # seconds
```
You can also configure this in `bunfig.toml`:
```toml
[install]
# Only install package versions published at least 3 days ago
- Only affects new package resolution - existing packages in `bun.lock` remain unchanged
- All dependencies (direct and transitive) are filtered to meet the age requirement when being resolved
- When versions are blocked by the age gate, a stability check detects rapid bugfix patterns
- If multiple versions were published close together just outside your age gate, it extends the filter to skip those potentially unstable versions and selects an older, more mature version
- Searches up to 7 days after the age gate, however if still finding rapid releases it ignores stability check
- Exact version requests (like `package@1.1.1`) still respect the age gate but bypass the stability check
- Versions without a `time` field are treated as passing the age check (npm registry should always provide timestamps)
For more advanced security scanning, including integration with services & custom filtering, see [Package manager > Security Scanner API](https://bun.com/docs/install/security-scanner-api).
## Configuration
The default behavior of `bun install` can be configured in `bunfig.toml`. The default values are shown below.
The `--tolerate-republish` flag makes `bun publish` exit with code 0 instead of code 1 when attempting to republish over an existing version number. This is useful in automated workflows where republishing the same version might occur and should not be treated as an error.
Exit with code 0 instead of 1 if the package version already exists. Useful in CI/CD where jobs may be re-run.
```sh
$ bun publish --tolerate-republish
```
Without this flag, attempting to publish a version that already exists will result in an error and exit code 1. With this flag, the command will exit successfully even when trying to republish an existing version.
### `--gzip-level`
Specify the level of gzip compression to use when packing the package. Only applies to `bun publish` without a tarball path argument. Values range from `0` to `9` (default is `9`).
@@ -7,7 +7,7 @@ When building a WebSocket server, it's typically necessary to store some identif
With [Bun.serve()](https://bun.com/docs/api/websockets#contextual-data), this "contextual data" is set when the connection is initially upgraded by passing a `data` parameter in the `server.upgrade()` call.
```ts
Bun.serve<{socketId: number}>({
Bun.serve({
fetch(req,server){
constsuccess=server.upgrade(req,{
data:{
@@ -20,6 +20,9 @@ Bun.serve<{ socketId: number }>({
// ...
},
websocket:{
// TypeScript: specify the type of ws.data like this
data:{}as{socketId: number},
// define websocket handlers
asyncmessage(ws,message){
// the contextual data is available as the `data` property
@@ -89,6 +89,12 @@ $ bun install --linker isolated
Isolated installs create strict dependency isolation similar to pnpm, preventing phantom dependencies and ensuring more deterministic builds. For complete documentation, see [Isolated installs](https://bun.com/docs/install/isolated).
To protect against supply chain attacks, set a minimum age (in seconds) for package versions:
```bash
$ bun install --minimum-release-age 259200# 3 days
```
{% details summary="Configuring behavior" %}
The default behavior of `bun install` can be configured in `bunfig.toml`:
By default, Bun uses the **hoisted** installation strategy for all projects. To use isolated installs, you must explicitly specify the `--linker isolated` flag or set it in your configuration file.
- **Workspaces**: Bun uses **isolated** installs by default to prevent hoisting-related bugs
- **Single projects**: Bun uses **hoisted** installs by default
To override the default, use `--linker hoisted` or `--linker isolated`, or set it in your configuration file.
## How isolated installs work
@@ -174,14 +177,13 @@ The main difference is that Bun uses symlinks in `node_modules` while pnpm uses
## When to use isolated installs
**Use isolated installs when:**
**Isolated installs are the default for workspaces.** You may want to explicitly enable them for single projects when:
- Working in monorepos with multiple packages
- Strict dependency management is required
- Preventing phantom dependencies is important
- Building libraries that need deterministic dependencies
**Use hoisted installs when:**
**Switch to hoisted installs (including for workspaces) when:**
- Working with legacy code that assumes flat `node_modules`
- Compatibility with existing build tools is required
@@ -38,9 +38,21 @@ In the root `package.json`, the `"workspaces"` key is used to indicate which sub
```
{% callout %}
**Glob support** — Bun supports full glob syntax in `"workspaces"` (see [here](https://bun.com/docs/api/glob#supported-glob-patterns) for a comprehensive list of supported syntax), _except_ for exclusions (e.g. `!**/excluded/**`), which are not implemented yet.
**Glob support** — Bun supports full glob syntax in `"workspaces"`, including negative patterns (e.g. `!**/excluded/**`). See [here](https://bun.com/docs/api/glob#supported-glob-patterns) for a comprehensive list of supported syntax.
{% /callout %}
```json
{
"name":"my-project",
"version":"1.0.0",
"workspaces":[
"packages/**",
"!packages/**/test/**",
"!packages/**/template/**"
]
}
```
Each workspace has it's own `package.json`. When referencing other packages in the monorepo, semver or workspace protocols (e.g. `workspace:*`) can be used as the version field in your `package.json`.
@@ -9,20 +9,29 @@ Run `bun init` to scaffold a new project. It's an interactive tool; for this tut
```bash
$ bun init
bun init helps you get started with a minimal project and tries to
guess sensible defaults. Press ^C anytime to quit.
package name (quickstart):
entry point (index.ts):
? Select a project template - Press return to submit.
❯ Blank
React
Library
Done! A package.json file was saved in the current directory.
+ index.ts
+ .gitignore
+ tsconfig.json (for editor auto-complete)
+ README.md
✓ Select a project template: Blank
+ .gitignore
+ index.ts
+ tsconfig.json (for editor autocomplete)
+ README.md
To get started, run:
bun run index.ts
bun run index.ts
bun install v$BUN_LATEST_VERSION
+ @types/bun@$BUN_LATEST_VERSION
+ typescript@5.9.2
7 packages installed
```
Since our entry point is a `*.ts` file, Bun generates a `tsconfig.json` for you. If you're using plain JavaScript, it will generate a [`jsconfig.json`](https://code.visualstudio.com/docs/languages/jsconfig) instead.
@@ -88,11 +97,15 @@ Bun can also execute `"scripts"` from your `package.json`. Add the following scr
The `--concurrent` CLI flag will override this setting when specified.
### `test.onlyFailures`
When enabled, only failed tests are displayed in the output. This helps reduce noise in large test suites by hiding passing tests. Default `false`.
```toml
[test]
onlyFailures=true
```
This is equivalent to using the `--only-failures` flag when running `bun test`.
### `test.reporter`
Configure the test reporter settings.
#### `test.reporter.dots`
Enable the dots reporter, which displays a compact output showing a dot for each test. Default `false`.
```toml
[test.reporter]
dots=true
```
#### `test.reporter.junit`
Enable JUnit XML reporting and specify the output file path.
```toml
[test.reporter]
junit="test-results.xml"
```
This generates a JUnit XML report that can be consumed by CI systems and other tools.
### `test.randomize`
Run tests in random order. Default `false`.
```toml
[test]
randomize=true
```
This helps catch bugs related to test interdependencies by running tests in a different order each time. When combined with `seed`, the random order becomes reproducible.
The `--randomize` CLI flag will override this setting when specified.
### `test.seed`
Set the random seed for test randomization. This option requires `randomize` to be `true`.
```toml
[test]
randomize=true
seed=2444615283
```
Using a seed makes the randomized test order reproducible across runs, which is useful for debugging flaky tests. When you encounter a test failure with randomization enabled, you can use the same seed to reproduce the exact test order.
The `--seed` CLI flag will override this setting when specified.
### `test.rerunEach`
Re-run each test file a specified number of times. Default `0` (run once).
```toml
[test]
rerunEach=3
```
This is useful for catching flaky tests or non-deterministic behavior. Each test file will be executed the specified number of times.
The `--rerun-each` CLI flag will override this setting when specified.
## Package manager
Package management is a complex issue; to support a range of use cases, the behavior of `bun install` can be configured under the `[install]` section.
@@ -570,6 +645,20 @@ Valid values are:
{% /table %}
### `install.minimumReleaseAge`
Configure a minimum age (in seconds) for npm package versions. Package versions published more recently than this threshold will be filtered out during installation. Default is `null` (disabled).
```toml
[install]
# Only install package versions published at least 3 days ago
minimumReleaseAge=259200
# These packages will bypass the 3-day minimum age requirement
For more details see [Minimum release age](https://bun.com/docs/cli/install#minimum-release-age) in the install documentation.
<!-- ## Debugging -->
<!--
@@ -597,7 +686,7 @@ editor = "code"
The `bun run` command can be configured under the `[run]` section. These apply to the `bun run` command and the `bun` command when running a file or executable or script.
Currently, `bunfig.toml` isn't always automatically loaded for `bun run` in a local project (it does check for a global `bunfig.toml`), so you might still need to pass `-c` or `-c=bunfig.toml` to use these settings.
Currently, `bunfig.toml` is only automatically loaded for `bun run` in a local project (it doesn't check for a global `.bunfig.toml`).
### `run.shell` - use the system shell or Bun's shell
The full specification of this algorithm are officially documented in the [Node.js documentation](https://nodejs.org/api/modules.html); we won't rehash it here. Briefly: if you import `from "foo"`, Bun scans up the file system for a `node_modules` directory containing the package `foo`.
### NODE_PATH
Bun supports `NODE_PATH` for additional module resolution directories:
```bash
NODE_PATH=./packages bun run src/index.js
```
```ts
// packages/foo/index.js
export const hello = "world";
// src/index.js
import { hello } from "foo";
```
Multiple paths use the platform's delimiter (`:` on Unix, `;` on Windows):
```bash
NODE_PATH=./packages:./lib bun run src/index.js # Unix/macOS
NODE_PATH=./packages;./lib bun run src/index.js # Windows
```
Once it finds the `foo` package, Bun reads the `package.json` to determine how the package should be imported. To determine the package's entrypoint, Bun first reads the `exports` field and checks for the following conditions.
The dots reporter shows `.` for passing tests and `F` for failures—useful for large test suites.
```sh
$ bun test --dots
$ bun test --reporter=dots
```
### JUnit XML Reporter
For CI/CD environments, Bun supports generating JUnit XML reports. JUnit XML is a widely-adopted format for test results that can be parsed by many CI/CD systems, including GitLab, Jenkins, and others.
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.