Commit Graph

4532 Commits

Author SHA1 Message Date
Jarred Sumner
e90070866e Merge branch 'main' into claude/cjs-code-splitting 2026-01-22 15:13:53 -08:00
Ciro Spaciari
2582e6f98e fix(http2): fix settings, window size handling, and dynamic header buffer allocation (#26119)
## Summary

This PR fixes multiple HTTP/2 protocol compliance issues that were
causing stream errors with various HTTP/2 clients (Fauna, gRPC/Connect,
etc.).
fixes https://github.com/oven-sh/bun/issues/12544
fixes https://github.com/oven-sh/bun/issues/25589
### Key Fixes

**Window Size and Settings Handling**
- Fix initial stream window size to use `DEFAULT_WINDOW_SIZE` until
`SETTINGS_ACK` is received
- Per RFC 7540 Section 6.5.1: The sender can only rely on settings being
applied AFTER receiving `SETTINGS_ACK`
- Properly adjust existing stream windows when `INITIAL_WINDOW_SIZE`
setting changes (RFC 7540 Section 6.9.2)

**Header List Size Enforcement**  
- Implement `maxHeaderListSize` checking per RFC 7540 Section 6.5.2
- Track cumulative header list size using HPACK entry overhead (32 bytes
per RFC 7541 Section 4.1)
- Reject streams with `ENHANCE_YOUR_CALM` when header list exceeds
configured limit

**Custom Settings Support**
- Add validation for `customSettings` option (up to 10 custom settings,
matching Node.js `MAX_ADDITIONAL_SETTINGS`)
- Validate setting IDs are in range `[0, 0xFFFF]` per RFC 7540
- Validate setting values are in range `[0, 2^32-1]`

**Settings Validation Improvements**
- Use float comparison for settings validation to handle large values
correctly (was using `toInt32()` which truncates)
- Use proper `HTTP2_INVALID_SETTING_VALUE_RangeError` error codes for
Node.js compatibility

**BufferFallbackAllocator** - New allocator that tries a provided buffer
first, falls back to heap:
- Similar to `std.heap.stackFallback` but accepts external buffer slice
- Used with `shared_request_buffer` (16KB threadlocal) for common cases
- Falls back to `bun.default_allocator` for large headers

## Test Plan

- [x] `bun bd` compiles successfully
- [x] Node.js HTTP/2 tests pass: `bun bd
test/js/node/test/parallel/test-http2-connect.js`
- [x] New regression tests for frame size issues: `bun bd test
test/regression/issue/25589.test.ts`
- [x] HTTP/2 continuation tests: `bun bd test
test/js/node/http2/node-http2-continuation.test.ts`

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
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: Jarred Sumner <jarred@jarredsumner.com>
2026-01-22 14:35:18 -08:00
mmitchellg5
85080f7949 fix: handle DT_UNKNOWN in dir_iterator for bind-mounted filesystems (#25838)
### What does this PR do?
Fixes #24007
Possibly fixes https://github.com/oven-sh/bun/issues/18902,
https://github.com/oven-sh/bun/issues/7412

Some filesystems (bind mounts, FUSE, NFS) don't provide `d_type` in
directory entries, returning `DT_UNKNOWN`. This caused glob and
recursive readdir to skip entries entirely.

## Problem
On Linux filesystems that don't populate `d_type` in directory entries
(bind mounts, FUSE, NFS, some ext4 configurations), `readdir()` returns
`DT_UNKNOWN` instead of the actual file type. This caused:
- `Bun.Glob` to skip files/directories entirely
- `fs.readdirSync(..., {recursive: true})` to not recurse into
subdirectories
- `fs.readdirSync(..., {withFileTypes: true})` to report incorrect types

## Solution
Implemented a **lazy `lstatat()` fallback** when `d_type == DT_UNKNOWN`:

- **`sys.zig`**: Added `lstatat()` function - same as `fstatat()` but
with `AT_SYMLINK_NOFOLLOW` flag to correctly identify symlinks
- **`GlobWalker.zig`**: When encountering `.unknown` entries, first
check if filename matches pattern, then call `lstatat()` only if needed
- **`node_fs.zig`**: Handle `.unknown` in both async and sync recursive
readdir paths; propagate resolved kind to Dirent objects
- **`dir_iterator.zig`**: Return `.unknown` for `DT_UNKNOWN` entries,
letting callers handle lazy stat

**Why `lstatat` instead of `fstatat`?** We use `AT_SYMLINK_NOFOLLOW` to
preserve consistent behavior with normal filesystems - symlinks should
be reported as symlinks, not as their target type. This matches [Node.js
behavior](https://github.com/nodejs/node/blob/main/lib/internal/fs/utils.js#L251-L269)
which uses `lstat()` for the DT_UNKNOWN fallback, and follows the lazy
stat pattern established in PR #18172.

### How did you verify your code works?

**Testing:**
- Regression test: `test/regression/issue/24007.test.ts`
- FUSE filesystem test: `test/cli/run/glob-on-fuse.test.ts` (reuses
`fuse-fs.py` from PR #18172, includes symlink verification)
- All existing glob/readdir tests pass
- **Verified in Docker bind-mount environment:**
  - Official Bun: `0 files`
  - Patched Bun: `3 files`

**Performance:** No impact on normal filesystems - the `.unknown` branch
is only hit when `d_type == DT_UNKNOWN`. The lazy stat pattern avoids
unnecessary syscalls by checking pattern match first.

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-22 13:44:49 -08:00
Claude Bot
790a758885 test(bundler): run /test.js runtime files in splitting tests
Address review feedback by actually running the /test.js runtime files
that verify shared module behavior across entry points:

- CJSSharedModuleSideEffects: verifies shared module side effects run
  only once when requiring both entry points
- LiveBindingSetters: verifies shared state is preserved when importing
  both entry points sequentially

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 13:31:43 +00:00
robobun
2a9980076d feat(windows): Add Windows ARM64 support (#26215) 2026-01-22 04:22:45 -08:00
Claude Bot
a072337234 test(bundler): add comprehensive ESM/CJS syntax edge case tests for code splitting
Add 42 new edge case tests covering all permutations of ESM and CJS syntax:

- require() variations: require of ESM, destructuring default, mixed with import
- await import() variations: top-level await, inside async functions, conditional
- module.exports variations: object pattern, class, function, exports.x pattern
- export { x } from: basic, renamed, chained through multiple files
- export * from: basic, multiple sources, as namespace
- export { default } from: basic, renamed, named to default
- Circular dependencies: ESM, CJS, export star, three-way
- Mixed syntax: ESM importing CJS requiring ESM, CJS requiring ESM importing CJS
- __esModule interop: CJS with __esModule flag, dynamic imports
- Live bindings: getter/setter patterns, CJS behavior differences

All tests include both ESM and CJS output format variants where applicable.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 09:38:18 +00:00
Claude Bot
750a8ae412 test(bundler): strengthen splitting tests with assertNotPresent assertions
Add assertNotPresent assertions to verify that shared code is actually
extracted to separate chunks rather than just testing runtime output.

Tests improved:
- SharedVendorChunk / SharedVendorChunkCJS
- SharedDefaultExport / SharedDefaultExportCJS
- SharedJSONImport / SharedJSONImportCJS
- AsyncAwaitDynamicImport / AsyncAwaitDynamicImportCJS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 05:19:23 +00:00
Claude Bot
e2120e0bb7 test(bundler): add comprehensive code splitting tests adapted from webpack
Add 31 new test cases (with ESM and CJS variants) adapted from webpack's
test suite covering:

- Basic dynamic imports
- CJS module dynamic imports
- Duplicate dynamic imports to same module
- Circular dynamic imports
- Shared vendor chunks
- Multiple shared dependencies
- Deep dependency chains
- Mixed ESM and CJS sources
- Re-exports and star exports
- Default export sharing
- Class export sharing
- JSON imports
- Async/await with dynamic imports
- Many entry points sharing code
- Partially shared dependencies
- Node modules style imports

All tests run in both ESM and CJS output formats to ensure code splitting
works correctly in both modes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 04:39:58 +00:00
Claude Bot
a963c7c4ae feat(bundler): add code splitting support for CommonJS output format
Previously, code splitting was only supported with ESM output format.
This change adds support for code splitting when using --format=cjs.

When code splitting with CJS output:
- Shared chunks export symbols via `module.exports = { alias: symbol, ... }`
- Entry points import from chunks via `var { alias } = require("./chunk.js")`
- CommonJS module caching ensures shared chunks are only executed once

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 04:23:23 +00:00
robobun
136d345752 fix(install): show dependency name when file: path resolution fails (#26340)
## Summary
- When `bun install` encounters a stale lockfile with a `file:`
dependency path that differs from the package.json, it now shows which
dependency caused the issue instead of the misleading "Bun could not
find a package.json file to install from" error.

## Test plan
- Added regression test in `test/regression/issue/26337.test.ts`
- Verified test fails with system bun (`USE_SYSTEM_BUN=1 bun test
test/regression/issue/26337.test.ts`)
- Verified test passes with debug build (`bun bd test
test/regression/issue/26337.test.ts`)

Fixes #26337

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 18:41:15 -08:00
Jarred Sumner
37c41137f8 feat(transpiler): add replMode option for REPL transforms (#26246)
## Summary

Add a new `replMode` option to `Bun.Transpiler` that transforms code for
interactive REPL evaluation. This enables building a Node.js-compatible
REPL using `Bun.Transpiler` with `vm.runInContext` for persistent
variable scope.

## Features

- **Expression result capture**: Wraps the last expression in `{
__proto__: null, value: expr }` for result capture
- **IIFE wrappers**: Uses sync/async IIFE wrappers to avoid extra
parentheses around object literals in output
- **Variable hoisting**: Hoists `var`/`let`/`const` declarations outside
the IIFE for persistence across REPL lines
- **const → let conversion**: Converts `const` to `let` for REPL
mutability (allows re-declaration)
- **Function hoisting**: Hoists function declarations with
`this.funcName = funcName` assignment for vm context persistence
- **Class hoisting**: Hoists class declarations with `var` for vm
context persistence
- **Object literal detection**: Auto-detects object literals (code
starting with `{` without trailing `;`) and wraps them in parentheses

## Usage

```typescript
import vm from "node:vm";

const transpiler = new Bun.Transpiler({
  loader: "tsx",
  replMode: true,
});

const context = vm.createContext({ console, Promise });

async function repl(code: string) {
  const transformed = transpiler.transformSync(code);
  const result = await vm.runInContext(transformed, context);
  return result.value;
}

// Example REPL session
await repl("var x = 10");        // 10
await repl("x + 5");             // 15
await repl("class Counter {}"); // [class Counter]
await repl("new Counter()");    // Counter {}
await repl("{a: 1, b: 2}");     // {a: 1, b: 2} (auto-detected object literal)
await repl("await Promise.resolve(42)"); // 42
```

## Transform Examples

| Input | Output |
|-------|--------|
| `42` | `(() => { return { __proto__: null, value: 42 }; })()` |
| `var x = 10` | `var x; (() => { return { __proto__: null, value: x =
10 }; })()` |
| `await fetch()` | `(async () => { return { __proto__: null, value:
await fetch() }; })()` |
| `{a: 1}` | `(() => { return { __proto__: null, value: ({a: 1}) };
})()` |
| `class Foo {}` | `var Foo; (() => { return { __proto__: null, value:
Foo = class Foo {} }; })()` |

## Files Changed

- `src/ast/repl_transforms.zig`: New module containing REPL transform
logic
- `src/ast/P.zig`: Calls REPL transforms after parsing in REPL mode
- `src/bun.js/api/JSTranspiler.zig`: Adds `replMode` config option and
object literal detection
- `src/options.zig`, `src/runtime.zig`, `src/transpiler.zig`: Propagate
`repl_mode` flag
- `packages/bun-types/bun.d.ts`: TypeScript type definitions
- `test/js/bun/transpiler/repl-transform.test.ts`: Test cases

## Testing

```bash
bun bd test test/js/bun/transpiler/repl-transform.test.ts
```

34 tests covering:
- Basic transform output
- REPL session with node:vm
- Variable persistence across lines
- Object literal detection
- Edge cases (empty input, comments, TypeScript, etc.)
- No-transform cases (await inside async functions)

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-21 13:39:25 -08:00
Dylan Conway
dc203e853a fix patch-bounds-check.test.ts (#26344)
### What does this PR do?

### How did you verify your code works?
2026-01-21 13:22:21 -08:00
robobun
2febdb5b49 feat(cli): add --cpu-prof-md flag for markdown CPU profile output (#26327)
## Summary
- Adds `--cpu-prof-md` flag that outputs CPU profiling data in markdown
format optimized for GitHub rendering and LLM analysis
- Complements the existing `--cpu-prof` flag which outputs Chrome
DevTools JSON format
- `--cpu-prof-md` works standalone or combined with `--cpu-prof` to
generate both formats

## Usage
```bash
# Markdown only
bun --cpu-prof-md script.js

# Both formats
bun --cpu-prof --cpu-prof-md script.js
```

## Example Output

# CPU Profile

| Duration | Samples | Interval | Functions |
|----------|---------|----------|----------|
| 255.7ms | 178 | 1ms | 32 |

**Top 10:** \`fibonacci\` 23.6%, \`fibonacci\` 12.6%, \`parseModule\`
11.7%, \`(anonymous)\` 9.5%, \`loadAndEvaluateModule\` 5.5%,
\`requestSatisfyUtil\` 3.7%, \`main\` 2.7%,
\`moduleDeclarationInstantiation\` 2.6%, \`loadModule\` 2.5%,
\`cacheSatisfyAndReturn\` 2.5%

## Hot Functions (Self Time)

| Self% | Self | Total% | Total | Function | Location |
|------:|-----:|-------:|------:|----------|----------|
| 23.6% | 60.5ms | 23.6% | 60.5ms | \`fibonacci\` | /tmp/test-profile.js
|
| 12.6% | 32.3ms | 100.0% | 1.29s | \`fibonacci\` |
/tmp/test-profile.js:3 |
| 11.7% | 29.9ms | 11.7% | 29.9ms | \`parseModule\` | [native code] |
| 9.5% | 24.3ms | 43.4% | 111.0ms | \`(anonymous)\` | [native code] |
| 5.5% | 14.2ms | 99.9% | 255.5ms | \`loadAndEvaluateModule\` | [native
code] |

## Call Tree (Total Time)

| Total% | Total | Self% | Self | Function | Location |
|-------:|------:|------:|-----:|----------|----------|
| 100.0% | 1.29s | 12.6% | 32.3ms | \`fibonacci\` |
/tmp/test-profile.js:3 |
| 99.9% | 255.5ms | 5.5% | 14.2ms | \`loadAndEvaluateModule\` | [native
code] |
| 86.0% | 219.9ms | 1.3% | 3.3ms | \`moduleEvaluation\` | [native code]
|
| 43.4% | 111.0ms | 9.5% | 24.3ms | \`(anonymous)\` | [native code] |

## Function Details

### \`fibonacci\`

- **Location:** \`/tmp/test-profile.js:3\`
- **Self:** 12.6% (32.3ms) | **Total:** 100.0% (1.29s)
- **Called by:** \`fibonacci\` (864), \`main\` (68)
- **Calls:** \`fibonacci\` (864), \`fibonacci\` (44), \`fibonacci\` (2)

### \`main\`

- **Location:** \`/tmp/test-profile.js:9\`
- **Self:** 0.0% (0us) | **Total:** 38.4% (98.2ms)
- **Called by:** \`(module)\` (72)
- **Calls:** \`fibonacci\` (68), \`inspect\` (2), \`fibonacci\` (2)

## Files

| Self% | Self | File |
|------:|-----:|------|
| 58.8% | 150.6ms | \`[native code]\` |
| 40.1% | 102.6ms | \`/tmp/test-profile.js\` |
| 0.9% | 2.4ms | \`bun:main\` |

## Test plan
- [x] `--cpu-prof-md` generates `.md` file with markdown tables
- [x] `--cpu-prof-md` works standalone without `--cpu-prof`
- [x] Both flags together generate both `.cpuprofile` and `.md` files
- [x] Custom filename with `--cpu-prof-name` works
- [x] Custom directory with `--cpu-prof-dir` works
- [x] All 9 tests pass

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <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>
2026-01-21 13:21:01 -08:00
Jarred Sumner
b6b3626c14 fix(bindings): handle errors from String.toJS() for oversized strings (#26213)
## Summary

- When a string exceeds `WTF::String::MaxLength` (~4GB),
`bun.String.createUninitialized()` returns a `.Dead` tag
- The C++ layer now properly throws `ERR_STRING_TOO_LONG` when this
happens
- Updated `String.toJS()` in Zig to return `bun.JSError!jsc.JSValue`
instead of just `jsc.JSValue`
- Updated ~40 Zig caller files to handle the error with `try`
- C++ callers updated with `RETURN_IF_EXCEPTION` checks

## Test plan

- [x] `bun bd test test/js/node/buffer.test.js` - 449 tests pass
- [x] `bun bd
test/js/node/test/parallel/test-buffer-tostring-rangeerror.js` - passes

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
2026-01-21 13:01:25 -08:00
robobun
08103aa2ff fix(compile): ensure bytecode alignment accounts for section header (#26299)
## Summary

Fixes bytecode alignment in standalone executables to prevent crashes
when loading bytecode cache on Windows.

The bytecode offset needs to be aligned such that when loaded at
runtime, the bytecode pointer is 128-byte aligned. Previously, alignment
was based on arbitrary memory addresses during compilation, which didn't
account for the 8-byte section header prepended at runtime. This caused
the bytecode to be misaligned, leading to segfaults in
`JSC::CachedJSValue::decode` on Windows.

## Root Cause

At runtime, embedded data starts 8 bytes after the PE/Mach-O section
virtual address (which is page-aligned, hence 128-byte aligned). For
bytecode at offset `O` to be aligned:
```
(section_va + 8 + O) % 128 == 0
=> (8 + O) % 128 == 0
=> O % 128 == 120
```

The previous code used `std.mem.alignInSlice()` which found aligned
addresses based on the compilation buffer's arbitrary address, not
accounting for the 8-byte header offset at load time.

## Changes

- **`src/StandaloneModuleGraph.zig`**: Calculate bytecode offset to
satisfy `offset % 128 == 120` instead of using `alignInSlice`
- **`test/regression/issue/26298.test.ts`**: Added regression tests for
bytecode cache in standalone executables

## Test plan

- [x] Added regression test `test/regression/issue/26298.test.ts` with 3
test cases
- [x] Existing `HelloWorldBytecode` test passes
- [x] Build succeeds

Fixes #26298

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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 22:42:38 -08:00
Jarred Sumner
bb4d150aed Try using internal string-width in node:readline (#26306)
### What does this PR do?

Remove NFKDC normalization and stripVTControlCharacters since
Bun.stringWidth does this now

### How did you verify your code works?

ci

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-20 22:41:14 -08:00
robobun
dbad2857ea fix(test): delete setTimeout.clock property when disabling fake timers (#26285)
## Summary

- Fixes the `setTimeout.clock` property not being properly deleted after
`jest.useRealTimers()` is called
- Previously, the property was set to `false` instead of deleted,
causing `hasOwnProperty` checks to return `true`
- This broke React Testing Library and other libraries that check for
fake timers using `Object.prototype.hasOwnProperty.call(setTimeout,
'clock')`

## Changes

- Added `JSValue.deleteProperty()` binding in Zig to call JSC's
`deleteProperty()` method
- Updated `setFakeTimerMarker()` in `FakeTimers.zig` to delete the
`clock` property when disabling fake timers
- Updated existing test in `test/regression/issue/25869.test.ts` to
verify correct behavior
- Added new regression test in `test/regression/issue/26284.test.ts`

## Test plan

- [x] Verified new test fails with system bun (before fix)
- [x] Verified new test passes with debug build (after fix)
- [x] Verified existing fake timer tests still pass
- [x] Verified test for issue #25869 passes with fix

Fixes #26284

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 12:51:54 -08:00
Alistair Smith
497a4d4818 Fix duplicate exports when two entrypoints share symbols (#26089)
### What does this PR do?

Fixes #5344
Fixes #6356

### How did you verify your code works?

Some test coverage

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
2026-01-20 12:40:33 -08:00
robobun
5f470278d1 fix(update): 'l' key now selects package in interactive update (#26265)
## Summary
- The 'l' key in `bun update --interactive` now correctly selects the
package when toggling between Target and Latest versions
- Previously, pressing 'l' would toggle `use_latest` but not mark the
package as selected, causing the underline indicator to disappear and
the package not being included when confirming

## Test plan
- [x] Added regression test `test/regression/issue/24131.test.ts` that
verifies 'l' selects the package
- [x] Test fails with system bun (before fix) and passes with debug
build (after fix)
- [x] `bun bd test test/regression/issue/24131.test.ts` passes

Fixes #24131

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 22:46:36 -08:00
robobun
d3f8bec565 fix(Terminal): callbacks not invoked inside AsyncLocalStorage.run() (#26288)
## Summary
- Fixed `Bun.Terminal` callbacks (data, exit, drain) not being invoked
when the terminal is created inside `AsyncLocalStorage.run()`

## Root Cause
The bug was a redundant `isCallable()` check when storing callbacks in
`initTerminal()`:

1. In `Options.parseFromJS()`, callbacks are validated with
`isCallable()`, then wrapped with `withAsyncContextIfNeeded()`
2. Inside `AsyncLocalStorage.run()`, `withAsyncContextIfNeeded()`
returns an `AsyncContextFrame` object that wraps the callback + async
context
3. An `AsyncContextFrame` is NOT callable - it's a wrapper object. So
the second `isCallable()` check fails
4. Because the check fails, the callback is never stored via
`js.gc.set()`
5. When `onReadChunk()` tries to get the callback, it returns `null` and
the callback is never invoked

## Fix
Removed the redundant `isCallable()` check in `initTerminal()`. The
check was already performed in `parseFromJS()` before wrapping. Other
similar patterns (socket Handlers, Timer) simply store the wrapped
callback without re-checking.

Fixes #26286

## Test plan
- [x] Added regression test in `test/regression/issue/26286.test.ts`
- [x] Verified test fails with system Bun (times out because callback
never invoked)
- [x] Verified test passes with debug build

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 22:45:22 -08:00
robobun
62834e1bfe fix(init): resolve TypeScript errors in react-tailwind template build.ts (#26258)
## Summary
- Fixes TypeScript errors in the react-tailwind template's `build.ts`
when used with the template's strict `tsconfig.json`

## Test plan
- Added regression test `test/regression/issue/24364.test.ts` that
verifies TypeScript compilation passes
- Verified test fails with old template code and passes with fix

Closes #24364

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 17:12:42 -08:00
robobun
704252e85f fix(npm): show helpful error when postinstall script hasn't run (#26259)
## Summary
- Replaces empty placeholder executables with shell scripts that print
helpful error messages
- The scripts exit with code 1 instead of silently succeeding with code
0
- Helps users diagnose issues when installing with `--ignore-scripts` or
using pnpm

## Problem
When installing the `bun` npm package with `--ignore-scripts` or using
pnpm (which skips postinstall by default), the placeholder `bun.exe` and
`bunx.exe` files were empty, causing them to silently exit with code 0
and produce no output. This made it very difficult for users to
understand why bun wasn't working.

## Solution
The placeholder files are now shell scripts that:
1. Print a clear error message explaining the issue
2. Provide instructions on how to fix it (manually running postinstall
or reinstalling without `--ignore-scripts`)
3. Exit with code 1 to indicate failure

Example output when running the placeholder:
```
Error: Bun's postinstall script was not run.

This occurs when using --ignore-scripts during installation, or when using a
package manager like pnpm that does not run postinstall scripts by default.

To fix this, run the postinstall script manually:
  cd node_modules/bun && node install.js

Or reinstall bun without the --ignore-scripts flag.
```

## Test plan
- [x] Added regression test that verifies the placeholder script
behavior
- [x] Test passes with `bun bd test test/regression/issue/24329.test.ts`

Fixes #24329

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 17:11:38 -08:00
robobun
04f441453d fix(assert): partialDeepStrictEqual now correctly handles Map subset checking (#26257)
## Summary

- Fixed `assert.partialDeepStrictEqual` to correctly handle Map subset
checking
- Previously, Map comparison used `Bun.deepEquals` which required exact
equality
- Now properly checks that all entries in the expected Map exist in the
actual Map with matching values

Fixes #24338

## Test plan

- Added comprehensive test suite in
`test/regression/issue/24338.test.ts` covering:
  - Basic subset checking (key2 in Map with key1 and key2)
  - Exact match cases
  - Empty expected Map
  - Multiple matching entries
  - Nested objects as values
  - Failure cases when expected has more keys
  - Failure cases when key is missing in actual
  - Failure cases when values differ
  - Nested Map values
  - Non-string keys

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 17:10:47 -08:00
robobun
362839c987 fix(websocket): forward URL credentials as Authorization header (#26278)
## Summary

- Extracts credentials from WebSocket URL (`ws://user:pass@host`) and
sends them as Basic Authorization header
- User-provided `Authorization` header takes precedence over URL
credentials
- Credentials are properly URL-decoded before being Base64-encoded

Fixes #24388

## Test plan

- [x] Added regression test `test/regression/issue/24388.test.ts` with 5
test cases:
  - Basic credentials in URL
  - Empty password
  - No credentials (no header sent)
  - Custom Authorization header takes precedence
  - Special characters (URL-encoded) in credentials
- [x] Tests pass with `bun bd test test/regression/issue/24388.test.ts`
- [x] Tests fail with `USE_SYSTEM_BUN=1 bun test` (confirming the bug
existed)

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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 17:04:44 -08:00
robobun
5e8a84e5e7 fix(fs): Dirent.isFIFO() incorrectly returns true for unknown type (#26263)
## Summary
- Fix `fs.Dirent.isFIFO()` incorrectly returning `true` for unknown file
types (e.g., on sshfs/NFS mounts)
- Remove the `EventPort` check from `isFIFO()` since `EventPort = 0 =
Unknown`
- Add regression test for the fix

Fixes #24129

## Root Cause
In `NodeDirent.cpp`, the `isFIFO()` method was checking:
```cpp
type == static_cast<int32_t>(DirEntType::NamedPipe) || type == static_cast<int32_t>(DirEntType::EventPort)
```

Since `EventPort = 0` and `Unknown = 0` (they share the same enum
value), any file with unknown type (returned by filesystems like sshfs,
NFS, etc. that don't populate `d_type`) would incorrectly trigger
`isFIFO() === true`.

## Test plan
- [x] Regression test: `bun bd test test/regression/issue/24129.test.ts`
- [x] Existing Dirent tests: `bun bd test test/js/node/fs/fs.test.ts -t
"Dirent"`

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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 15:44:27 -08:00
robobun
d85d40ea29 fix(ffi): respect C_INCLUDE_PATH and LIBRARY_PATH env vars (#26250)
## Summary
- Make `bun:ffi`'s TinyCC compiler check standard C compiler environment
variables
- Add support for `C_INCLUDE_PATH` (include paths) and `LIBRARY_PATH`
(library paths)
- Fixes compilation on NixOS and other systems that don't use standard
FHS paths

## Test plan
- [x] Added regression test `test/regression/issue/26249.test.ts` that
verifies:
  - Single path in `C_INCLUDE_PATH` works
  - Multiple colon-separated paths in `C_INCLUDE_PATH` work
- [x] Verified test fails with system bun (without fix)
- [x] Verified test passes with debug build (with fix)
- [x] Verified existing `cc.test.ts` tests still pass

Closes #26249

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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:24:02 -08:00
Jarred Sumner
c47f84348a Update CLAUDE.md 2026-01-18 14:07:30 -08:00
robobun
039c89442f chore: bump TinyCC to latest upstream (Jan 2026) (#26210)
## What does this PR do?

Updates the oven-sh/tinycc fork to the latest upstream TinyCC,
incorporating 30+ upstream commits while preserving all Bun-specific
patches.

### Upstream changes incorporated
- Build system improvements (c2str.exe handling, cross-compilation)
- macOS 15 compatibility fixes
- libtcc debugging support
- pic/pie support for i386
- arm64 alignment and symbol offset fixes
- RISC-V 64 improvements (pointer difference, assembly, Zicsr extension)
- Relocation updates
- Preprocessor improvements (integer literal overflow handling)
- x86-64 cvts*2si fix
- Various bug fixes

### Bun-specific patches preserved
- Fix crash on macOS x64 (libxcselect.dylib memory handling)
- Implement `-framework FrameworkName` on macOS (for framework header
parsing)
- Add missing #ifdef guards for TCC_IS_NATIVE
- Make `__attribute__(deprecated)` a no-op
- Fix `__has_include` with framework paths
- Support attributes after identifiers in enums
- Fix dlsym behavior on macOS (RTLD_SELF first, then RTLD_DEFAULT)
- Various tccmacho.c improvements

### Related PRs
- TinyCC fork CI is passing:
https://github.com/oven-sh/tinycc/actions/runs/21105489093

## How did you verify your code works?

- [x] TinyCC fork CI passes on all platforms (Linux
x86_64/arm64/armv7/riscv64, macOS x86_64/arm64, Windows i386/x86_64)
- [ ] Bun CI passes

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 13:31:21 -08:00
robobun
3d46ae2fa4 fix(node-fetch): convert old-style Node.js streams to Web streams (#26226)
## Summary
- Fix multipart uploads using form-data + node-fetch@2 +
fs.createReadStream() being truncated
- Convert old-style Node.js streams (that don't implement
`Symbol.asyncIterator`) to Web ReadableStreams before passing to native
fetch

## Test plan
- [x] New tests in `test/regression/issue/26225.test.ts` verify:
  - Multipart uploads with form-data and createReadStream work correctly
  - Async iterable bodies still work (regression test)
  - Large file streams work correctly
- [x] Tests fail with `USE_SYSTEM_BUN=1` and pass with debug build

Fixes #26225

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 13:19:02 -08:00
robobun
1344151576 fix(json): prevent stack overflow in JSONC parser on deeply nested input (#26174)
## Summary
- Add stack overflow protection to JSON/JSONC parser to prevent
segmentation faults
- Parser now throws `RangeError: Maximum call stack size exceeded`
instead of crashing
- Fixes DoS vulnerability when parsing deeply nested JSON structures
(~150k+ depth)

## Test plan
- [x] Added regression tests for deeply nested arrays and objects (25k
depth)
- [x] Verified system Bun v1.3.6 crashes with segfault at 150k depth
- [x] Verified fix throws proper error instead of crashing
- [x] All existing JSONC tests pass

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 16:23:01 -08:00
SUZUKI Sosuke
44df912d37 Add Bun.wrapAnsi() for text wrapping with ANSI escape code preservation (#26061)
## Summary

Adds `Bun.wrapAnsi()`, a native implementation of the popular
[wrap-ansi](https://www.npmjs.com/package/wrap-ansi) npm package for
wrapping text with ANSI escape codes.

## API

```typescript
Bun.wrapAnsi(string: string, columns: number, options?: WrapAnsiOptions): string

interface WrapAnsiOptions {
  hard?: boolean;              // default: false - Break words longer than columns
  wordWrap?: boolean;          // default: true - Wrap at word boundaries
  trim?: boolean;              // default: true - Trim leading/trailing whitespace
  ambiguousIsNarrow?: boolean; // default: true - Treat ambiguous-width chars as narrow
}
```

## Features

- Wraps text to fit within specified column width
- Preserves ANSI escape codes (SGR colors/styles)
- Supports OSC 8 hyperlinks
- Respects Unicode display widths (full-width characters, emoji)
- Normalizes `\r\n` to `\n`

## Implementation Details

The implementation closes and reopens ANSI codes around line breaks for
robust terminal compatibility. This differs slightly from the npm
package in edge cases but produces visually equivalent output.

### Behavioral Differences from npm wrap-ansi

1. **ANSI code preservation**: Bun always maintains complete ANSI escape
sequences. The npm version can output malformed codes (missing ESC
character) in certain edge cases with `wordWrap: false, trim: false`.

2. **Newline ANSI handling**: Bun closes and reopens ANSI codes around
newlines for robustness. The npm version sometimes keeps them spanning
across newlines. The visual output is equivalent.

## Tests

- 27 custom tests covering basic functionality, ANSI codes, Unicode, and
options
- 23 tests ported from the npm package (MIT licensed, credited in file
header)
- All 50 tests pass

## Benchmark

<!-- Benchmark results will be added -->
```
$ cd /Users/sosuke/code/bun/bench && ../build/release/bun snippets/wrap-ansi.js
clk: ~3.82 GHz
cpu: Apple M4 Max
runtime: bun 1.3.7 (arm64-darwin)

benchmark                    avg (min … max) p75   p99    (min … top 1%)
-------------------------------------------- -------------------------------
Short text (45 chars) - npm    25.81 µs/iter  21.71 µs  █
                      (16.79 µs … 447.38 µs) 110.96 µs ▆█▃▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
Short text (45 chars) - Bun   685.55 ns/iter 667.00 ns    █
                       (459.00 ns … 2.16 ms)   1.42 µs ▁▁▁█▃▂▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁

summary
  Short text (45 chars) - Bun
   37.65x faster than Short text (45 chars) - npm

-------------------------------------------- -------------------------------
Medium text (810 chars) - npm 568.12 µs/iter 578.00 µs  ▄▅█▆▆▃
                     (525.25 µs … 944.71 µs) 700.75 µs ▄██████▆▅▄▃▃▂▂▂▁▁▁▁▁▁
Medium text (810 chars) - Bun  11.22 µs/iter  11.28 µs                     █
                       (11.04 µs … 11.46 µs)  11.33 µs █▁▁▁██▁█▁▁▁▁█▁█▁▁█▁▁█

summary
  Medium text (810 chars) - Bun
   50.62x faster than Medium text (810 chars) - npm

-------------------------------------------- -------------------------------
Long text (8100 chars) - npm    7.66 ms/iter   7.76 ms     ▂▂▅█   ▅
                         (7.31 ms … 8.10 ms)   8.06 ms ▃▃▄▃█████▇▇███▃▆▆▆▄▁▃
Long text (8100 chars) - Bun  112.14 µs/iter 113.50 µs        █
                     (102.50 µs … 146.04 µs) 124.92 µs ▁▁▁▁▁▁██▇▅█▃▂▂▂▂▁▁▁▁▁

summary
  Long text (8100 chars) - Bun
   68.27x faster than Long text (8100 chars) - npm

-------------------------------------------- -------------------------------
Colored short - npm            28.46 µs/iter  28.56 µs              █
                       (27.90 µs … 29.34 µs)  28.93 µs ▆▁▆▁▁▆▁▁▆▆▆▁▆█▁▁▁▁▁▁▆
Colored short - Bun           861.64 ns/iter 867.54 ns         ▂  ▇█▄▂
                     (839.68 ns … 891.12 ns) 882.04 ns ▃▅▄▅▆▆▇▆██▇████▆▃▅▅▅▂

summary
  Colored short - Bun
   33.03x faster than Colored short - npm

-------------------------------------------- -------------------------------
Colored medium - npm          557.84 µs/iter 562.63 µs      ▂▃█▄
                     (508.08 µs … 911.92 µs) 637.96 µs ▁▁▁▂▄█████▅▂▂▁▁▁▁▁▁▁▁
Colored medium - Bun           14.91 µs/iter  14.94 µs ██  ████ ██ █      ██
                       (14.77 µs … 15.17 µs)  15.06 µs ██▁▁████▁██▁█▁▁▁▁▁▁██

summary
  Colored medium - Bun
   37.41x faster than Colored medium - npm

-------------------------------------------- -------------------------------
Colored long - npm              7.84 ms/iter   7.90 ms       █  ▅
                         (7.53 ms … 8.38 ms)   8.19 ms ▂▂▂▄▃▆██▇██▇▃▂▃▃▃▄▆▂▂
Colored long - Bun            176.73 µs/iter 175.42 µs       █
                       (162.50 µs … 1.37 ms) 204.46 µs ▁▁▂▄▇██▅▂▂▂▁▁▁▁▁▁▁▁▁▁

summary
  Colored long - Bun
   44.37x faster than Colored long - npm

-------------------------------------------- -------------------------------
Hard wrap long - npm            8.05 ms/iter   8.12 ms       ▃ ▇█
                         (7.67 ms … 8.53 ms)   8.50 ms ▄▁▁▁▃▄█████▄▃▂▆▄▃▂▂▂▂
Hard wrap long - Bun          111.85 µs/iter 112.33 µs         ▇█
                     (101.42 µs … 145.42 µs) 123.88 µs ▁▁▁▁▁▁▁████▄▃▂▂▂▁▁▁▁▁

summary
  Hard wrap long - Bun
   72.01x faster than Hard wrap long - npm

-------------------------------------------- -------------------------------
Hard wrap colored - npm         8.82 ms/iter   8.92 ms   ▆ ██
                         (8.55 ms … 9.47 ms)   9.32 ms ▆▆████▆▆▄▆█▄▆▄▄▁▃▁▃▄▃
Hard wrap colored - Bun       174.38 µs/iter 175.54 µs   █ ▂
                     (165.75 µs … 210.25 µs) 199.50 µs ▁▃█▆███▃▂▃▂▂▂▂▂▁▁▁▁▁▁

summary
  Hard wrap colored - Bun
   50.56x faster than Hard wrap colored - npm

-------------------------------------------- -------------------------------
Japanese (full-width) - npm    51.00 µs/iter  52.67 µs    █▂   █▄
                      (40.71 µs … 344.88 µs)  66.13 µs ▁▁▃██▄▃▅██▇▄▃▄▃▂▂▁▁▁▁
Japanese (full-width) - Bun     7.46 µs/iter   7.46 µs       █
                        (6.50 µs … 34.92 µs)   9.38 µs ▁▁▁▁▁██▆▂▁▂▁▁▁▁▁▁▁▁▁▁

summary
  Japanese (full-width) - Bun
   6.84x faster than Japanese (full-width) - npm

-------------------------------------------- -------------------------------
Emoji text - npm              173.63 µs/iter 222.17 µs   █
                     (129.42 µs … 527.25 µs) 249.58 µs ▁▃█▆▃▃▃▁▁▁▁▁▁▁▂▄▆▄▂▂▁
Emoji text - Bun                9.42 µs/iter   9.47 µs           ██
                         (9.32 µs … 9.52 µs)   9.50 µs █▁▁███▁▁█▁██▁▁▁▁██▁▁█

summary
  Emoji text - Bun
   18.44x faster than Emoji text - npm

-------------------------------------------- -------------------------------
Hyperlink (OSC 8) - npm       208.00 µs/iter 254.25 µs   █
                     (169.58 µs … 542.17 µs) 281.00 µs ▁▇█▃▃▂▂▂▁▁▁▁▁▁▁▃▃▅▃▂▁
Hyperlink (OSC 8) - Bun         6.00 µs/iter   6.06 µs      █           ▄
                         (5.88 µs … 6.11 µs)   6.10 µs ▅▅▅▁▅█▅▁▅▁█▁▁▅▅▅▅█▅▁█

summary
  Hyperlink (OSC 8) - Bun
   34.69x faster than Hyperlink (OSC 8) - npm

-------------------------------------------- -------------------------------
No trim long - npm              8.32 ms/iter   8.38 ms  █▇
                        (7.61 ms … 13.67 ms)  11.74 ms ▃████▄▂▃▂▂▃▁▁▁▁▁▁▁▁▁▂
No trim long - Bun             93.92 µs/iter  94.42 µs           █▂
                      (82.75 µs … 162.38 µs) 103.83 µs ▁▁▁▁▁▁▁▁▄███▄▃▂▂▁▁▁▁▁

summary
  No trim long - Bun
   88.62x faster than No trim long - npm
```

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-16 16:12:23 -08:00
robobun
05434add3e fix(bundler): legal comments no longer break module.exports = require() redirect optimization (#26113)
## Summary

- Legal comments (`/*! ... */`) were preventing the `module.exports =
require()` redirect optimization from being applied to CommonJS wrapper
modules
- The fix scans all parts to find a single meaningful statement,
skipping comments, directives, and empty statements
- If exactly one such statement exists and matches the `module.exports =
require()` pattern, the redirect optimization is now applied

This fixes an issue where wrapper modules like Express's `index.js`:
```js
/*!
 * express
 * MIT Licensed
 */

'use strict';

module.exports = require('./lib/express');
```

Were generating unnecessary wrapper functions instead of being
redirected directly to the target module.

## Test plan

- [x] Added regression test in `test/regression/issue/3179.test.ts`
- [x] Verified test fails with system bun and passes with the fix
- [x] Tested manual reproduction scenario

Fixes #3179

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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 15:57:27 -08:00
vadim-anthropic
8da29af1ae feat(node:inspector): implement Profiler API (#25939) 2026-01-16 10:12:28 -08:00
robobun
bcbb4fc35d fix(cli): show helpful error for unsupported file types instead of "File not found" (#26126)
## Summary

- When running `bun <file>` on a file with an unsupported type (e.g.,
`.css`, `.yaml`, `.toml`), Bun now shows a helpful error message instead
of the misleading "File not found"
- Tracks when a file is resolved but has a loader that can't be run
directly
- Shows the actual file path and file type in the error message

**Before:**
```
error: File not found "test.css"
```

**After:**
```
error: Cannot run "/path/to/test.css"
note: Bun cannot run css files directly
```

## Test plan

- [x] Added regression test in `test/regression/issue/1365.test.ts`
- [x] Test verifies unsupported files show "Cannot run" error
- [x] Test verifies nonexistent files still show "File not found"
- [x] Test fails with `USE_SYSTEM_BUN=1` and passes with debug build

Fixes #1365

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2026-01-15 23:40:45 -08:00
robobun
ad4aabf486 fix(Request): set cache and mode options correctly (#26099)
## Summary

- `new Request()` was ignoring `cache` and `mode` options, always
returning hardcoded default values ("default" for cache, "navigate" for
mode)
- Added proper storage and handling of these options in the Request
struct
- Both options are now correctly parsed from the constructor init object
and preserved when cloning

Fixes #2993

## Test plan

- [x] Added regression test in `test/regression/issue/2993.test.ts`
- [x] Tests verify all valid cache values: "default", "no-store",
"reload", "no-cache", "force-cache", "only-if-cached"
- [x] Tests verify all valid mode values: "same-origin", "no-cors",
"cors", "navigate"
- [x] Tests verify default values (cache: "default", mode: "cors")
- [x] Tests verify `Request.clone()` preserves options
- [x] Tests verify `new Request(request)` preserves options
- [x] Tests verify `new Request(request, init)` allows overriding
options


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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 23:02:16 -08:00
robobun
5b25a3abdb fix: don't call Bun.serve() on exported Server instances (#26144)
## Summary

- Fixes the entry point wrapper to distinguish between Server
configuration objects and already-running Server instances
- When a Server object from `Bun.serve()` is exported as the default
export, Bun no longer tries to call `Bun.serve()` on it again

## Root Cause

The entry point wrapper in `src/bundler/entry_points.zig` checks if the
default export has a `fetch` method to auto-start servers:

```javascript
if (typeof entryNamespace?.default?.fetch === 'function' || ...) {
   const server = Bun.serve(entryNamespace.default);
}
```

However, `Server` objects returned from `Bun.serve()` also have a
`fetch` method (for programmatic request handling), so the wrapper
mistakenly tried to call `Bun.serve(server)` on an already-running
server.

## Solution

Added an `isServerConfig()` helper that checks:
1. The object has a `fetch` function or `app` property (config object
indicators)
2. The object does NOT have a `stop` method (Server instance indicator)

Server instances have `stop`, `reload`, `upgrade`, etc. methods, while
config objects don't.

## Test plan

- [x] Added regression test that verifies exporting a Server as default
export works without errors
- [x] Added test that verifies config objects with `fetch` still trigger
auto-start
- [x] Verified test fails with `USE_SYSTEM_BUN=1` and passes with the
fix

Fixes #26142

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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 20:28:32 -08:00
robobun
12243b9715 fix(ws): pass selected protocol from handleProtocols to upgrade response (#26118)
## Summary
- Fixes the `handleProtocols` option not setting the selected protocol
in WebSocket upgrade responses
- Removes duplicate protocol header values in responses

## Test plan
- Added regression tests in `test/regression/issue/3613.test.ts`
- Verified using fetch to check actual response headers contain the
correct protocol

Fixes #3613

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-15 18:22:01 -08:00
Ciro Spaciari
5d3f37d7ae feat(s3): add Content-Encoding header support for S3 uploads (#26149)
## Summary

Add support for setting the `Content-Encoding` header in S3 `.write()`
and `.writer()` calls, following the same pattern as
`Content-Disposition`.

This allows users to specify the encoding of uploaded content:

```typescript
// With .write()
await s3file.write("compressed data", { contentEncoding: "gzip" });

// With .writer()
const writer = s3file.writer({ contentEncoding: "gzip" });
writer.write("compressed data");
await writer.end();

// With bucket.write()
await bucket.write("key", data, { contentEncoding: "br" });
```

## Implementation

- Extended `SignedHeaders.Key` from 6 bits to 7 bits (64→128
combinations) to accommodate the new header
- Added `content_encoding` to `S3CredentialsWithOptions`, `SignOptions`,
and `SignResult` structs
- Updated `CanonicalRequest` format strings to include
`content-encoding` in AWS SigV4 signing
- Added `getContentEncoding()` method to `Headers` for fetch-based S3
uploads
- Expanded `_headers` array from 9 to 10 elements
- Pass `content_encoding` through all S3 upload paths (upload,
uploadStream, writableStream)

## Test plan

- Added tests for "should be able to set content-encoding" 
- Added tests for "should be able to set content-encoding in writer"
- Tests verify the Content-Encoding header is properly set on uploaded
objects via presigned URL fetch
- All 4 new tests pass with `bun bd test` and fail with
`USE_SYSTEM_BUN=1` (confirming the feature is new)

## Changelog

> Describe your changes in 1-2 sentences. These will be featured on
[bun.sh/blog](https://bun.sh/blog) and Bun's release notes.

Added `contentEncoding` option to S3 `.write()` and `.writer()` methods,
allowing users to set the `Content-Encoding` header when uploading
objects.

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 18:09:33 -08:00
robobun
2a483631fb fix(http): allow body on GET/HEAD/OPTIONS requests for Node.js compatibility (#26145)
## Summary

Fixed `http.request()` and `https.request()` hanging indefinitely when a
GET request includes a body (via `req.write()`).

### Approach

Instead of adding a public `allowGetBody` option to `fetch()`, this PR
creates a dedicated internal function `nodeHttpClient` that:
- Uses a comptime parameter to avoid code duplication
- Allows body on GET/HEAD/OPTIONS requests (Node.js behavior)
- Is only accessible internally via `$newZigFunction`
- Keeps the public `Bun.fetch()` API unchanged (Web Standards compliant)

### Implementation

1. **fetch.zig**: Refactored to use `fetchImpl(comptime allow_get_body:
bool, ...)` shared implementation
- `Bun__fetch_()` calls `fetchImpl(false, ...)` - validates body on
GET/HEAD/OPTIONS
- `nodeHttpClient()` calls `fetchImpl(true, ...)` - allows body on
GET/HEAD/OPTIONS

2. **_http_client.ts**: Uses `$newZigFunction("fetch.zig",
"nodeHttpClient", 2)` for HTTP requests

## Test plan

- [x] Added regression test at `test/regression/issue/26143.test.ts`
- [x] Test verifies GET requests with body complete successfully
- [x] Test verifies HEAD requests with body complete successfully
- [x] Test verifies `Bun.fetch()` still throws on GET with body (Web
Standards)
- [x] Test fails on current release (v1.3.6) and passes with this fix

Fixes #26143

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
Co-authored-by: Ciro Spaciari MacBook <ciro@anthropic.com>
2026-01-15 17:46:07 -08:00
robobun
cdcff11221 fix(cli): handle BrokenPipe gracefully in bun completions (#26097)
## Summary
- Fixes `bun completions` crashing with `BrokenPipe` error when piped to
commands that close stdout early (e.g., `bun completions | true`)
- The fix catches `error.BrokenPipe` and exits cleanly with status 0
instead of propagating the error

## Test plan
- [x] Added regression test that pipes `bun completions` to `true` and
verifies no BrokenPipe error occurs
- [x] Verified test fails with system Bun and passes with fixed build

Fixes #2977

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2026-01-15 17:44:59 -08:00
robobun
dfa704cc62 fix(s3): add contentDisposition and type support to presign() (#25999)
## Summary
- S3 `File.presign()` was ignoring the `contentDisposition` and `type`
options
- These options are now properly included as
`response-content-disposition` and `response-content-type` query
parameters in the presigned URL
- Added `content_type` field to `SignOptions` and
`S3CredentialsWithOptions` structs
- Added parsing for the `type` option in `getCredentialsWithOptions()`
- Query parameters are added in correct alphabetical order for AWS
Signature V4 compliance

## Test plan
- [x] Added regression test in `test/regression/issue/25750.test.ts`
- [x] Verified tests pass with debug build: `bun bd test
test/regression/issue/25750.test.ts`
- [x] Verified tests fail with system bun (without fix):
`USE_SYSTEM_BUN=1 bun test test/regression/issue/25750.test.ts`
- [x] Verified existing S3 presign tests still pass
- [x] Verified existing S3 signature order tests still pass

Fixes #25750

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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 15:33:43 -08:00
Alistair Smith
97feb66189 Double the hardcoded max http header count (#26130)
### What does this PR do?

Doubles the hardcoded max http header count

### How did you verify your code works?

ci (?)

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-15 00:35:37 -08:00
robobun
ed75a0e2d1 fix(http): use correct client certificate for mTLS fetch() requests (#26129)
## Summary
- Fixes bug where `fetch()` with mTLS would use the first client
certificate for all subsequent requests to the same host, ignoring
per-request `tls` options
- Corrects `SSLConfig.isSame()` to properly compare all fields (was
incorrectly returning early when both optional fields were null)
- Sets `disable_keepalive=true` when reusing cached SSL contexts to
prevent socket pooling issues

Fixes #26125

## Test plan
- [x] Added regression test `test/regression/issue/26125.test.ts`
- [x] Verified test fails with system Bun 1.3.6 (demonstrates the bug)
- [x] Verified test passes with patched build

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 19:22:08 -08:00
Jarred Sumner
d4e5197208 Fix exception scope verification error 2026-01-14 19:09:18 -08:00
robobun
65a9a2a580 fix(process): emit EPIPE error on broken pipe for process.stdout.write() (#26124)
## Summary
- Fixes the broken pipe behavior for `process.stdout.write()` to match
Node.js
- When writing to a broken pipe (stdout destroyed), the process now
properly exits with code 1 instead of 0
- EPIPE errors are now properly propagated to JavaScript via the
stream's error event

## Test plan
- [x] Added regression test `test/regression/issue/1632.test.ts`
- [x] Verified test fails with system bun (exit code 0) and passes with
debug build (exit code 1)
- [x] Verified `console.log` still ignores errors (uses `catch {}`) and
doesn't crash
- [x] Verified callback-based `process.stdout.write()` receives EPIPE
error

## Changes
1. **`src/io/PipeWriter.zig`**: Return EPIPE as an error instead of
treating it as successful end-of-file (`.done`)
2. **`src/shell/IOWriter.zig`**: Track `broken_pipe` flag when EPIPE is
received via `onError` callback, and propagate error properly
3. **`src/js/internal/fs/streams.ts`**: When a write fails without a
callback, emit the error on the stream via `this.destroy(err)` to match
Node.js behavior

Fixes #1632

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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 18:37:54 -08:00
robobun
393198d190 fix(install): quote workspace: versions in yarn lockfile (#26106)
## Summary
- Add colon (`:`) to the list of characters that require quoting in yarn
lockfile version strings
- This fixes yarn parse errors when using `workspace:*` dependencies in
monorepo setups

Fixes #3192

## Test plan
- [x] Added regression test that verifies `workspace:*` versions are
properly quoted
- [x] Test fails with system bun (before fix)
- [x] Test passes with debug build (after fix)

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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 18:22:32 -08:00
robobun
798b48c898 fix(runtime): exclude globalThis from auto-serve detection (#26107)
## Summary
- When a module exports `globalThis` (e.g., `module.exports =
globalThis`), Bun's auto-serve detection incorrectly triggered because
`globalThis.fetch` is the Fetch API function
- Scripts that export globalThis (like `core-js/es/global-this.js`)
would start a development server on port 3000 instead of exiting
normally
- Added explicit check to skip auto-serve when the default export is
`globalThis` itself

Fixes #440

## Test plan
- [x] Added test case `test/regression/issue/440.test.ts` that verifies:
  - `module.exports = globalThis` does not start a server
  - `export default globalThis` does not start a server
- [x] Verified test fails with system Bun (without fix)
- [x] Verified test passes with debug build (with fix)

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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 17:41:21 -08:00
robobun
6104705f5f fix(yaml): fix memory leak in YAML parser (#26090)
## Summary
- Fix memory leak in YAML parser that caused segfaults after high-volume
parsing
- Added `defer parser.deinit()` to free internal data structures
(context, block_indents, anchors, tag_handles, whitespace_buf)
- Fixes #26088

## Test plan
- [x] Added regression test at `test/regression/issue/26088.test.ts`
- [x] Verified YAML parsing still works correctly with debug build
- [x] Ran subset of YAML tests to confirm no regressions


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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-14 17:13:30 -08:00
robobun
11aedbe402 fix(fs.watch): emit 'change' events for files in watched directories on Linux (#26009)
## Summary
- Fixes #3657 - `fs.watch` on directory doesn't emit `change` events for
files created after watch starts

When watching a directory with `fs.watch`, files created after the watch
was established would only emit a 'rename' event on creation, but
subsequent modifications would not emit 'change' events.

## Root Cause

The issue was twofold:
1. `watch_dir_mask` in INotifyWatcher.zig was missing `IN.MODIFY`, so
the inotify system call was not subscribed to file modification events
for watched directories.
2. When directory events were processed in path_watcher.zig, all events
were hardcoded to emit 'rename' instead of properly distinguishing
between file creation/deletion ('rename') and file modification
('change').

## Changes

- Adds `IN.MODIFY` to `watch_dir_mask` to receive modification events
- Adds a `create` flag to `WatchEvent.Op` to track `IN.CREATE` events
- Updates directory event processing to emit 'change' for pure write
events and 'rename' for create/delete/move events

## Test plan
- [x] Added regression test `test/regression/issue/3657.test.ts`
- [x] Verified test fails with system Bun (before fix)
- [x] Verified test passes with debug build (after fix)
- [x] Verified manual reproduction from issue now works correctly

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

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 16:46:20 -08:00
robobun
b72af3d329 fix(compile): respect autoloadBunfig: false when execArgv is present (#26017)
## Summary

Fixes #25640

- Fixed bug where compiled binaries with `autoloadBunfig: false` would
still load `bunfig.toml` when `execArgv` was also provided
- The issue was that `Command.init(.AutoCommand)` was called to parse
execArgv, which loaded bunfig before checking the disable flag

## Test plan

- [x] Added tests for `autoloadBunfig: false` with `execArgv` in
`test/bundler/bundler_compile_autoload.test.ts`
- [x] Verified tests pass with debug build: `bun bd test
test/bundler/bundler_compile_autoload.test.ts`
- [x] Verified tests fail with system bun (demonstrates fix works):
`USE_SYSTEM_BUN=1 bun test test/bundler/bundler_compile_autoload.test.ts
-t "AutoloadBunfigDisabledWithExecArgv"`
- [x] All existing autoload tests still pass (22 tests total)

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

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 16:08:49 -08:00