Compare commits

...

162 Commits

Author SHA1 Message Date
Claude Bot
106ef125c0 fix(resolver): allow long data URLs in module resolution
Data URLs can be very long (e.g. zip.js embeds ~25KB worker code in data URIs).
The path length check was rejecting these before they could be recognized as
data URLs. This fix excludes data URLs from the path length check.

Fixes #24161

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 22:10:57 +00:00
wovw
716801e92d gitignore: add .direnv dir (#26198)
### What does this PR do?

The `.direnv` folder is created by [direnv](https://direnv.net/) when
using `use flake` in `.envrc` to automatically load the Nix development
shell. Since the repo already includes a flake.nix, developers on NixOS
commonly use direnv (via nix-direnv) to auto-load the environment. This
folder contains cached environment data and should not be committed.
2026-01-18 00:17:14 -08:00
wovw
939f5cf7af fix(nix): disable fortify hardening for debug builds (#26199)
### What does this PR do?

NixOS enables security hardening flags by default in `mkShell` /
`devShells` e.g. `_FORTIFY_SOURCE=2`. This flag adds runtime buffer
overflow checks but requires compiler optimization (`-O1` or higher) to
work, since it needs to inline functions to insert checks.
Debug builds use `-O0` (no optimization), which causes this compilation
error:
`error: _FORTIFY_SOURCE requires compiling with optimization (-O)
[-Werror,-W#warnings]`

This patch is a standard Nix way to disable this specific flag while
keeping other hardening features intact. It doesn't affect release
builds since it's scoped to `devShells`.

### How did you verify your code works?

`bun bd test` successfully runs test cases.
2026-01-18 00:17:01 -08:00
SUZUKI Sosuke
496aeb97f9 refactor(wrapAnsi): use WTF::find for character searches (#26200)
## Summary

This PR addresses the review feedback from #26061
([comment](https://github.com/oven-sh/bun/pull/26061#discussion_r2697257836))
requesting the use of `WTF::find` for newline searches in
`wrapAnsi.cpp`.

## Changes

### 1. CRLF Normalization (lines 628-639)
Replaced manual loop with `WTF::findNextNewline` which provides
SIMD-optimized detection for `\r`, `\n`, and `\r\n` sequences.

**Before:**
```cpp
for (size_t i = 0; i < input.size(); ++i) {
    if (i + 1 < input.size() && input[i] == '\r' && input[i + 1] == '\n') {
        normalized.append(static_cast<Char>('\n'));
        i++;
    } else {
        normalized.append(input[i]);
    }
}
```

**After:**
```cpp
size_t pos = 0;
while (pos < input.size()) {
    auto newline = WTF::findNextNewline(input, pos);
    if (newline.position == WTF::notFound) {
        normalized.append(std::span { input.data() + pos, input.size() - pos });
        break;
    }
    if (newline.position > pos)
        normalized.append(std::span { input.data() + pos, newline.position - pos });
    normalized.append(static_cast<Char>('\n'));
    pos = newline.position + newline.length;
}
```

### 2. Word Length Calculation (lines 524-533)
Replaced manual loop with `WTF::find` for space character detection.

**Before:**
```cpp
for (const Char* it = lineStart; it <= lineEnd; ++it) {
    if (it == lineEnd || *it == ' ') {
        // word boundary logic
    }
}
```

**After:**
```cpp
auto lineSpan = std::span<const Char>(lineStart, lineEnd);
size_t wordStartIdx = 0;
while (wordStartIdx <= lineSpan.size()) {
    size_t spacePos = WTF::find(lineSpan, static_cast<Char>(' '), wordStartIdx);
    // word boundary logic using spacePos
}
```

## Benchmark Results

Tested on Apple M4 Max. No performance regression observed - most
benchmarks show slight improvements.

| Benchmark | Before | After | Change |
|-----------|--------|-------|--------|
| Short text (45 chars) | 613 ns | 583 ns | -4.9% |
| Medium text (810 chars) | 10.85 µs | 10.31 µs | -5.0% |
| Long text (8100 chars) | 684 µs | 102 µs | -85% * |
| Colored short | 1.26 µs | 806 ns | -36% |
| Colored medium | 19.24 µs | 13.80 µs | -28% |
| Japanese (full-width) | 7.74 µs | 7.43 µs | -4.0% |
| Emoji text | 9.35 µs | 9.27 µs | -0.9% |
| Hyperlink (OSC 8) | 5.73 µs | 5.58 µs | -2.6% |

\* Large variance in baseline measurement

## Testing

- All 35 existing tests pass
- Manual verification of CRLF normalization and word wrapping edge cases

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-17 23:43:02 -08:00
robobun
3b5f2fe756 chore(deps): update BoringSSL fork to latest upstream (#26212)
## Summary

Updates the BoringSSL fork to the latest upstream (337 commits since
last update) with bug fixes for Node.js crypto compatibility.

### Upstream BoringSSL Changes (337 commits)

| Category | Count |
|----------|-------|
| API Changes (including namespacing) | 42 |
| Code Cleanup/Refactoring | 35 |
| Testing/CI | 32 |
| Build System (Bazel, CMake) | 27 |
| Bug Fixes | 25 |
| Post-Quantum Cryptography | 14 |
| TLS/SSL Changes | 12 |
| Rust Bindings/Wrappers | 9 |
| Performance Improvements | 8 |
| Documentation | 8 |

#### Highlights

**Post-Quantum Cryptography**
- ML-DSA (Module-Lattice Digital Signature Algorithm): Full EVP
integration, Wycheproof tests, external mu verification
- SLH-DSA: Implementation of pure SLH-DSA-SHAKE-256f
- Merkle Tree Certificates: New support for verifying signatureless MTCs

**Major API Changes**
- New `CRYPTO_IOVEC` based AEAD APIs for zero-copy I/O across all
ciphers
- Massive namespacing effort moving internal symbols into `bssl`
namespace
- `bssl::Span` modernization to match `std::span` behavior

**TLS/SSL**
- Added `TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256` support
- HMAC on SHA-384 for TLS 1.3
- Improved Lucky 13 mitigation

**Build System**
- Bazel 8.x and 9.0.0 compatibility
- CI upgrades: Ubuntu 24.04, Android NDK r29

---

### Bun-specific Patches (in oven-sh/boringssl)

1. **Fix SHA512-224 EVP final buffer size** (`digests.cc.inc`)
- `BCM_sha512_224_final` writes 32 bytes but `EVP_MD.md_size` is 28
bytes
   - Now uses a temp buffer to avoid buffer overwrite

2. **Fix `EVP_do_all_sorted` to return only lowercase names**
(`evp_do_all.cc`)
- `EVP_CIPHER_do_all_sorted` and `EVP_MD_do_all_sorted` now return only
lowercase names
- Matches Node.js behavior for `crypto.getCiphers()` and
`crypto.getHashes()`

---

### Changes in Bun

- Updated BoringSSL commit hash to
`4f4f5ef8ebc6e23cbf393428f0ab1b526773f7ac`
- Removed `ignoreSHA512_224` parameter from `ncrypto::getDigestByName()`
to enable SHA512-224 support
- Removed special SHA512-224 buffer handling in `JSHash.cpp` (no longer
needed after BoringSSL fix)

## Test plan
- [x] `crypto.createHash('sha512-224')` works correctly
- [x] `crypto.getHashes()` returns lowercase names (md4, md5, sha1,
sha256, etc.)
- [x] `crypto.getCiphers()` returns lowercase names (aes-128-cbc,
aes-256-gcm, etc.)
- [x] `test/regression/issue/crypto-names.test.ts` passes
- [x] All CI tests pass

🤖 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-17 23:39:04 -08:00
robobun
f833f11afa fix(bake): respect --no-clear-screen in DevServer HMR (#26184) 2026-01-16 21:33:36 -08:00
robobun
b2e5c6c7d1 Upgrade WebKit to ea1bfb85d259 (#26161)
## Summary
- Upgrades WebKit from `c4d4cae03ece` to `ea1bfb85d259`
- Merges upstream WebKit changes into oven-sh/webkit fork

## WebKit Upgrade Summary (JavaScriptCore Changes)

### JSType Enum Changes

**No breaking changes to JSType enum from upstream.** The diff showing
`InternalFieldTupleType` removal is actually showing Bun's custom
addition - upstream WebKit does not have this type. The Bun fork
maintains `InternalFieldTupleType` after `DerivedStringObjectType`,
which is preserved during the upgrade.

### Notable Performance Improvements

#### ARM64 Conditional Compare Chain (ccmp/ccmn)
- **Commit:** `2cd6a734ed6c`
- Implements ARM64 `ccmp`/`ccmn` instruction chaining for compound
boolean expressions
- Converts patterns like `if (x0 == 0 && x1 == 1)` into efficient
conditional compare sequences
- Reduces branch prediction misses and code size
- Introduces new Air opcodes: `CompareOnFlags`,
`CompareConditionallyOnFlags`, `BranchOnFlags`

#### Extended Constant Materialization for Float16/Float/Double/V128
- **Commit:** `0521cc7f331a`
- Enhanced ARM64 constant materialization using `movi`, `mvni`, and
vector `fmov`
- Avoids memory loads for Float constants (32-bit values can now be
materialized directly)
- Adds `FPImm128` and `Move128ToVector` Air instructions

#### DFG/FTL Storage Pointer Improvements
- **Commits:** `00c0add58ec3`, `7051d3ac1f34`
- FTL Phis now properly support storage (butterfly) pointers
- Introduces `KnownStorageUse` for all storage operands in DFG/FTL
- Fixes issues with Array allocation sinking when creating storage Phis
- Improves GC safety by ensuring butterfly pointers are properly tracked

### Bug Fixes

#### Thread Termination Race Condition
- **Commit:** `23922a766f07`
- Fixes race condition in `VM::m_hasTerminationRequest` between main
thread and worker threads
- Moves `setHasTerminationRequest()` call into `VMTraps::handleTraps()`
to eliminate race

#### ThrowScope Exception Clearing
- **Commit:** `67abaaa35c4d`
- ThrowScopes can no longer accidentally clear termination exceptions
- Introduces `tryClearException()` which fails on termination exceptions
- Affects iterator operations, promises, and WebCore stream handling

#### Bytecode Cache JIT Threshold
- **Commit:** `e0644034f46e`
- Functions loaded from bytecode cache now correctly set JIT threshold
- Previously, cached functions would JIT immediately on first execution

#### Wasm Fixes
- **Commit:** `8579516f4b61` - Fix JIT-less Wasm-to-JS i31ref
marshalling for i31 values in double format
- **Commit:** `22b6a610f6ff` - Fix nullability for wasm js-string
builtins return types (`cast`, `fromCharCode`, `fromCodePoint`,
`concat`, `substring`)
- **Commit:** `5ad2efd177db` - Optimize Wasm BlockSignature to avoid
lock contention during parsing

#### 32-bit ARM (Armv7) Fix
- **Commit:** `9cc23c0e75b7`
- Fixes tail call shuffler register allocation on 32-bit ARM
- Prevents assertion failures when JSValue can load via FPR but GPRs are
exhausted

### New Features

#### Temporal PlainYearMonth Support
- **Commit:** `d865004780e6`
- Enables all PlainYearMonth test262 tests
- Fixes several bugs in month code handling and rounding modes

#### Wasm IPInt Execution Tracing
- **Commit:** `634156af4114`
- Adds `--traceWasmIPIntExecution` option for debugging WebAssembly
interpreter execution

### Code Quality Improvements

- **Commit:** `31bc5e6778d4` - `JSRegExpStringIterator` reduced from 56
to 40 bytes by merging boolean fields into bitfield
- **Commit:** `cda948675446` - Fix fragile include dependency in
`JSC::getCallDataInline`
- **Commit:** `bd87f5db107e` - Fix unretained local variable warnings in
JavaScriptCore/API

## Merge Conflicts Resolved

Fixed 4 merge conflicts related to Bun-specific patches:
1. `Source/JavaScriptCore/API/JSVirtualMachine.mm` - Removed
JSLockHolder as per Bun's patch
2. `Source/JavaScriptCore/runtime/JSBoundFunction.h` - Used relative
includes instead of framework includes
3. `Source/JavaScriptCore/runtime/JSObjectInlines.h` - Used relative
includes and updated `JSFunction.h` to `JSFunctionInlines.h`
4. `Source/WTF/wtf/text/WTFString.h` - Preserved ExternalStringImpl
support

## Test plan
- [x] WebKit builds successfully (`bun build.ts debug`)
- [x] JSType enum values verified to be compatible
- [ ] CI builds and tests pass

🤖 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>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: vadim-anthropic <vadim@anthropic.com>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
2026-01-16 18:46:48 -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
robobun
7e9fa4ab08 feat(scripts): enhance buildkite-failures.ts to fetch and save full logs (#26177)
## Summary
- Fetches complete logs from BuildKite's public API (no token required)
- Saves logs to `/tmp/bun-build-{number}-{platform}-{step}.log`
- Shows log file path in output for each failed job
- Displays brief error summary (unique errors, max 5)
- Adds help text with usage examples (`--help`)
- Groups failures by type (build/test/other)
- Shows annotation counts with link to view full annotations
- Documents usage in CLAUDE.md

## Test plan
- [x] Tested with build #35051 (9 failed jobs)
- [x] Verified logs saved to `/tmp/bun-build-35051-*.log`
- [x] Verified error extraction and deduplication works
- [x] Verified `--help` flag shows usage

🤖 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:37:31 -08:00
Dylan Conway
6f6f76f0c0 fix(macho): only update signature size on ARM64 with codesigning enabled (#26175)
The signature size adjustment was being applied unconditionally, but it
should only happen when building for ARM64 and codesigning is enabled.
This prevents incorrect offset calculations on non-ARM64 platforms.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-16 14:18:48 -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
SUZUKI Sosuke
f01467d3dc perf(buffer): optimize Buffer.from(array) by using setFromArrayLike directly (#26135)
## Summary

Optimizes `Buffer.from(array)` by bypassing `JSC::construct()` overhead
(~30ns) and leveraging JSC's internal array optimizations.

## Changes

- For JSArray inputs, directly use `setFromArrayLike()` which internally
detects array indexing types (Int32Shape/DoubleShape) and uses bulk copy
operations (`copyFromInt32ShapeArray`/`copyFromDoubleShapeArray`)
- Array-like objects and iterables continue to use the existing slow
path
- Added mitata benchmark for measuring performance

## Benchmark Results

| Test | Before | After | Improvement |
|------|--------|-------|-------------|
| Buffer.from(int32[8]) | ~85ns | ~43ns | ~50% faster |
| Buffer.from(int32[64]) | ~207ns | ~120ns | ~42% faster |
| Buffer.from(int32[1024]) | ~1.85μs | ~1.32μs | ~29% faster |
| Buffer.from(double[8]) | ~86ns | ~50ns | ~42% faster |
| Buffer.from(double[64]) | ~212ns | ~151ns | ~29% faster |

Bun is now faster than Node.js for these operations.

## Test

All 449 buffer tests pass.
2026-01-15 12:10:47 -08:00
Jarred Sumner
b268004715 Upgrade WebKit to d5bd162d9ab2 (#25958) 2026-01-15 10:26: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
Jarred Sumner
8466f12671 Update outdated doc. io_uring has not been required in years. 2026-01-15 00:34:49 -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
fdb956f2fe Update CLAUDE.md 2026-01-14 19:09:29 -08:00
Jarred Sumner
d4e5197208 Fix exception scope verification error 2026-01-14 19:09:18 -08:00
robobun
7d640cccd1 fix(tls): check SSL_is_init_finished directly for _secureEstablished (#26086)
## Summary

Fixes flaky test
`test/js/node/test/parallel/test-http-url.parse-https.request.js` where
`request.socket._secureEstablished` sometimes returned `false` even when
the TLS handshake had completed.

## Root Cause

There's a race condition between when the TLS handshake completes and
when the `on_handshake` callback fires. The HTTP request handler could
start executing before the callback set `httpResponseData->isAuthorized
= true`, causing `_secureEstablished` to return `false`.

## Previous Failed Approach (PR #25946)

Attempted to trigger the handshake callback earlier in `ssl_on_data`,
but this broke gRPC and HTTP/2 tests because the callback has side
effects that disrupted the data processing.

## This Fix

Instead of changing when the callback fires, directly query OpenSSL's
`SSL_is_init_finished()` when checking `_secureEstablished`:

1. Added `us_socket_is_ssl_handshake_finished()` API that wraps
`SSL_is_init_finished()`
2. Modified `JSNodeHTTPServerSocket::isAuthorized()` to use this
function directly

This approach is non-invasive - it doesn't change any TLS processing
logic, just reads the correct state at the point where it's needed.

## Test plan

- [x] Original flaky test passes under high parallelism (50/50 runs)
- [x] gRPC tests pass (`test-channel-credentials.test.ts`)
- [x] All `test-http-url.parse-*.js` tests pass
- [x] HTTPS tests pass (`test-https-simple.js`, `test-https-agent.js`)

🤖 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: Jarred Sumner <jarred@jarredsumner.com>
2026-01-14 18:48:09 -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
05df51ff84 fix(runner): filter out non-JS files from node tests (#26092)
## Summary
- `isNodeTest()` was only checking if the path included the node test
directories but not verifying the file was actually a JavaScript file
- This caused `test/js/node/test/parallel/CLAUDE.md` to be incorrectly
treated as a test file
- Added `isJavaScript(path)` check to filter out non-JS files

## Test plan
- [x] Verify CLAUDE.md is no longer picked up as a test file

🤖 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 16:28:29 -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
robobun
f27c6768ce fix(bundler): include lazy chunks in frontend.files for compiled fullstack builds (#26024)
## Summary

- Fixed lazy-loaded chunks from dynamic imports not appearing in
`frontend.files` when using `--splitting` with `--compile` in fullstack
builds
- Updated `computeChunks.zig` to mark non-entry-point chunks as browser
chunks when they contain browser-targeted files
- Updated `HTMLImportManifest.zig` to include browser chunks from server
builds in the files manifest

Fixes #25628

## Test plan

- [ ] Added regression test `test/regression/issue/25628.test.ts` that
verifies lazy chunks appear in `frontend.files`
- [ ] Manually verified: system bun reports `CHUNK_COUNT:1` (bug), debug
bun reports `CHUNK_COUNT:2` (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 16:08:06 -08:00
robobun
c57d0f73b4 fix(css): preserve logical border-radius properties (#26006)
## Summary
- CSS logical border-radius properties (`border-start-start-radius`,
`border-start-end-radius`, `border-end-end-radius`,
`border-end-start-radius`) were being silently dropped when processed by
the CSS bundler
- The bug was in `src/css/properties/border_radius.zig` where
`VendorPrefix{}` (all fields false) was used instead of `VendorPrefix{
.none = true }` when computing prefixes for logical properties
- This caused the properties to be dropped by a later `isEmpty()` check
since an empty prefix struct was returned

## Test plan
- [x] Added regression test `test/regression/issue/25785.test.ts`
- [x] Verified test fails with system Bun (`USE_SYSTEM_BUN=1 bun test`)
- [x] Verified test passes with fixed bun-debug (`bun bd test`)

Fixes #25785

🤖 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-14 13:34:31 -08:00
robobun
6a27a25e5b fix(debugger): retroactively report tests when TestReporter.enable is called (#25986)
## Summary
- Fixes #25972: TestReporter domain events not firing when debugger
connects after test discovery

When a debugger client connects and enables the TestReporter domain
after tests have been discovered (e.g., using `--inspect` instead of
`--inspect-wait`), the `TestReporter.found`, `TestReporter.start`, and
`TestReporter.end` events would not fire. This is because tests
discovered without an enabled debugger have `test_id_for_debugger = 0`,
and the event emission code checks for non-zero IDs.

The fix retroactively assigns test IDs and reports discovered tests when
`TestReporter.enable` is called:

1. Check if there's an active test file in collection or execution phase
2. Iterate through the test tree (DescribeScopes and test entries)
3. Assign unique `test_id_for_debugger` values to each test/describe
4. Send `TestReporter.found` events for each discovered test

## Test plan
- [ ] Verify IDE integrations can now receive test telemetry when
connecting after test discovery
- [ ] Ensure existing `--inspect-wait` behavior continues to work
(debugger enabled before discovery)

🤖 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 13:32:51 -08:00
robobun
2b86ab0cd3 fix(shell): implement long listing format for ls -l builtin (#25991)
## Summary
- Implements the `-l` (long listing) flag functionality for the shell
`ls` builtin
- The flag was being parsed but never used - output was identical to
short format
- Now displays proper long listing format: file type, permissions, hard
link count, UID, GID, size, modification time, and filename

## Test plan
- [x] Added regression test in `test/regression/issue/25831.test.ts`
- [x] Test passes with debug build: `bun bd test
test/regression/issue/25831.test.ts`
- [x] Test fails with system bun (confirming the bug exists):
`USE_SYSTEM_BUN=1 bun test test/regression/issue/25831.test.ts`

Example output with fix:
```
$ bun -e 'import { $ } from "bun"; console.log(await $`ls -l`.text())'
drwxr-xr-x   2  1000  1000     4096 Jan 12 15:30 subdir
-rw-r--r--   1  1000  1000       11 Jan 12 15:30 file.txt
```

Fixes #25831

🤖 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 13:31:06 -08:00
robobun
6e6896510a fix(cli): prevent --version/--help interception in standalone executables with compile-exec-argv (#26083)
## Summary

Fixes https://github.com/oven-sh/bun/issues/26082

- Fixes a bug where standalone executables compiled with
`--compile-exec-argv` would intercept `--version`, `-v`, `--help`, and
`-h` flags before user code could handle them
- CLI applications using libraries like `commander` can now properly
implement their own version and help commands

## Root Cause

When `--compile-exec-argv` is used, `Command.init` was being called with
`.AutoCommand`, which parses ALL arguments (including user arguments).
The `Arguments.parse` function intercepts `--version`/`--help` flags for
`AutoCommand`, preventing them from reaching user code.

## Fix

Temporarily set `bun.argv` to only include the executable name +
embedded exec argv options when calling `Command.init`. This ensures:
1. Bun's embedded options (like `--smol`, `--use-system-ca`) are
properly parsed
2. User arguments (including `--version`/`--help`) are NOT intercepted
by Bun's parser
3. User arguments are properly passed through to user code

## Test plan

- [x] Added tests for `--version`, `-v`, `--help`, and `-h` flags in
`compile-argv.test.ts`
- [x] Verified tests fail with `USE_SYSTEM_BUN=1` (proving the bug
exists)
- [x] Verified tests pass with debug build
- [x] Verified existing compile-argv tests still pass

🤖 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 13:10:53 -08:00
robobun
5a71ead8a2 Add CLAUDE.md for Node.js compatibility tests (#26084)
## Summary
- Adds a CLAUDE.md file to `test/js/node/test/parallel/` documenting
that these are official Node.js tests
- Explains that these tests should not be modified since they come from
the Node.js repository
- Documents how to run these tests with debug builds (`bun bd
<file-path>` instead of `bun bd test`)

## Test plan
- [x] Verified file was created correctly

🤖 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 13:07:38 -08:00
robobun
a9b5f5cbd1 fix(sql): prevent hang in sequential MySQL transactions with returned array queries (#26048)
## Summary

- Fix a hang in sequential MySQL transactions where an INSERT is awaited
followed by a SELECT returned in an array
- The issue occurred because `handleResultSetOK`'s defer block only
called `queue.advance()` without flushing, causing queries added during
the JS callback to not be properly sent
- Changed to call `flushQueue()` instead of just `advance()` to ensure
data is actually sent to the server

Fixes #26030

## Test plan

- Added regression test `test/regression/issue/26030.test.ts` with three
test cases:
- `Sequential transactions with INSERT and returned SELECT should not
hang` - reproduces the exact pattern from the bug report
- `Sequential transactions with returned array of multiple queries` -
tests returning multiple queries in array
- `Many sequential transactions with awaited INSERT and returned SELECT`
- stress tests with 5 sequential transactions

🤖 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 12:53:04 -08:00
robobun
7333500df8 fix(bundler): rename named function expressions when shadowing outer symbol (#26027)
## Summary
- Fixed a bug where named function expressions were not renamed when
their name shadowed an outer symbol that's referenced inside the
function body
- This caused infinite recursion at runtime when namespace imports were
inlined
- Particularly affected Svelte 5 apps in dev mode

## Test plan
- [x] Added regression test that reproduces the issue
- [x] Verified test fails with system bun and passes with fix
- [x] Ran bundler tests (bundler_regressions, bundler_naming,
bundler_edgecase, bundler_minify) - all pass

## Root cause
The bundler was skipping `function_args` scopes when renaming symbols.
This meant named function expression names (which are declared in the
function_args scope) were never considered for renaming when they
collided with outer symbols.

For example, this code:
```javascript
import * as $ from './lib';
$.doSomething(function get() {
  return $.get(123);  // Should call outer get
});
```

Would be bundled as:
```javascript
function get(x) { return x * 2; } // from lib
doSomething(function get() {
  return get(123);  // Calls itself - infinite recursion!
});
```

Instead of:
```javascript
function get(x) { return x * 2; }
doSomething(function get2() {  // Renamed to avoid collision
  return get(123);  // Correctly calls outer get
});
```

Fixes #25648

🤖 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-14 12:52:41 -08:00
robobun
e6733333f0 fix(sql): MySQL VARCHAR with binary collations returns string instead of Buffer (#26064)
## Summary

- Fixed MySQL VARCHAR/CHAR/TEXT columns with binary collations (like
`utf8mb4_bin`) being incorrectly returned as `Buffer` instead of
`string`
- The fix checks for `character_set == 63` (binary collation) in
addition to the BINARY flag to properly distinguish true binary types

Fixes #26063

## Root Cause

PR #26011 introduced a fix for binary column handling that checked
`column.flags.BINARY` to determine if data should be returned as
`Buffer`. However, MySQL sets the BINARY flag on VARCHAR/CHAR/TEXT
columns with binary collations (like `utf8mb4_bin`) even though they
should return strings.

The proper way to detect true binary types (BINARY, VARBINARY, BLOB) is
to check if `character_set == 63` (the "binary" collation), not just the
BINARY flag.

## Changes

1. **Text Protocol** (`ResultSet.zig:143-148`): Updated binary check to
`column.flags.BINARY and column.character_set == 63`
2. **Binary Protocol** (`DecodeBinaryValue.zig:154-156`): Added
`character_set` parameter and updated binary check

## Test plan

- [ ] Added regression test `test/regression/issue/26063.test.ts` that
tests VARCHAR, CHAR, and TEXT columns with `utf8mb4_bin` collation
return strings
- [ ] Test verifies that true BINARY/VARBINARY/BLOB columns still return
Buffers

🤖 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 12:50:36 -08:00
Ciro Spaciari
22bebfc467 respect agent options and connectOpts in https module (#25937) 2026-01-14 11:52:53 -08:00
robobun
1800093a64 fix(install): use scope-specific registry for scoped packages in frozen lockfile (#26047)
## Summary
- Fixed `bun install --frozen-lockfile` to use scope-specific registry
for scoped packages when the lockfile has an empty registry URL

When parsing a `bun.lock` file with an empty registry URL for a scoped
package (like `@example/test-package`), bun was unconditionally using
the default npm registry (`https://registry.npmjs.org/`) instead of
looking up the scope-specific registry from `bunfig.toml`.

For example, with this configuration in `bunfig.toml`:
```toml
[install.scopes]
example = { url = "https://npm.pkg.github.com" }
```

And this lockfile entry with an empty registry URL:
```json
"@example/test-package": ["@example/test-package@1.0.0", "", {}, "sha512-AAAA"]
```

bun would try to fetch from
`https://registry.npmjs.org/@example/test-package/-/...` instead of
`https://npm.pkg.github.com/@example/test-package/-/...`.

The fix uses `manager.scopeForPackageName()` (the same pattern used in
`pnpm.zig`) to look up the correct scope-specific registry URL.

## Test plan
- [x] Added regression test `test/regression/issue/026039.test.ts` that
verifies:
  - Scoped packages use the scope-specific registry from `bunfig.toml`
  - Non-scoped packages continue to use the default registry
- [x] Verified test fails with system bun (without fix) and passes with
debug build (with fix)

Fixes #26039

🤖 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: Jarred Sumner <jarred@jarredsumner.com>
2026-01-14 10:22:30 -08:00
Jarred Sumner
967a6a2021 Fix blocking realpathSync call (#26056)
### 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>
2026-01-13 23:05:46 -08:00
Jarred Sumner
49d0fbd2de Update 25716.test.ts 2026-01-13 22:38:31 -08:00
Tommy D. Rossi
af2317deb4 fix(bundler): allow reactFastRefresh Bun.build option with non-browser targets (#26035)
### What does this PR do?

Previously, reactFastRefresh was silently ignored when target was not
'browser', even when explicitly enabled. This was confusing as there was
no warning or error.

This change removes the `target == .browser` check, trusting explicit
user intent. If users enable reactFastRefresh with a non-browser target,
the transform will now be applied. If `$RefreshReg$` is not defined at
runtime, it will fail fast with a clear error rather than silently doing
nothing.

Use case: Terminal UIs (like [termcast](https://termcast.app)) need
React Fast Refresh with target: 'bun' for hot reloading in non-browser
environments.

### How did you verify your code works?

Updated existing test removing target browser
2026-01-13 22:13:17 -08:00
robobun
ab009fe00d fix(init): respect --minimal flag for agent rule files (#26051)
## Summary
- Fixes `bun init --minimal` creating Cursor rules files and CLAUDE.md
when it shouldn't
- Adds regression test to verify `--minimal` only creates package.json
and tsconfig.json

## Test plan
- [x] Verify test fails with system bun (unfixed): `USE_SYSTEM_BUN=1 bun
test test/cli/init/init.test.ts -t "bun init --minimal"`
- [x] Verify test passes with debug build: `bun bd test
test/cli/init/init.test.ts -t "bun init --minimal"`
- [x] All existing init tests pass: `bun bd test
test/cli/init/init.test.ts`

Fixes #26050

🤖 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-13 18:33:42 -08:00
Jarred Sumner
fbd800551b Bump 2026-01-13 15:06:36 -08:00
robobun
113cdd9648 fix(completions): add update command to Fish completions (#25978)
## Summary

- Add the `update` subcommand to Fish shell completions
- Apply the install/add/remove flags (--global, --dry-run, --force,
etc.) to the `update` command

Previously, Fish shell autocompletion for `bun update --gl<TAB>` would
not work because:
1. The `update` command was missing from the list of built-in commands
2. The install/add/remove flags were not being applied to `update`

Fixes #25953

## Test plan

- [x] Verify `update` appears in subcommand completions (`bun <TAB>`)
- [x] Verify `--global` flag completion works (`bun update --gl<TAB>`)
- [x] Verify other install flags work with update (--dry-run, --force,
etc.)

🤖 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-13 15:05:00 -08:00
robobun
3196178fa7 fix(timers): add _idleStart property to Timeout object (#26021)
## Summary

- Add `_idleStart` property (getter/setter) to the Timeout object
returned by `setTimeout()` and `setInterval()`
- The property returns a monotonic timestamp (in milliseconds)
representing when the timer was created
- This mimics Node.js's behavior where `_idleStart` is the libuv
timestamp at timer creation time

## Test plan

- [x] Verified test fails with `USE_SYSTEM_BUN=1 bun test
test/regression/issue/25639.test.ts`
- [x] Verified test passes with `bun bd test
test/regression/issue/25639.test.ts`
- [x] Manual verification:
  ```bash
  # Bun with fix - _idleStart exists
./build/debug/bun-debug -e "const t = setTimeout(() => {}, 0);
console.log('_idleStart' in t, typeof t._idleStart); clearTimeout(t)"
  # Output: true number
  
  # Node.js reference - same behavior
node -e "const t = setTimeout(() => {}, 0); console.log('_idleStart' in
t, typeof t._idleStart); clearTimeout(t)"
  # Output: true number
  ```

Closes #25639

🤖 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-12 19:35:11 -08:00
robobun
d530ed993d fix(css): restore handler context after minifying nested rules (#25997)
## Summary
- Fixes handler context not being restored after minifying nested CSS
rules
- Adds regression test for the issue

## Test plan
- [x] Test fails with `USE_SYSTEM_BUN=1 bun test
test/regression/issue/25794.test.ts`
- [x] Test passes with `bun bd test test/regression/issue/25794.test.ts`

Fixes #25794

🤖 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-12 14:55:27 -08:00
Dylan Conway
959169dfaf feat(archive): change API to constructor-based with S3 support (#25940)
## Summary
- Change Archive API from `Bun.Archive.from(data)` to `new
Bun.Archive(data, options?)`
- Change compression options from `{ gzip: true }` to `{ compress:
"gzip", level?: number }`
- Default to no compression when no options provided
- Use `{ compress: "gzip" }` to enable gzip compression (level 6 by
default)
- Add Archive support for S3 and local file writes via `Bun.write()`

## New API

```typescript
// Create archive - defaults to uncompressed tar
const archive = new Bun.Archive({
  "hello.txt": "Hello, World!",
  "data.json": JSON.stringify({ foo: "bar" }),
});

// Enable gzip compression
const compressed = new Bun.Archive(files, { compress: "gzip" });

// Gzip with custom level (1-12)
const maxCompression = new Bun.Archive(files, { compress: "gzip", level: 12 });

// Write to local file
await Bun.write("archive.tar", archive);           // uncompressed by default
await Bun.write("archive.tar.gz", compressed);     // gzipped

// Write to S3
await client.write("archive.tar.gz", compressed);          // S3Client.write()
await Bun.write("s3://bucket/archive.tar.gz", compressed); // S3 URL
await s3File.write(compressed);                            // s3File.write()

// Get bytes/blob (uses compression setting from constructor)
const bytes = await archive.bytes();
const blob = await archive.blob();
```

## TypeScript Types

```typescript
type ArchiveCompression = "gzip";

type ArchiveOptions = {
  compress?: "gzip";
  level?: number;  // 1-12, default 6 when gzip enabled
};
```

## Test plan
- [x] 98 archive tests pass
- [x] S3 integration tests updated to new API
- [x] TypeScript types updated
- [x] Documentation updated with new examples

🤖 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>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2026-01-12 14:54:21 -08:00
SUZUKI Sosuke
461ad886bd fix(http): fix Strong reference leak in server response streaming (#25965)
## Summary

Fix a memory leak in `RequestContext.doRenderWithBody()` where
`Strong.Impl` memory was leaked when proxying streaming responses
through Bun's HTTP server.

## Problem

When a streaming response (e.g., from a proxied fetch request) was
forwarded through Bun's server:

1. `response_body_readable_stream_ref` was initialized at line 1836
(from `lock.readable`) or line 1841 (via `Strong.init()`)
2. For `.Bytes` streams with `has_received_last_chunk=false`, a **new**
Strong reference was created at line 1902
3. The old Strong reference was **never deinit'd**, causing
`Strong.Impl` memory to leak

This leak accumulated over time with every streaming response proxied
through the server.

## Solution

Add `this.response_body_readable_stream_ref.deinit()` before creating
the new Strong reference. This is safe because:

- `stream` exists as a stack-local variable
- JSC's conservative GC tracks stack-local JSValues
- No GC can occur between consecutive synchronous Zig statements
- Therefore, `stream` won't be collected between `deinit()` and
`Strong.init()`

## Test

Added `test/js/web/fetch/server-response-stream-leak.test.ts` which:
- Creates a backend server that returns delayed streaming responses
- Creates a proxy server that forwards the streaming responses
- Makes 200 requests and checks that ReadableStream objects don't
accumulate
- Fails on system Bun v1.3.5 (202 leaked), passes with the fix

## Related

Similar to the Strong reference leak fixes in:
- #23313 (fetch memory leak)
- #25846 (fetch cyclic reference leak)
2026-01-12 14:41:58 -08:00
Markus Schmidt
b6abbd50a0 fix(Bun.SQL): handle binary columns in MySQL correctly (#26011)
## What does this PR do?
Currently binary columns are returned as strings which means they get
corrupted when encoded in UTF8. This PR returns binary columns as
Buffers which is what user's actually expect and is also consistent with
PostgreSQL and SQLite.
### How did you verify your code works?
I added tests to verify the correct behavior. Before there were no tests
for binary columns at all.

This fixes #23991
2026-01-12 11:56:02 -08:00
Alex Miller
beccd01647 fix(FileSink): add Promise<number> to FileSink.write() return type (#25962)
Co-authored-by: Alistair Smith <hi@alistair.sh>
2026-01-11 12:51:16 -08:00
github-actions[bot]
35eb53994a deps: update sqlite to 3.51.200 (#25957)
## What does this PR do?

Updates SQLite to version 3.51.200

Compare: https://sqlite.org/src/vdiff?from=3.51.1&to=3.51.200

Auto-updated by [this
workflow](https://github.com/oven-sh/bun/actions/workflows/update-sqlite3.yml)

Co-authored-by: Jarred-Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2026-01-10 22:46:17 -08:00
robobun
ebf39e9811 fix(install): prevent use-after-free when retrying failed HTTP requests (#25949) 2026-01-10 18:03:24 -08:00
Ciro Spaciari
b610e80ee0 fix(http): properly handle pipelined data in CONNECT requests (#25938)
Fixes #25862

### What does this PR do?

When a client sends pipelined data immediately after CONNECT request
headers in the same TCP segment, Bun now properly delivers this data to
the `head` parameter of the 'connect' event handler, matching Node.js
behavior.

This enables compatibility with Cap'n Proto's KJ HTTP library used by
Cloudflare's workerd runtime, which pipelines RPC data after CONNECT.

### How did you verify your code works?
<img width="694" height="612" alt="CleanShot 2026-01-09 at 15 30 22@2x"
src="https://github.com/user-attachments/assets/3ffe840e-1792-429c-8303-d98ac3e6912a"
/>

Tests

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-09 19:08:02 -08:00
robobun
7076a49bb1 feat(archive): add TypeScript types, docs, and files() benchmark (#25922)
## Summary

- Add comprehensive TypeScript type definitions for `Bun.Archive` in
`bun.d.ts`
  - `ArchiveInput` and `ArchiveCompression` types
- Full JSDoc documentation with examples for all methods (`from`,
`write`, `extract`, `blob`, `bytes`, `files`)
- Add documentation page at `docs/runtime/archive.mdx`
  - Quickstart examples
  - Creating and extracting archives
  - `files()` method with glob filtering
  - Compression support
  - Full API reference section
- Add Archive to docs sidebar under "Data & Storage"
- Add `files()` benchmark comparing `Bun.Archive.files()` vs node-tar
- Shows ~7x speedup for reading archive contents into memory (59µs vs
434µs)

## Test plan

- [x] TypeScript types compile correctly
- [x] Documentation renders properly in Mintlify format
- [x] Benchmark runs successfully and shows performance comparison
- [x] Verified `files()` method works correctly with both Bun.Archive
and node-tar

🤖 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>
2026-01-09 19:00:19 -08:00
robobun
d4a966f8ae fix(install): prevent symlink path traversal in tarball extraction (#25584)
## Summary

- Fixes a path traversal vulnerability via symlink when installing
GitHub packages
- Validates symlink targets before creation to ensure they stay within
the extraction directory
- Rejects absolute symlinks and relative paths that would escape the
extraction directory

## Details

When extracting GitHub tarballs, Bun did not validate symlink targets. A
malicious tarball could:
1. Create a symlink pointing outside the extraction directory (e.g.,
`../../../../../../../tmp`)
2. Include a file entry through that symlink path (e.g.,
`symlink-to-tmp/pwned.txt`)

When extracted, the symlink would be created first, then the file would
be written through it, ending up outside the intended package directory
(e.g., `/tmp/pwned.txt`).

### The Fix

Added `isSymlinkTargetSafe()` function that:
1. Rejects absolute symlink targets (starting with `/`)
2. Normalizes the combined path (symlink location + target) and rejects
if the result starts with `..` (would escape)

## Test plan

- [x] Added regression test
`test/cli/install/symlink-path-traversal.test.ts`
- [x] Tests verify relative path traversal symlinks are blocked
- [x] Tests verify absolute symlink targets are blocked  
- [x] Tests verify safe relative symlinks within the package still work
- [x] Verified test fails with system bun (vulnerable) and passes with
debug build (fixed)

🤖 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: Dylan Conway <dylan.conway567@gmail.com>
2026-01-09 16:56:31 -08:00
Dylan Conway
7704dca660 feat(build): add --compile-executable-path CLI flag (#25934)
## Summary

Adds a new CLI flag `--compile-executable-path` that allows specifying a
custom Bun executable path for cross-compilation instead of downloading
from the npm registry.

## Usage

```bash
bun build --compile --target=bun-linux-x64 \
  --compile-executable-path=/path/to/bun-linux-x64 app.ts
```

## Motivation

The `executablePath` option was already available in the JavaScript
`Bun.build()` API. This exposes the same functionality from the CLI.

## Changes

- Added `--compile-executable-path <STR>` CLI parameter in
`src/cli/Arguments.zig`
- Added `compile_executable_path` field to `BundlerOptions` in
`src/cli.zig`
- Wired the option through to `StandaloneModuleGraph.toExecutable()` in
`src/cli/build_command.zig`

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-09 16:21:40 -08:00
Jarred Sumner
1e0f51ddcc Revert "feat(shell): add $.trace for analyzing shell commands without execution (#25667)"
This reverts commit 6b5de25d8a.
2026-01-09 16:20:18 -08:00
Ciro Spaciari
32a76904fe remove agent in global WebSocket add agent support in ws module (#25935)
### What does this PR do?
remove agent in global WebSocket (in node.js it uses dispatcher not
agent) add agent support in ws module (this actually uses agent)
### How did you verify your code works?
Tests
2026-01-09 16:18:47 -08:00
Jarred Sumner
367eeb308e Fixes #23177 (#25936)
### What does this PR do?

Fixes #23177

### How did you verify your code works?
2026-01-09 15:14:00 -08:00
freya
1879b7eeca Note that Windows API handles should be u64, not ptr (#25886)
### What does this PR do?

Update FFI documentation with a note on Windows API handle values, as
pointer encoding to double causes intermittent failures with some
classes of handles.

Putting it in this accordion feels less than ideal but it's also a very
specific use case.

The specific issue I experienced: HDCs and HBITMAPs are basically 32
bits, although they are typedef'd in the headers to HANDLE. They are
returned from and passed to the GDI APIs as sign-extended versions of
the underlying 32-bit value, and so when going through the ptr <->
double pathway of bun FFI, the bottom 11 bits of those values are lost
if the original value had bit 31 set, and subsequent calls will fail.
Probably this is fixable by correctly encoding 'negative' pointers in
the double representation, and I might tackle that if I find time.
2026-01-09 14:04:14 -08:00
robobun
70fa6af355 feat: add Bun.Archive API for creating and extracting tarballs (#25665)
## Summary

- Adds new `Bun.Archive` API for working with tar archives
- `Bun.Archive.from(data)` - Create archive from object, Blob,
TypedArray, or ArrayBuffer
- `Bun.Archive.write(path, data, compress?)` - Write archive to disk
(async)
- `archive.extract(path)` - Extract to directory, returns
`Promise<number>` (file count)
- `archive.blob(compress?)` - Get archive as Blob (async)
- `archive.bytes(compress?)` - Get archive as Uint8Array (async)

Key implementation details:
- Uses existing libarchive bindings for tarball creation/extraction via
`extractToDisk`
- Uses libdeflate for gzip compression
- Immediate byte copying for GC safety (no JSValue protection, no
`hasPendingActivity`)
- Async operations run on worker pool threads with proper VM reference
handling
- Growing memory buffer via `archive_write_open2` callbacks for
efficient tarball creation

## Test plan

- [x] 65 comprehensive tests covering:
  - Normal operations (create, extract, blob, bytes, write)
  - GC safety (unreferenced archives, mutation isolation)  
  - Error handling (invalid args, corrupted data, I/O errors)
- Edge cases (large files, many files, special characters, path
normalization)
  - Concurrent operations

🤖 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: Dylan Conway <dylan.conway567@gmail.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2026-01-09 00:33:35 -08:00
robobun
eb5b498c62 fix(test): make fake timers work with testing-library/react (#25915)
## Summary

Fixes #25869

Two fixes to enable `jest.useFakeTimers()` to work with
`@testing-library/react` and `@testing-library/user-event`:

- Set `setTimeout.clock = true` when fake timers are enabled.
testing-library/react's `jestFakeTimersAreEnabled()` checks for this
property to determine if `jest.advanceTimersByTime()` should be called
when draining the microtask queue. Without this, testing-library never
advances timers.

- Make `advanceTimersByTime(0)` fire `setTimeout(fn, 0)` timers.
`setTimeout(fn, 0)` is internally scheduled with a 1ms delay per HTML
spec. Jest/testing-library expect `advanceTimersByTime(0)` to fire such
"immediate" timers, but we were advancing by 0ms so they never fired.

## Test plan

- [x] All 30 existing fake timer tests pass
- [x] New regression test validates both fixes
- [x] Original user-event reproduction now works (test completes instead
of hanging)

🤖 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>
2026-01-08 20:25:35 -08:00
Dylan Conway
596e83c918 fix: correct logic bugs in libarchive, s3 credentials, and postgres bindings (#25905)
## Summary

- **libarchive.zig:110**: Fix self-assignment bug where `this.pos` was
assigned to itself instead of `new_pos`
- **s3/credentials.zig:165,176,199**: Fix impossible range checks -
`and` should be `or` for pageSize, partSize, and retry validation (a
value cannot be both less than MIN and greater than MAX simultaneously)
- **postgres.zig:14**: Fix copy-paste error where createConnection
function was internally named "createQuery"

## Test plan

- [ ] Verify S3 credential validation now properly rejects out-of-range
values for pageSize, partSize, and retry
- [ ] Verify libarchive seek operations work correctly
- [ ] Verify postgres createConnection function has correct internal
name

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
2026-01-08 19:46:06 -08:00
SUZUKI Sosuke
3842a5ee18 Fix stack precommit crash on Windows (#25891)
### What does this PR do?

Attempt to fix stack precommit crash on Windows

https://github.com/oven-sh/WebKit/pull/128

### How did you verify your code works?
2026-01-08 18:12:51 -08:00
robobun
50daf5df27 fix(io): respect mode option when copying files with Bun.write() (#25906)
## Summary
- Fixes #25903 - `Bun.write()` mode option ignored when copying from
`Bun.file()`
- The destination file now correctly uses the specified `mode` option
instead of default permissions
- Works on Linux (via open flags), macOS (chmod after clonefile), and
Windows (chmod after copyfile)

## Test plan
- [x] Added regression test in `test/regression/issue/25903.test.ts`
- [x] Test passes with `bun bd test test/regression/issue/25903.test.ts`
- [x] Test fails with `USE_SYSTEM_BUN=1 bun test
test/regression/issue/25903.test.ts` (verifies the bug exists)

## Changes
- `src/bun.js/webcore/Blob.zig`: Add `mode` field to `WriteFileOptions`
and parse from options
- `src/bun.js/webcore/blob/copy_file.zig`: Use `destination_mode` in
`CopyFile` struct and `doOpenFile`
- `packages/bun-types/bun.d.ts`: Add `mode` option to BunFile copy
overloads

🤖 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>
2026-01-08 17:51:42 -08:00
Ciro Spaciari
c90c0e69cb feat(websocket): add HTTP/HTTPS proxy support (#25614)
## Summary

Add `proxy` option to WebSocket constructor for connecting through HTTP
CONNECT proxies.

### Features
- Support for `ws://` and `wss://` through HTTP proxies
- Support for `ws://` and `wss://` through HTTPS proxies (with
`rejectUnauthorized: false`)
- Proxy authentication via URL credentials (Basic auth)
- Custom proxy headers support
- Full TLS options (`ca`, `cert`, `key`, etc.) for target connections
using `SSLConfig.fromJS`

### API

```javascript
// String format
new WebSocket("wss://example.com", { proxy: "http://proxy:8080" })

// With credentials
new WebSocket("wss://example.com", { proxy: "http://user:pass@proxy:8080" })

// Object format with custom headers
new WebSocket("wss://example.com", {
  proxy: { url: "http://proxy:8080", headers: { "X-Custom": "value" } }
})

// HTTPS proxy
new WebSocket("ws://example.com", {
  proxy: "https://proxy:8443",
  tls: { rejectUnauthorized: false }
})
```

### Implementation

| File | Changes |
|------|---------|
| `WebSocketUpgradeClient.zig` | Proxy state machine and CONNECT
handling |
| `WebSocketProxyTunnel.zig` | **New** - TLS tunnel inside CONNECT for
wss:// through HTTP proxy |
| `JSWebSocket.cpp` | Parse proxy option and TLS options using
`SSLConfig.fromJS` |
| `WebSocket.cpp` | Pass proxy parameters to Zig, handle HTTPS proxy
socket selection |
| `bun.d.ts` | Add `proxy` and full TLS options to WebSocket types |

### Supported Scenarios

| Scenario | Status |
|----------|--------|
| ws:// through HTTP proxy |  Working |
| wss:// through HTTP proxy |  Working (TLS tunnel) |
| ws:// through HTTPS proxy |  Working (with `rejectUnauthorized:
false`) |
| wss:// through HTTPS proxy |  Working (with `rejectUnauthorized:
false`) |
| Proxy authentication (Basic) |  Working |
| Custom proxy headers |  Working |
| Custom CA for HTTPS proxy |   Working |

## Test plan

- [x] API tests verify proxy option is accepted in various formats
- [x] Functional tests with local HTTP CONNECT proxy server
- [x] Proxy authentication tests (Basic auth)
- [x] HTTPS proxy tests with `rejectUnauthorized: false`
- [x] Error handling tests (auth failures, wrong credentials)

Run tests: `bun test test/js/web/websocket/websocket-proxy.test.ts`

## Changelog

- Added `proxy` option to `WebSocket` constructor for HTTP/HTTPS proxy
support
- Added full TLS options (`ca`, `cert`, `key`, `passphrase`, etc.) to
`WebSocket` constructor

🤖 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>
2026-01-08 16:21:34 -08:00
robobun
24b97994e3 feat(bundler): add files option for in-memory bundling (#25852)
## Summary

Add support for in-memory entrypoints and files in `Bun.build` via the
`files` option:

```ts
await Bun.build({
  entrypoints: ["/app/index.ts"],
  files: {
    "/app/index.ts": `
      import { greet } from "./greet.ts";
      console.log(greet("World"));
    `,
    "/app/greet.ts": `
      export function greet(name: string) {
        return "Hello, " + name + "!";
      }
    `,
  },
});
```

### Features

- **Bundle entirely from memory**: No files on disk needed
- **Override files on disk**: In-memory files take priority over disk
files
- **Mix disk and virtual files**: Real files can import virtual files
and vice versa
- **Multiple content types**: Supports `string`, `Blob`, `TypedArray`,
and `ArrayBuffer`

### Use Cases

- Code generation at build time
- Injecting build-time constants
- Testing with mock modules
- Bundling dynamically generated code
- Overriding configuration files for different environments

### Implementation Details

- Added `FileMap` struct in `JSBundler.zig` with `resolve`, `get`,
`contains`, `fromJS`, and `deinit` methods
- Uses `"memory"` namespace to avoid `pathWithPrettyInitialized`
allocation issues during linking phase
- FileMap checks added in:
  - `runResolver` (entry point resolution)
  - `runResolutionForParseTask` (import resolution)
  - `enqueueEntryPoints` (entry point handling)
  - `getCodeForParseTaskWithoutPlugins` (file content reading)
- Root directory defaults to cwd when all entrypoints are in the FileMap
- Added TypeScript types with JSDoc documentation
- Added bundler documentation with examples

## Test plan

- [x] Basic in-memory file bundling
- [x] In-memory files with absolute imports
- [x] In-memory files with relative imports (same dir, subdirs, parent
dirs)
- [x] Nested/chained imports between in-memory files
- [x] TypeScript and JSX support
- [x] Blob, Uint8Array, and ArrayBuffer content types
- [x] Re-exports and default exports
- [x] In-memory file overrides real file on disk
- [x] Real file on disk imports in-memory file via relative path
- [x] Mixed disk and memory files with complex import graphs

Run tests with: `bun bd test test/bundler/bundler_files.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: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
2026-01-08 15:05:41 -08:00
Jarred Sumner
dda9a9b0fd Increase shard count for Windows & Linux test runs (#25913)
### What does this PR do?

### How did you verify your code works?
2026-01-08 14:38:35 -08:00
robobun
eeef013365 Add Bun.JSONC API for parsing JSON with comments and trailing commas (#22115)
## Summary

This PR implements a new `Bun.JSONC.parse()` API that allows parsing
JSONC (JSON with Comments) files. It addresses the feature request from
issue #16257 by providing a native API for parsing JSON with comments
and trailing commas.

The implementation follows the same pattern as `Bun.YAML` and
`Bun.TOML`, leveraging the existing `TSConfigParser` which already
handles JSONC parsing internally.

## Features

- **Parse JSON with comments**: Supports both `//` single-line and `/*
*/` block comments
- **Handle trailing commas**: Works with trailing commas in objects and
arrays
- **Full JavaScript object conversion**: Returns native JavaScript
objects/arrays
- **Error handling**: Proper error throwing for invalid JSON
- **TypeScript compatibility**: Works with TypeScript config files and
other JSONC formats

## Usage Example

```javascript
const result = Bun.JSONC.parse(`{
  // This is a comment
  "name": "my-app",
  "version": "1.0.0", // trailing comma is allowed
  "dependencies": {
    "react": "^18.0.0",
  },
}`);
// Returns native JavaScript object
```

## Implementation Details

- Created `JSONCObject.zig` following the same pattern as
`YAMLObject.zig` and `TOMLObject.zig`
- Uses the existing `TSConfigParser` from `json.zig` which already
handles comments and trailing commas
- Added proper C++ bindings and exports following Bun's established
patterns
- Comprehensive test suite covering various JSONC features

## Test Plan

- [x] Basic JSON parsing works
- [x] Single-line comments (`//`) are handled correctly
- [x] Block comments (`/* */`) are handled correctly  
- [x] Trailing commas in objects and arrays work
- [x] Complex nested structures parse correctly
- [x] Error handling for invalid JSON
- [x] Empty objects and arrays work
- [x] Boolean and null values work correctly

🤖 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>
Co-authored-by: Jarred-Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2026-01-08 13:27:47 -08:00
Kaj Kowalski
65d006aae0 fix(parser): fix bytecode CJS pragma detection after shebang (#25868)
### What does this PR do?

Fix bytecode CJS pragma detection when source file contains a shebang.

When bundling with `--bytecode` and the source file has a shebang, the
output silently fails to execute (exits 0, no output).

Reproduction:
[github.com/kjanat/bun-bytecode-banner-bug](https://github.com/kjanat/bun-bytecode-banner-bug)

```js
// Bundled output:
#!/usr/bin/env bun           // shebang preserved
// @bun @bytecode @bun-cjs   // pragma on line 2
(function(exports, require, module, __filename, __dirname) { ... })
```

The pragma parser in `hasBunPragma()` correctly skips the shebang line,
but uses `self.lexer.end` instead of `contents.len` when scanning for
`@bun-cjs`/`@bytecode` tokens. This causes the pragma to not be
recognized.

**Fix:**

```zig
// Before
while (cursor < self.lexer.end) : (cursor += 1) {

// After
while (cursor < end) : (cursor += 1) {
```

Where `end` is already defined as `contents.len` at the top of the
function.

### How did you verify your code works?

- Added bundler test `banner/SourceHashbangWithBytecodeAndCJSTargetBun`
in `test/bundler/bundler_banner.test.ts`
- Added regression tests in
`test/regression/issue/bun-bytecode-shebang.test.ts` that verify:
  - CJS wrapper executes when source has shebang
  - CJS wrapper executes when source has shebang + bytecode pragma
- End-to-end: bundled bytecode output with source shebang runs correctly
- Ran the tests in the
[kjanat/bun-bytecode-banner-bug](https://github.com/kjanat/bun-bytecode-banner-bug)
repo to verify the issue is fixed

---------

Signed-off-by: Kaj Kowalski <info@kajkowalski.nl>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-01-08 11:32:08 -08:00
Alistair Smith
8b59b8d17d types: Missing methods on udp socket 2026-01-08 09:38:31 +00:00
robobun
a1f1252771 refactor(test): migrate bun-install tests to concurrent execution (#25895) 2026-01-08 01:06:03 -08:00
Jarred Sumner
bf1e4922b4 Speed up some more tests (#25892)
### What does this PR do?

### How did you verify your code works?
2026-01-07 23:39:10 -08:00
SUZUKI Sosuke
fbf47d0256 fix: use JSPromise helper methods instead of direct internal field manipulation (#25889)
## Summary

Fixes `ASSERTION FAILED: isUInt32AsAnyInt()` errors that occurred
intermittently, particularly in inspector-related tests.

## Problem

The code was directly manipulating JSPromise internal fields using
`asUInt32AsAnyInt()`:

```cpp
promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32AsAnyInt()
```

This caused assertion failures when the internal field state was not a
valid uint32.

## Solution

Use WebKit's official JSPromise helper methods instead:

| Before | After |
|--------|-------|
| Direct `internalField` manipulation for rejected promise |
`promise->rejectAsHandled(vm, globalObject, value)` |
| Direct `internalField` manipulation for resolved promise |
`promise->fulfill(vm, globalObject, value)` |
| Direct `internalField` manipulation for handled flag |
`promise->markAsHandled()` |

## Files Changed

- `src/bun.js/bindings/ZigGlobalObject.cpp`
- `src/bun.js/bindings/ModuleLoader.cpp`
- `src/bake/BakeGlobalObject.cpp`

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 23:23:27 -08:00
robobun
f83214e0a9 test(http2): refactor tests to use describe.concurrent and await using (#25893)
## Summary
- Use `describe.concurrent` at module scope for parallel test execution
across node/bun executables and padding strategies
- Replace `Bun.spawnSync` with async `Bun.spawn` in memory leak test
- Replace `beforeEach`/`afterEach` server setup with `await using` in
each test
- Add `Symbol.asyncDispose` to `nodeEchoServer` helper for proper
cleanup
- Fix IPv6/IPv4 binding issue by explicitly binding echo server to
127.0.0.1

## Test plan
- [x] Run `bun test test/js/node/http2/node-http2.test.js` - all 245
tests pass (6 skipped)
- [x] Verify tests run faster due to concurrent execution

🤖 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>
2026-01-07 23:15:50 -08:00
robobun
81debb4269 feat(bundler): add metafile support matching esbuild format (#25842) 2026-01-07 22:46:51 -08:00
robobun
962ac0c2fd refactor(test): use describe.concurrent and async spawn in bun-run.test.ts (#25890)
## Summary

- Wrap all tests in `describe.concurrent` at module scope for parallel
test execution
- Replace `Bun.spawnSync` with `Bun.spawn` + `await` throughout
- Replace `run_dir`/`writeFile` pattern with `tempDir` for automatic
cleanup via `using` declarations
- Remove `beforeEach` hook that created shared temp directory

## Test plan

- [x] All 291 tests pass with `bun bd test
test/cli/install/bun-run.test.ts`
- [x] All tests pass with `USE_SYSTEM_BUN=1 bun test
test/cli/install/bun-run.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>
2026-01-07 21:52:41 -08:00
Jarred Sumner
bdc95c2dc5 Speed up shell leak test (#25880)
### What does this PR do?
18s -> 3s

### How did you verify your code works?
2026-01-07 21:31:33 -08:00
Jarred Sumner
29a6c0d263 Speed up require-cache.test.ts (#25887)
### What does this PR do?

21.92s -> 6s

### How did you verify your code works?
2026-01-07 21:13:28 -08:00
Jarred Sumner
39e2c22e1a fix(http): disable keep-alive on proxy authentication failure (407) (#25884)
## Summary

- Disable HTTP keep-alive when a proxy returns a 407 (Proxy
Authentication Required) status code
- This prevents subsequent requests from trying to reuse a connection
that the proxy server has closed
- Refactored proxy tests to use `describe.concurrent` and async
`Bun.spawn` patterns

## Test plan

- [x] Added test `simultaneous proxy auth failures should not hang` that
verifies multiple concurrent requests with invalid proxy credentials
complete without hanging
- [x] Existing proxy tests pass

🤖 Generated with [Claude Code](https://claude.ai/code)
2026-01-07 20:49:30 -08:00
robobun
b20a70dc40 fix: use JSONParseWithException for proper error handling (#25881)
## Summary
- **SQLClient.cpp**: Fix bug where `RETURN_IF_EXCEPTION` after
`JSONParse` would never trigger on JSON parse failure since `JSONParse`
doesn't throw
- **BunString.cpp**: Simplify by using `JSONParseWithException` instead
of manually checking for empty result and throwing

## Details

`JSC::JSONParse` returns an empty `JSValue` on failure **without
throwing an exception**. This means that `RETURN_IF_EXCEPTION(scope,
{})` will never catch JSON parsing errors when used after `JSONParse`.

Before this fix in `SQLClient.cpp`:
```cpp
JSC::JSValue json = JSC::JSONParse(globalObject, str);
RETURN_IF_EXCEPTION(scope, {});  // This never triggers on parse failure!
return json;  // Returns empty JSValue
```

This could cause issues when parsing invalid JSON data from SQL
databases (e.g., PostgreSQL's JSON/JSONB columns).

`JSONParseWithException` properly throws a `SyntaxError` exception that
`RETURN_IF_EXCEPTION` can catch.

## Test plan
- [x] Build succeeds with `bun bd`
- The changes follow the same pattern used in `ModuleLoader.cpp` and
`BunObject.cpp`

🤖 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>
2026-01-07 20:13:01 -08:00
Jarred Sumner
1f22f4447d Align temp directory resolution with os.tmpdir() (#25878)
## Summary
- Aligns Bun's temp directory resolution with Node.js's `os.tmpdir()`
behavior
- Checks `TMPDIR`, `TMP`, and `TEMP` environment variables in order
(matching Node.js)
- Uses `bun.once` for lazy initialization instead of mutable static
state
- Removes `setTempdir` function and simplifies the API to use
`RealFS.tmpdirPath()` directly

## Test plan
- [ ] Verify temp directory resolution matches Node.js behavior
- [ ] Test with various environment variable configurations
- [ ] Ensure existing tests pass with `bun bd test`

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

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-07 16:09:49 -08:00
Bradley Walters
ff590e9cfd fix(cmake): fix include paths for local WebKit (#25815)
### What does this PR do?

Without this change, building with `-DWEBKIT_LOCAL=ON` fails with:
```
/work/bun/src/bun.js/bindings/BunObject.cpp:12:10: fatal error: 'JavaScriptCore/JSBase.h' file not found
   12 | #include <JavaScriptCore/JSBase.h>
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
```

The reason for this is because the directory structure differs between
downloaded and local WebKit.

Downloaded WebKit:
```
build/debug/cache/webkit-6d0f3aac0b817cc0/
  └── include/
      └── JavaScriptCore/
          └── JSBase.h          ← Direct path
```

Local WebKit:
```
vendor/WebKit/WebKitBuild/Debug/
  └── JavaScriptCore/Headers/
      └── JavaScriptCore/
          └── JSBase.h          ← Nested path
```

The include paths are thus configured differently for each build type.

For Remote WebKit (when WEBKIT_LOCAL=OFF):
- SetupWebKit.cmake line 22 sets: WEBKIT_INCLUDE_PATH =
${WEBKIT_PATH}/include
- BuildBun.cmake line 1253 adds:
include_directories(${WEBKIT_INCLUDE_PATH})
- This resolves to: build/debug/cache/webkit-6d0f3aac0b817cc0/include/
- So #include <JavaScriptCore/JSBase.h> finds the file at
include/JavaScriptCore/JSBase.h 

For Local WebKit (when WEBKIT_LOCAL=ON):
- The original code only added:
${WEBKIT_PATH}/JavaScriptCore/Headers/JavaScriptCore
- This resolves to:
vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/Headers/JavaScriptCore/
- So #include <JavaScriptCore/JSBase.h> fails because there's no
JavaScriptCore/ subdirectory at that level 
- The fix adds: ${WEBKIT_PATH}/JavaScriptCore/Headers
- Now the include path includes:
vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/Headers/
- So #include <JavaScriptCore/JSBase.h> finds the file at
Headers/JavaScriptCore/JSBase.h 

### How did you verify your code works?

Built locally.

Co-authored-by: Carl Smedstad <carsme@archlinux.org>
2026-01-07 15:46:46 -08:00
Bradley Walters
18f242daa1 feat(cmake): simplify bindgenv2 error handling using COMMAND_ERROR_IS_FATAL (#25814)
### What does this PR do?

In CMake, failure to execute a process will place a message string in
the RESULT_VARIABLE. If the message string starts with 'No' such as in
'No such file or directory' then CMake will interpret that as the
boolean false and not halt the build. The new code uses a built-in
option to halt the build on any failure, so the script will halt
correctly if that error occurs. This could also be fixed by quoting, but
might as well use the CMake feature.

I encountered this error when I improperly defined BUN_EXECUTABLE.

### How did you verify your code works?

<details>

<summary>Ran the build with invalid executable path:</summary>

```
$ node ./scripts/build.mjs \
                   -GNinja \
                   -DCMAKE_BUILD_TYPE=Debug \
                   -B build/debug \
                   --log-level=NOTICE \
                   -DBUN_EXECUTABLE="foo" \
                   -DNPM_EXECUTABLE="$(which npm)" \
                   -DZIG_EXECUTABLE="$(which zig)" \
                   -DENABLE_ASAN=OFF

Globbed 1971 sources [178.21ms]
CMake Configure
  $ cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -B /work/bun/build/debug --log-level NOTICE -DBUN_EXECUTABLE=foo -DNPM_EXECUTABLE=/bin/npm -DZIG_EXECUTABLE=/bin/zig -DENABLE_ASAN=OFF -S /work/bun -DCACHE_STRATEGY=auto
sccache: Using local cache strategy.
```

</details>

Result:

```
CMake Error at cmake/targets/BuildBun.cmake:422 (execute_process):
  execute_process failed command indexes:

    1: "Abnormal exit with child return code: no such file or directory"

Call Stack (most recent call first):
  CMakeLists.txt:66 (include)
```
2026-01-07 15:46:00 -08:00
Bradley Walters
fbc175692f feat(cmake): log download failure from SetupWebKit.cmake (#25813)
### What does this PR do?

Before this change, downloading WebKit would fail silently if there were
e.g. a connection or TLS certificate issue. I experienced this when
trying to build bun in an overly-restrictive sandbox.

After this change, the failure reason will be visible.

### How did you verify your code works?

<details>

<summary>Brought down my network interface and ran the build:</summary>

```sh
$ node ./scripts/build.mjs \
                   -GNinja \
                   -DCMAKE_BUILD_TYPE=Debug \
                   -B build/debug \
                   --log-level=NOTICE \
                   -DBUN_EXECUTABLE="$(which node)" \
                   -DNPM_EXECUTABLE="$(which npm)" \
                   -DZIG_EXECUTABLE="$(which zig)" \
                   -DENABLE_ASAN=OFF

Globbed 1971 sources [180.67ms]
CMake Configure
  $ cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -B /work/bun/build/debug --log-level NOTICE -DBUN_EXECUTABLE=/bin/node -DNPM_EXECUTABLE=/bin/npm -DZIG_EXECUTABLE=/bin/zig -DENABLE_ASAN=OFF -S /work/bun -DCACHE_STRATEGY=auto
sccache: Using local cache strategy.
...
```

</details>

Result:

```
CMake Error at cmake/tools/SetupWebKit.cmake:99 (message):
  Failed to download WebKit: 6;"Could not resolve hostname"
Call Stack (most recent call first):
  cmake/targets/BuildBun.cmake:1204 (include)
  CMakeLists.txt:66 (include)
```
2026-01-07 15:44:57 -08:00
Jarred Sumner
22315000e0 Update CLAUDE.md 2026-01-07 12:33:21 -08:00
M Waheed
bc02c18dc5 Fix typo in PM2 configuration example
According to PM2 documentation, the name of the application is defined
by "name" key, not "title"
2026-01-07 14:04:06 +00:00
Dylan Conway
4c492c66b8 fix(bundler): fix --compile with 8+ embedded files (#25859)
## Summary

Fixes #20821

When `bun build --compile` was used with 8 or more embedded files, the
compiled binary would silently fail to execute any code (exit code 0, no
output).

**Root cause:** Chunks were sorted alphabetically by their `entry_bits`
key bytes. For entry point 0, the key starts with bit 0 set (byte
pattern `0x01`), but for entry point 8, the key has bit 8 set in the
byte (pattern `0x00, 0x01`). Alphabetically, `0x00 < 0x01`, so entry
point 8's chunk sorted before entry point 0.

This caused the wrong entry point to be identified as the main entry,
resulting in asset wrapper code being executed instead of the user's
code.

**Fix:** Custom sort that ensures `entry_point_id=0` (the main entry
point) always sorts first, with remaining chunks sorted alphabetically
for determinism.

## Test plan

- Added regression test `compile/ManyEmbeddedFiles` that embeds 8 files
and verifies the main entry point runs correctly
- Verified manually with reproduction case from issue

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-06 23:05:01 +00:00
Jack Kleeman
46801ec926 Send http2 window resize frames after half the window is used up (#25847)
### What does this PR do?
It is standard practice to send HTTP2 window resize frames once half the
window is used up:
- [nghttp2](https://en.wikipedia.org/wiki/Nghttp2#HTTP/2_implementation)
- [Go](https://github.com/golang/net/blob/master/http2/flow.go#L31)
- [Rust
h2](https://github.com/hyperium/h2/blob/master/src/proto/streams/flow_control.rs#L17)

The current behaviour of Bun is to send a window update once the client
has sent 65535 bytes exactly. This leads to an interruption in
throughput while the window update is received.

This is not just a performance concern however, I think some clients
will not handle this well, as it means that if you stop sending data
even 1 byte before 65535, you have a deadlock. The reason I came across
this issue is that it appears that the Rust `hyper` crate always
reserves an additional 1 byte from the connection for each http2 stream
(https://github.com/hyperium/hyper/blob/master/src/proto/h2/mod.rs#L122).
This means that when you have two concurrent requests, the client treats
it like the window is exhausted when it actually has a byte remaining,
leading to a sequential dependency between the requests that can create
deadlocks if they depend on running concurrently. I accept this is not a
problem with bun, but its a happy accident that we can resolve such
off-by-one issues by increasing the window size once it is 50% utilized

### How did you verify your code works?
Using wireshark, bun debug logging, and client logging I can observe
that the window updates are now sent after 32767 bytes. This also
resolves the h2 crate client issue.
2026-01-06 11:37:50 -08:00
robobun
5617b92a5a test: refactor spawnSync to spawn with describe.concurrent (#25849)
## Summary

- Refactor 16 test files to use async `Bun.spawn` instead of
`Bun.spawnSync`
- Wrap tests in `describe.concurrent` blocks for parallel execution
- Use `await using` for automatic resource cleanup

## Performance Improvement

| Test File | Before | After | Improvement |
|-----------|--------|-------|-------------|
| `node-module-module.test.js` (28 tests) | ~325ms | ~185ms | **43%
faster** |
| `non-english-import.test.js` (3 tests) | ~238ms | ~157ms | **34%
faster** |

## Files Changed

- `test/cli/run/commonjs-invalid.test.ts`
- `test/cli/run/commonjs-no-export.test.ts`
- `test/cli/run/empty-file.test.ts`
- `test/cli/run/jsx-symbol-collision.test.ts`
- `test/cli/run/run-cjs.test.ts`
- `test/cli/run/run-extensionless.test.ts`
- `test/cli/run/run-shell.test.ts`
- `test/cli/run/run-unicode.test.ts`
- `test/js/bun/resolve/non-english-import.test.js`
- `test/js/node/module/node-module-module.test.js`
- `test/regression/issue/00631.test.ts`
- `test/regression/issue/03216.test.ts`
- `test/regression/issue/03830.test.ts`
- `test/regression/issue/04011.test.ts`
- `test/regression/issue/04893.test.ts`
- `test/regression/issue/hashbang-still-works.test.ts`

## Test plan

- [x] All refactored tests pass with `USE_SYSTEM_BUN=1 bun test <file>`

🤖 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>
2026-01-06 15:37:56 +00:00
Jarred Sumner
c6a73fc23e Hardcode __NR_close_range (#25840)
### What does this PR do?

Fixes https://github.com/oven-sh/bun/issues/25839

### How did you verify your code works?
2026-01-06 15:07:19 +00:00
Andrew Johnston
3de2dc1287 fix: use the computed size of the Offsets struct instead of hard-co… (#25816)
### What does this PR do?

- I was trying to understand how the SEA bundling worked in Bun and
noticed the size of the `Offsets` struct is hard-coded here to 32. It
should use the computed size to be future proof to changes in the
schema.

### How did you verify your code works?

- I didn't. Can add tests if this is not covered by existing tests.
ChatGPT agreed with me though. =)
2026-01-06 15:04:28 +00:00
SUZUKI Sosuke
370e6fb9fa fix(fetch): fix ReadableStream memory leak when using stream body (#25846)
## Summary

This PR fixes a memory leak that occurs when `fetch()` is called with a
`ReadableStream` body. The ReadableStream objects were not being
properly released, causing them to accumulate in memory.

## Problem

When using `fetch()` with a ReadableStream body:

```javascript
const stream = new ReadableStream({
  start(controller) {
    controller.enqueue(new TextEncoder().encode("data"));
    controller.close();
  }
});

await fetch(url, { method: "POST", body: stream });
```

The ReadableStream objects leak because `FetchTasklet.clearData()` has a
conditional that prevents `detach()` from being called on ReadableStream
request bodies after streaming has started.

### Root Cause

The problematic condition in `clearData()`:

```zig
if (this.request_body != .ReadableStream or this.is_waiting_request_stream_start) {
    this.request_body.detach();
}
```

After `startRequestStream()` is called:
- `is_waiting_request_stream_start` becomes `false`
- `request_body` is still `.ReadableStream`
- The condition evaluates to `(false or false) = false`
- `detach()` is skipped → **memory leak**

### Why the Original Code Was Wrong

The original code appears to assume that when `startRequestStream()` is
called, ownership of the Strong reference is transferred to
`ResumableSink`. However, this is incorrect:

1. `startRequestStream()` creates a **new independent** Strong reference
in `ResumableSink` (see `ResumableSink.zig:119`)
2. The FetchTasklet's original reference is **not transferred** - it
becomes redundant
3. Strong references in Bun are independent - calling `deinit()` on one
does not affect the other

## Solution

Remove the conditional and always call `detach()`:

```zig
// Always detach request_body regardless of type.
// When request_body is a ReadableStream, startRequestStream() creates
// an independent Strong reference in ResumableSink, so FetchTasklet's
// reference becomes redundant and must be released to avoid leaks.
this.request_body.detach();
```

### Safety Analysis

This change is safe because:

1. **Strong references are independent**: Each Strong reference
maintains its own ref count. Detaching FetchTasklet's reference doesn't
affect ResumableSink's reference
2. **Idempotency**: `detach()` is safe to call on already-detached
references
3. **Timing**: `clearData()` is only called from `deinit()` after
streaming has completed (ref_count = 0)
4. **No UAF risk**: `deinit()` only runs when ref_count reaches 0, which
means all streaming operations have completed

## Test Results

Before fix (with system Bun):
```
Expected: <= 100
Received: 501   (Request objects leaked)
Received: 1002  (ReadableStream objects leaked)
```

After fix:
```
6 pass
0 fail
```

## Test Coverage

Added comprehensive tests in
`test/js/web/fetch/fetch-cyclic-reference.test.ts` covering:
- Response stream leaks with cyclic references
- Streaming response body leaks
- Request body stream leaks with cyclic references
- ReadableStream body leaks (no cyclic reference needed to reproduce)
- Concurrent fetch operations with cyclic references

---------

Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-06 15:00:52 +00:00
Dylan Conway
91f7a94d84 Add symbols.def to link-metadata.json (#25841)
Include the Windows module definition file (symbols.def) in
link-metadata.json, similar to how
   symbols.txt and symbols.dyn are already included for macOS and Linux.

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

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-05 17:45:41 -08:00
Prithvish Baidya
9ab6365a13 Add support for Requester Pays in S3 operations (#25514)
- Introduced `requestPayer` option in S3-related functions and
structures to handle Requester Pays buckets.
- Updated S3 client methods to accept and propagate the `requestPayer`
flag.
- Enhanced documentation for the `requestPayer` option in the S3 type
definitions.
- Adjusted existing S3 operations to utilize the `requestPayer`
parameter where applicable, ensuring compatibility with AWS S3's
Requester Pays feature.
- Ensured that the new functionality is integrated into multipart
uploads and simple requests.

### What does this PR do?

This change allows users to specify whether they are willing to pay for
data transfer costs when accessing objects in Requester Pays buckets,
improving flexibility and compliance with AWS S3's billing model.

This closes #25499

### How did you verify your code works?

I have added a new test file to verify this functionality, and all my
tests pass.
I also tested this against an actual S3 bucket which can only be
accessed if requester pays. I can confirm that it's accessible with
`requestPayer` is `true`, and the default of `false` does not allow
access.

An example bucket is here: s3://hl-mainnet-evm-blocks/0/0/1.rmp.lz4
(my usecase is indexing [hyperliquid block
data](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/hyperevm/raw-hyperevm-block-data)
which is stored in s3, and I want to use bun to index faster)

---------

Co-authored-by: Alistair Smith <hi@alistair.sh>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
2026-01-05 15:04:20 -08:00
Alistair Smith
bf937f7294 sql: filter out undefined values in INSERT helper instead of treating as NULL (#25830)
### What does this PR do?

the `sql()` helper now filters out `undefined` values in INSERT
statements instead of converting them to `NULL`. This allows columns
with `DEFAULT` values to use their defaults when `undefined` is passed,
rather than being overridden with `NULL`.

**Before:** `sql({ foo: undefined, id: "123" })` in INSERT would
generate `(foo, id) VALUES (NULL, "123")`, causing NOT NULL constraint
violations even when the column has a DEFAULT.

**After:** `sql({ foo: undefined, id: "123" })` in INSERT generates
`(id) VALUES ("123")`, omitting the undefined column entirely and
letting the database use the DEFAULT value.

Also fixes a data loss bug in bulk inserts where columns were determined
only from the first item - now all items are checked, so values in later
items aren't silently dropped.

  Fixes #25829

  ### How did you verify your code works?

  - Added regression test for #25829 (NOT NULL column with DEFAULT)
- Added tests for bulk insert with mixed undefined patterns which is the
data loss scenario
2026-01-05 15:03:20 -08:00
Meghan Denny
ce9788716f test: resolve this napi TODO (#25287)
since https://github.com/oven-sh/bun/pull/20772

Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
2026-01-05 12:07:28 -08:00
robobun
4301af9f3e Harden TLS hostname verification (#25727)
## Summary
- Tighten wildcard certificate matching logic for improved security
- Add tests for wildcard hostname verification edge cases

## Test plan
- [x] `bun bd test test/js/web/fetch/fetch.tls.wildcard.test.ts` passes
- [x] Existing TLS tests 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>
2026-01-05 10:21:49 -08:00
Jarred Sumner
8d1de78c7e Deflake stress.test.ts 2026-01-05 17:21:34 +00:00
Darwin ❤️❤️❤️
27ff6aaae0 fix(web): make URLSearchParams.prototype.size configurable (#25762) 2026-01-02 04:57:48 -08:00
robobun
779764332a feat(cli): add --grep as alias for -t/--test-name-pattern in bun test (#25788) 2026-01-02 04:52:47 -08:00
Alex Miller
0141a4fac9 docs: fix shell prompt rendering and remove hardcoded prompts (#25792) 2026-01-02 01:43:19 +00:00
robobun
113830d3cf docs: update npmrc.mdx with all supported .npmrc fields (#25783) 2025-12-31 01:46:37 -08:00
robobun
d9ae93e025 fix(cmake): fix JSON parsing in SetupBuildkite.cmake (#25755)
## Summary

- Fix CMake JSON parsing error when Buildkite API returns commit
messages with newlines

CMake's `file(READ ...)` reads files with literal newlines, which breaks
`string(JSON ...)` when the JSON contains escape sequences like `\n` in
string values (e.g., commit messages from Buildkite API).

Use `file(STRINGS ...)` to read line-by-line, then join with `\n` to
preserve valid JSON escape sequences while avoiding literal newlines.

## Test plan

- Verify CMake configure succeeds when Buildkite build has commit
messages with newlines

🤖 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>
2025-12-30 23:51:29 -08:00
robobun
604c83c8a6 perf(ipc): fix O(n²) JSON scanning for large chunked messages (#25743)
## Summary

- Fix O(n²) performance bug in JSON mode IPC when receiving large
messages that arrive in chunks
- Add `JsonIncomingBuffer` wrapper that tracks newline positions to
avoid re-scanning
- Each byte is now scanned exactly once (on arrival or when preceding
message is consumed)

## Problem

When data arrives in chunks in JSON mode, `decodeIPCMessage` was calling
`indexOfChar(data, '\n')` on the ENTIRE accumulated buffer every time.
For a 10MB message arriving in 160 chunks of 64KB:

- Chunk 1: scan 64KB
- Chunk 2: scan 128KB  
- Chunk 3: scan 192KB
- ...
- Chunk 160: scan 10MB

Total: ~800MB scanned for one 10MB message.

## Solution

Introduced a `JsonIncomingBuffer` struct that:
1. Tracks `newline_pos: ?u32` - position of known upcoming newline (if
any)
2. On `append(bytes)`: Only scans new chunk for `\n` if no position is
cached
3. On `consume(bytes)`: Updates or re-scans as needed after message
processing

This ensures O(n) scanning instead of O(n²).

## Test plan

- [x] `bun run zig:check-all` passes (all platforms compile)
- [x] `bun bd test test/js/bun/spawn/spawn.ipc.test.ts` - 4 tests pass
- [x] `bun bd test test/js/node/child_process/child_process_ipc.test.js`
- 1 test pass
- [x] `bun bd test test/js/bun/spawn/bun-ipc-inherit.test.ts` - 1 test
pass
- [x] `bun bd test test/js/bun/spawn/spawn.ipc.bun-node.test.ts` - 1
test pass
- [x] `bun bd test test/js/bun/spawn/spawn.ipc.node-bun.test.ts` - 1
test pass
- [x] `bun bd test
test/js/node/child_process/child_process_ipc_large_disconnect.test.js` -
1 test pass
- [x] Manual verification with `child-process-send-cb-more.js` (32KB
messages)

🤖 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>
2025-12-29 20:02:18 -08:00
robobun
370b25c086 perf(Buffer.indexOf): use SIMD-optimized search functions (#25745)
## Summary

Optimize `Buffer.indexOf` and `Buffer.includes` by replacing
`std::search` with Highway SIMD-optimized functions:

- **Single-byte search**: `highway_index_of_char` - SIMD-accelerated
character search
- **Multi-byte search**: `highway_memmem` - SIMD-accelerated substring
search

These Highway functions are already used throughout Bun's codebase for
fast string searching (URL parsing, JS lexer, etc.) and provide
significant speedups for pattern matching in large buffers.

### Changes

```cpp
// Before: std::search (scalar)
auto it = std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end());

// After: SIMD-optimized
if (valueLength == 1) {
    size_t result = highway_index_of_char(haystackPtr, haystackLen, valuePtr[0]);
} else {
    void* result = highway_memmem(haystackPtr, haystackLen, valuePtr, valueLength);
}
```

## Test plan

- [x] Debug build compiles successfully
- [x] Basic functionality verified (`indexOf`, `includes` with single
and multi-byte patterns)
- [ ] Existing Buffer 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>
2025-12-29 17:27:12 -08:00
Tommy D. Rossi
538be1399c feat(bundler): expose reactFastRefresh option in Bun.build API (#25731)
Fixes #25716

Adds support for a `reactFastRefresh: boolean` option in the `Bun.build`
JavaScript API, matching the existing `--react-fast-refresh` CLI flag.

```ts
const result = await Bun.build({
    reactFastRefresh: true,
    entrypoints: ["src/App.tsx"],
});
```

When enabled, the bundler adds React Fast Refresh transform code
(`$RefreshReg$`, `$RefreshSig$`) to the output.
2025-12-28 22:07:47 -08:00
robobun
d04b86d34f perf: use jsonStringifyFast for faster JSON serialization (#25733)
## Summary

Apply the same optimization technique from PR #25717 (Response.json) to
other APIs that use JSON.stringify internally:

- **IPC message serialization** (`ipc.zig`) - used for inter-process
communication
- **console.log with %j format** (`ConsoleObject.zig`) - commonly used
for debugging
- **PostgreSQL JSON/JSONB types** (`PostgresRequest.zig`) - database
operations
- **MySQL JSON type** (`MySQLTypes.zig`) - database operations
- **Jest %j/%o format specifiers** (`jest.zig`) - test output formatting
- **Transpiler tsconfig/macros** (`JSTranspiler.zig`) - build
configuration

### Root Cause

When calling `JSONStringify(globalObject, value, 0)`, the space
parameter `0` becomes `jsNumber(0)`, which is NOT `undefined`. This
causes JSC's FastStringifier (SIMD-optimized) to bail out:

```cpp
// In WebKit's JSONObject.cpp FastStringifier::stringify()
if (!space.isUndefined()) {
    logOutcome("space"_s);
    return { };  // Bail out to slow path
}
```

Using `jsonStringifyFast` which passes `jsUndefined()` triggers the fast
path.

### Expected Performance Improvement

Based on PR #25717 results, these changes should provide ~3x speedup for
JSON serialization in the affected APIs.

## Test plan

- [x] Debug build compiles successfully
- [x] Basic functionality verified (IPC, console.log %j, Response.json)
- [x] Existing 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>
2025-12-28 18:01:07 -08:00
robobun
37fc8e99f7 Harden WebSocket client decompression (#25724)
## Summary
- Add maximum decompressed message size limit to WebSocket client
deflate handling
- Add test coverage for decompression limits

## Test plan
- Run `bun test
test/js/web/websocket/websocket-permessage-deflate-edge-cases.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>
2025-12-28 17:58:24 -08:00
robobun
6b5de25d8a feat(shell): add $.trace for analyzing shell commands without execution (#25667)
## Summary

Adds `Bun.$.trace` for tracing shell commands without executing them.

```js
const result = $.trace`cat /tmp/file.txt > output.txt`;
// { operations: [...], cwd: "...", success: true, error: null }
```

## Test plan

- [x] `bun bd test test/js/bun/shell/trace.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: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2025-12-27 17:25:52 -08:00
Alex Miller
7b49654db6 fix(io): Prevent data corruption in Bun.write for files >2GB (#25720)
Closes #8254

Fixes a data corruption bug in `Bun.write()` where files larger than 2GB
would have chunks skipped resulting in corrupted output with missing
data.

The `doWriteLoop` had an issue where it would essentially end up
offsetting twice every 2GB chunks:
-  it first sliced the buffer by `total_written`: 
```remain = remain[@min(this.total_written, remain.len)..]``` 
-  it would then increment `bytes_blob.offset`: 
`this.bytes_blob.offset += @truncate(wrote)` 

but because `sharedView()` already uses the blob offset `slice_ = slice_[this.offset..]` it would end up doubling the offset.

In a local reproduction writing a 16GB file with each 2GB chunk filled with incrementing values `[1, 2, 3, 4, 5, 6, 7, 8]`, the buggy version produced: `[1, 3, 5, 7, …]`, skipping every other chunk.

The fix is to simply remove the redundant manual offset and rely only on `total_written` to track write progress.
2025-12-27 16:58:36 -08:00
SUZUKI Sosuke
603bbd18a0 Enable CHECK_REF_COUNTED_LIFECYCLE in WebKit (#25705)
### What does this PR do?

Enables `CHECK_REF_COUNTED_LIFECYCLE` in WebKit (
https://github.com/oven-sh/WebKit/pull/121 )

See also
a978fae619

#### `CHECK_REF_COUNTED_LIFECYCLE`?

A compile-time macro that enables lifecycle validation for
reference-counted objects in debug builds.

**Definition**
```cpp
  #if ASSERT_ENABLED || ENABLE(SECURITY_ASSERTIONS)
  #define CHECK_REF_COUNTED_LIFECYCLE 1
  #else
  #define CHECK_REF_COUNTED_LIFECYCLE 0
  #endif
```
**Purpose**

  Detects three categories of bugs:
1. Missing adoption - Objects stored in RefPtr without using adoptRef()
2. Ref during destruction - ref() called while destructor is running
(causes dangling pointers)
  3. Thread safety violations - Unsafe ref/deref across threads

**Implementation**

  When enabled, RefCountDebugger adds two tracking flags:
  - m_deletionHasBegun - Set when destructor starts
  - m_adoptionIsRequired - Cleared when adoptRef() is called

These flags are checked on every ref()/deref() call, with assertions
failing on violations.

**Motivation**

  Refactored debug code into a separate RefCountDebugger class to:
  - Improve readability of core refcount logic
- Eliminate duplicate code across RefCounted, ThreadSafeRefCounted, etc.
  - Simplify adding new refcount classes

 **Overhead**

Zero in release builds - the flags and checks are compiled out entirely.

### How did you verify your code works?

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 15:02:11 -08:00
robobun
1d7cb4bbad perf(Response.json): use JSC's FastStringifier by passing undefined for space (#25717)
## Summary

- Fix performance regression where `Response.json()` was 2-3x slower
than `JSON.stringify() + new Response()`
- Root cause: The existing code called `JSC::JSONStringify` with
`indent=0`, which internally passes `jsNumber(0)` as the space
parameter. This bypasses WebKit's FastStringifier optimization.
- Fix: Add a new `jsonStringifyFast` binding that passes `jsUndefined()`
for the space parameter, triggering JSC's FastStringifier
(SIMD-optimized) code path.

## Root Cause Analysis

In WebKit's `JSONObject.cpp`, the `stringify()` function has this logic:

```cpp
static NEVER_INLINE String stringify(JSGlobalObject& globalObject, JSValue value, JSValue replacer, JSValue space)
{
    // ...
    if (String result = FastStringifier<Latin1Character, BufferMode::StaticBuffer>::stringify(globalObject, value, replacer, space, failureReason); !result.isNull())
        return result;
    // Falls back to slow Stringifier...
}
```

And `FastStringifier::stringify()` checks:
```cpp
if (!space.isUndefined()) {
    logOutcome("space"_s);
    return { };  // Bail out to slow path
}
```

So when we called `JSONStringify(globalObject, value, (unsigned)0)`, it
converted to `jsNumber(0)` which is NOT `undefined`, causing
FastStringifier to bail out.

## Performance Results

### Before (3.5x slower than manual approach)
```
Response.json():                2415ms
JSON.stringify() + Response():  689ms
Ratio:                          3.50x
```

### After (parity with manual approach)
```
Response.json():                ~700ms  
JSON.stringify() + Response():  ~700ms
Ratio:                          ~1.09x
```

## Test plan

- [x] Existing `Response.json()` tests pass
(`test/regression/issue/21257.test.ts`)
- [x] Response tests pass (`test/js/web/fetch/response.test.ts`)
- [x] Manual verification that output is correct for various JSON inputs

Fixes #25693

🤖 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: Sosuke Suzuki <sosuke@bun.com>
2025-12-27 15:01:28 -08:00
SUZUKI Sosuke
01de0ecbd9 Add simple benchmark for Array.of (#25711)
**before:**

```
$ bun bench/snippets/array-of.js
cpu: Apple M4 Max
runtime: bun 1.3.5 (arm64-darwin)

benchmark                                  time (avg)             (min … max)       p75       p99      p999
----------------------------------------------------------------------------- -----------------------------
int: Array.of(1,2,3,4,5)                 9.19 ns/iter       (8.1 ns … 108 ns)   9.28 ns  13.63 ns  69.44 ns
int: Array.of(100 elements)             1'055 ns/iter   (94.58 ns … 1'216 ns)  1'108 ns  1'209 ns  1'216 ns
double: Array.of(1.1,2.2,3.3,4.4,5.5)   10.34 ns/iter      (8.81 ns … 102 ns)  10.17 ns  17.19 ns  73.51 ns
double: Array.of(100 elements)          1'073 ns/iter     (124 ns … 1'215 ns)  1'136 ns  1'204 ns  1'215 ns
object: Array.of(obj x5)                19.19 ns/iter     (16.58 ns … 122 ns)  19.06 ns   77.6 ns  85.75 ns
object: Array.of(100 elements)          1'340 ns/iter     (294 ns … 1'568 ns)  1'465 ns  1'537 ns  1'568 ns
```

**after:**

```
$ ./build/release/bun bench/snippets/array-of.js
cpu: Apple M4 Max
runtime: bun 1.3.6 (arm64-darwin)

benchmark                                  time (avg)             (min … max)       p75       p99      p999
----------------------------------------------------------------------------- -----------------------------
int: Array.of(1,2,3,4,5)                 2.68 ns/iter    (2.14 ns … 92.96 ns)   2.52 ns   3.95 ns  59.73 ns
int: Array.of(100 elements)             23.69 ns/iter     (18.88 ns … 155 ns)  20.91 ns  83.82 ns  96.66 ns
double: Array.of(1.1,2.2,3.3,4.4,5.5)    3.62 ns/iter    (2.97 ns … 75.44 ns)   3.46 ns   5.05 ns  65.82 ns
double: Array.of(100 elements)          26.96 ns/iter     (20.14 ns … 156 ns)  24.45 ns  87.75 ns  96.88 ns
object: Array.of(obj x5)                11.82 ns/iter     (9.6 ns … 87.38 ns)  11.23 ns  68.99 ns  77.09 ns
object: Array.of(100 elements)            236 ns/iter       (206 ns … 420 ns)    273 ns    325 ns    386 ns
```
2025-12-27 00:05:57 -08:00
Oleksandr Herasymov
d3a5f2eef2 perf: speed up Bun.hash.crc32 by switching to zlib CRC32 (#25692)
## What does this PR do?
Switch `Bun.hash.crc32` to use `zlib`'s CRC32 implementation. Bun
already links `zlib`, which provides highly optimized,
hardware-accelerated CRC32. Because `zlib.crc32` takes a 32-bit length,
chunk large inputs to avoid truncation/regressions on buffers >4GB.

Note: This was tried before (PR #12164 by Jarred), which switched CRC32
to zlib for speed. This proposal keeps that approach and adds explicit
chunking to avoid the >4GB length pitfall.

**Problem:** `Bun.hash.crc32` is a significant outlier in
microbenchmarks compared to other hash functions (about 21x slower than
`zlib.crc32` in a 1MB test on M1).

**Root cause:** `Bun.hash.crc32` uses Zig's `std.hash.Crc32`
implementation, which is software-only and does not leverage hardware
acceleration (e.g., `PCLMULQDQ` on x86 or `CRC32` instructions on ARM).

**Implementation:**
https://github.com/oven-sh/bun/blob/main/src/bun.js/api/HashObject.zig

```zig
pub const crc32 = hashWrap(struct {
    pub fn hash(seed: u32, bytes: []const u8) u32 {
        // zlib takes a 32-bit length, so chunk large inputs to avoid truncation.
        var crc: u64 = seed;
        var offset: usize = 0;
        while (offset < bytes.len) {
            const remaining = bytes.len - offset;
            const max_len: usize = std.math.maxInt(u32);
            const chunk_len: u32 = if (remaining > max_len) @intCast(max_len) else @intCast(remaining);
            crc = bun.zlib.crc32(crc, bytes.ptr + offset, chunk_len);
            offset += chunk_len;
        }
        return @intCast(crc);
    }
});
```

### How did you verify your code works?
**Benchmark (1MB payload):**
- **Before:** Bun 1.3.5 `Bun.hash.crc32` = 2,644,444 ns/op vs
`zlib.crc32` = 124,324 ns/op (~21x slower)
- **After (local bun-debug):** `Bun.hash.crc32` = 360,591 ns/op vs
`zlib.crc32` = 359,069 ns/op (~1.0x), results match

## Test environment
- **Machine:** MacBook Pro 13" (M1, 2020)
- **OS:** macOS 15.7.3
- **Baseline Bun:** 1.3.5
- **After Bun:** local `bun-debug` (build/debug)
2025-12-26 23:41:10 -08:00
robobun
b51e993bc2 fix: reject null bytes in spawn args, env, and shell arguments (#25698)
## Summary

- Reject null bytes in command-line arguments passed to `Bun.spawn` and
`Bun.spawnSync`
- Reject null bytes in environment variable keys and values
- Reject null bytes in shell (`$`) template literal arguments

This prevents null byte injection attacks (CWE-158) where null bytes in
strings could cause unintended truncation when passed to the OS,
potentially allowing attackers to bypass file extension validation or
create files with unexpected names.

## Test plan

- [x] Added tests in `test/js/bun/spawn/null-byte-injection.test.ts`
- [x] Tests pass with debug build: `bun bd test
test/js/bun/spawn/null-byte-injection.test.ts`
- [x] Tests fail with system Bun (confirming the fix works)

🤖 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>
2025-12-26 23:39:37 -08:00
SUZUKI Sosuke
92f105dbe1 Add microbench for String#includes (#25699)
note: This is due to constant folding by the JIT. For `String#includes`
on dynamic strings, the performance improvement is not this significant.

**before:**
```
$ bun ./bench/snippets/string-includes.mjs
cpu: Apple M4 Max
runtime: bun 1.3.5 (arm64-darwin)

benchmark                                  time (avg)             (min … max)       p75       p99      p999
----------------------------------------------------------------------------- -----------------------------
String.includes - short, hit (middle)   82.24 ns/iter     (14.95 ns … 881 ns)  84.98 ns    470 ns    791 ns
String.includes - short, hit (start)    37.44 ns/iter      (8.46 ns … 774 ns)  26.08 ns    379 ns    598 ns
String.includes - short, hit (end)      97.27 ns/iter     (16.93 ns … 823 ns)    107 ns    537 ns    801 ns
String.includes - short, miss             102 ns/iter       (0 ps … 1'598 µs)     42 ns    125 ns    167 ns
String.includes - long, hit (middle)    16.01 ns/iter     (14.34 ns … 115 ns)  16.03 ns   20.1 ns   53.1 ns
String.includes - long, miss              945 ns/iter       (935 ns … 972 ns)    948 ns    960 ns    972 ns
String.includes - with position          9.83 ns/iter    (8.44 ns … 58.45 ns)   9.83 ns  12.31 ns  15.69 ns
```

**after:**
```
$ ./build/release/bun bench/snippets/string-includes.mjs
cpu: Apple M4 Max
runtime: bun 1.3.6 (arm64-darwin)

benchmark                                  time (avg)             (min … max)       p75       p99      p999
----------------------------------------------------------------------------- -----------------------------
String.includes - short, hit (middle)     243 ps/iter     (203 ps … 10.13 ns)    244 ps    325 ps    509 ps !
String.includes - short, hit (start)      374 ps/iter     (244 ps … 19.78 ns)    387 ps    488 ps    691 ps
String.includes - short, hit (end)        708 ps/iter     (407 ps … 18.03 ns)    651 ps   2.62 ns   2.69 ns
String.includes - short, miss            1.49 ns/iter     (407 ps … 27.93 ns)   2.87 ns   3.09 ns   3.78 ns
String.includes - long, hit (middle)     3.28 ns/iter      (3.05 ns … 118 ns)   3.15 ns   8.75 ns  16.07 ns
String.includes - long, miss             7.28 ns/iter      (3.44 ns … 698 ns)   9.34 ns  42.85 ns    240 ns
String.includes - with position          7.97 ns/iter       (3.7 ns … 602 ns)   9.68 ns  52.19 ns    286 ns
```
2025-12-26 21:49:00 -08:00
Dylan Conway
d0bd1b121f Fix DCE producing invalid syntax for empty objects in spreads (#25710)
## Summary
- Fixes dead code elimination producing invalid syntax like `{ ...a, x:
}` when simplifying empty objects in spread contexts
- The issue was that `simplifyUnusedExpr` and `joinAllWithCommaCallback`
could return `E.Missing` instead of `null` to indicate "no side effects"
- Added checks to return `null` when the result is `E.Missing`

Fixes #25609

## Test plan
- [x] Added regression test that fails on v1.3.5 and passes with fix
- [x] `bun bd test test/regression/issue/25609.test.ts` passes

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-26 21:48:19 -08:00
robobun
81b4a40fbd [publish images] Remove sccache, use ccache only (#25682)
## Summary
- Remove sccache support entirely, use ccache only
- Missing ccache no longer fails the build (just skips caching)
- Remove S3 distributed cache support

## Changes
- Remove `cmake/tools/SetupSccache.cmake` and S3 distributed cache
support
- Simplify `CMakeLists.txt` to only use ccache
- Update `SetupCcache.cmake` to not fail when ccache is missing
- Replace sccache with ccache in bootstrap scripts (sh, ps1)
- Update `.buildkite/Dockerfile` to install ccache instead of sccache
- Update `flake.nix` and `shell.nix` to use ccache
- Update documentation (CONTRIBUTING.md, contributing.mdx,
building-windows.mdx)
- Remove `scripts/build-cache/` directory (was only for sccache S3
access)

## Test plan
- [x] Build completes 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>
2025-12-26 20:24:27 -08:00
Nico Cevallos
5715b54614 add test for dependency order when a package's name is larger than 8 characters + fix (#25697)
### What does this PR do?

- Add test that is broken before the changes in the code and fix
previous test making script in dependency takes a bit of time to be
executed. Without the `setTimeout` in the tests, due race conditions it
always success. I tried adding a test combining both tests, with
dependencies `dep0` and `larger-than-8-char`, but if the timeout is the
same it success.
- Fix for the use case added, by using the correct buffer for
`Dependency.name` otherwise it gets garbage when package name is larger
than 8 characters. This should fix #12203

### How did you verify your code works?

Undo the changes in the code to verify the new test fails and check it
again after adding the changes in the code.
2025-12-25 23:49:23 -08:00
Jarred Sumner
28fd495b39 Deflake test/js/bun/resolve/load-same-js-file-a-lot.test.ts 2025-12-25 17:43:43 -08:00
SUZUKI Sosuke
699d8b1e1c Upgrade WebKit Dec 24, 2025 (#25684)
- WTFMove → WTF::move / std::move: Replaced WTFMove() macro with
WTF::move() function for WTF types, std::move() for std types
- SortedArrayMap removed: Replaced with if-else chains in
EventFactory.cpp, JSCryptoKeyUsage.cpp
- Wasm::Memory::create signature changed: Removed VM parameter
- URLPattern allocation: Changed from WTF_MAKE_ISO_ALLOCATED to
WTF_MAKE_TZONE_ALLOCATED

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-12-25 14:00:58 -08:00
robobun
2247c3859a chore: convert .cursor/rules to .claude/skills (#25683)
## Summary
- Migrate Cursor rules to Claude Code skills format
- Add 4 new skills for development guidance:
  - `writing-dev-server-tests`: HMR/dev server test guidance
  - `implementing-jsc-classes-cpp`: C++ JSC class implementation  
  - `implementing-jsc-classes-zig`: Zig JSC bindings generator
  - `writing-bundler-tests`: bundler test guidance with itBundled
- Remove all `.cursor/rules/` files

## Test plan
- [x] Skills follow Claude Code skill authoring guidelines
- [x] Each skill has proper YAML frontmatter with name and description
- [x] Skills are concise and actionable

🤖 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>
2025-12-24 23:37:26 -08:00
Jarred Sumner
08e03814e5 [publish images] Fix CI, remove broken freebsd image step 2025-12-24 20:02:56 -08:00
Jarred Sumner
0dd4f025b6 [publish images] (+ add Object.hasOwn benchmark) 2025-12-24 19:55:44 -08:00
Jarred Sumner
79067037ff Add Promise.race microbenchmark 2025-12-23 22:53:24 -08:00
Aiden Cline
822d75a380 fix(@types/bun): add missing autoloadTsconfig and autoloadPackageJson types (#25501)
### What does this PR do?

Adds missing types, fixes typo

### How did you verify your code works?

Add missing types from: 
https://github.com/oven-sh/bun/pull/25340/changes

---------

Co-authored-by: Alistair Smith <hi@alistair.sh>
2025-12-24 06:47:07 +00:00
SUZUKI Sosuke
bffccf3d5f Upgrade WebKit 2025/12/07 (#25429)
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: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
2025-12-23 22:24:18 -08:00
robobun
0300150324 docs: fix incorrect [env] section documentation in bunfig.toml (#25634)
## Summary
- Fixed documentation that incorrectly claimed you could use `[env]` as
a TOML section to set environment variables directly
- The `env` option in bunfig.toml only controls whether automatic `.env`
file loading is disabled (via `env = false`)
- Updated to show the correct approaches: using preload scripts or
`.env` files with `--env-file`

## Test plan
- Documentation-only change, no code 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>
2025-12-23 15:31:12 -08:00
robobun
34a1e2adad fix: use LLVM unstable repo for Debian Trixie (13) (#25657)
## Summary

- Fix LLVM installation on Debian Trixie (13) by using the unstable
repository from apt.llvm.org

The `llvm.sh` script doesn't automatically detect that Debian Trixie
needs to use the unstable repository. This is because trixie's `VERSION`
is `13 (trixie)` rather than `testing`, and apt.llvm.org doesn't have a
dedicated trixie repository.

Without this fix, the LLVM installation falls back to Debian's main
repository packages, which don't include `libclang-rt-19-dev` (the
compiler-rt sanitizer libraries) by default. This causes builds with
ASan (AddressSanitizer) to fail with:

```
ld.lld: error: cannot open /usr/lib/llvm-19/lib/clang/19/lib/x86_64-pc-linux-gnu/libclang_rt.asan.a: No such file or directory
```

This was breaking the [Daily Docker
Build](https://github.com/oven-sh/bun-development-docker-image/actions/runs/20437290601)
in the bun-development-docker-image repo.

## Test plan

- [ ] Wait for the PR CI to pass
- [ ] After merging, the next Daily Docker Build should succeed

🤖 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>
2025-12-22 23:10:15 -08:00
robobun
8484e1b827 perf: shrink ConcurrentTask from 24 bytes to 16 bytes (#25636) 2025-12-22 12:07:24 -08:00
robobun
3898ed5e3f perf: pack boolean flags and reorder fields to reduce struct padding (#25627) 2025-12-21 17:12:42 -08:00
Jarred Sumner
c08ffadf56 perf(linux): add memfd optimizations and typed flags (#25597)
## Summary

- Add `MemfdFlags` enum to replace raw integer flags for `memfd_create`,
providing semantic clarity for different use cases (`executable`,
`non_executable`, `cross_process`)
- Add support for `MFD_EXEC` and `MFD_NOEXEC_SEAL` flags (Linux 6.3+)
with automatic fallback to older kernel flags when `EINVAL` is returned
- Use memfd + `/proc/self/fd/{fd}` path for loading embedded `.node`
files in standalone builds, avoiding disk writes entirely on Linux

## Test plan

- [ ] Verify standalone builds with embedded `.node` files work on Linux
- [ ] Verify fallback works on older kernels (pre-6.3)
- [ ] Verify subprocess stdio memfd still works correctly

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-19 23:18:21 -08:00
Dylan Conway
fa983247b2 fix(create): crash when running postinstall task with --no-install (#25616)
## Summary
- Fix segmentation fault in `bun create` when using `--no-install` with
a template that has a `bun-create.postinstall` task starting with "bun "
- The bug was caused by unconditionally slicing `argv[2..]` which
created an empty array when `npm_client` was null
- Added check for `npm_client != null` before slicing

## Reproduction
```bash
# Create template with bun-create.postinstall
mkdir -p ~/.bun-create/test-template
echo '{"name":"test","bun-create":{"postinstall":"bun install"}}' > ~/.bun-create/test-template/package.json

# This would crash before the fix
bun create test-template /tmp/my-app --no-install
```

## Test plan
- [x] Verified the reproduction case crashes before the fix
- [x] Verified the reproduction case works after the fix

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-19 23:17:51 -08:00
Dylan Conway
99b0a16c33 fix: prevent out-of-bounds access in NO_PROXY parsing (#25617)
## Summary
- Fix out-of-bounds access when parsing `NO_PROXY` environment variable
with empty entries
- Empty entries (e.g., `"localhost, , example.com"`) would cause a panic
when checking if the host starts with a dot
- Skip empty entries after trimming whitespace

fixes BUN-110G
fixes BUN-128V

## Test plan
- [x] Verify `NO_PROXY="localhost, , example.com"` no longer crashes

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-19 23:17:29 -08:00
Dylan Conway
085e25d5d1 fix: protect StringOrBuffer from GC in async operations (#25594)
## Summary

- Fix use-after-free crash in async zstd compression, scrypt, and
JSTranspiler operations
- When `StringOrBuffer.fromJSMaybeAsync` is called with `is_async=true`,
the buffer's JSValue is now protected from garbage collection
- Previously, the buffer could be GC'd while a worker thread was still
accessing it, causing segfaults in zstd's `HIST_count_simple` and
similar functions

Fixes BUN-167Z

## Changes

- `fromJSMaybeAsync`: Call `protect()` on buffer when `is_async=true`
- `fromJSWithEncodingMaybeAsync`: Same protection for the early return
path
- `Scrypt`: Fix cleanup to use `deinitAndUnprotect()` for async path,
add missing `deinit()` in sync path
- `JSTranspiler`: Use new protection mechanism instead of manual
`protect()`/`unprotect()` calls
- Simplify `createOnJSThread` signatures to not return errors (OOM is
handled internally)
- Update all callers to use renamed/simplified APIs

## Test plan

- [x] Code review of all callsites to verify correct protect/unprotect
pairing
- [ ] Run existing zstd tests
- [ ] Run existing scrypt tests
- [ ] Run existing transpiler tests

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-19 17:30:26 -08:00
Jarred Sumner
ce5c336ea5 Revert "fix: memory leaks in IPC message handling (#25602)"
This reverts commit 05b12e0ed0.

The tests did not fail with system version of Bun.
2025-12-19 17:28:54 -08:00
robobun
05b12e0ed0 fix: memory leaks in IPC message handling (#25602)
## Summary

- Add periodic memory reclamation for IPC buffers after processing
messages
- Fix missing `deref()` on `bun.String` created from `cmd` property in
`handleIPCMessage`
- Add `reclaimMemory()` function to shrink incoming buffer and send
queue when they exceed 2MB capacity
- Track message count to trigger memory reclamation every 256 messages

The incoming `ByteList` buffer and send queue `ArrayList` would grow but
never shrink, causing memory accumulation during sustained IPC
messaging.

## Test plan

- [x] Added regression tests in
`test/js/bun/spawn/spawn-ipc-memory.test.ts`
- [x] Existing IPC tests pass (`spawn.ipc.test.ts`)
- [x] Existing cluster 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>
2025-12-19 17:27:09 -08:00
Angus Comrie
d9459f8540 Fix postgres empty check when handling arrays (#25607)
### What does this PR do?
Closes #25505. This adjusts the byte length check in `DataCell:
fromBytes` to 12 bytes instead of 16, as zero-dimensional arrays will
have a shorter preamble.

### How did you verify your code works?
Test suite passes, and I've added a new test that fails in the main
branch but passes with this change. The issue only seems to crop up when
a connection is _reused_, which is curious.
2025-12-19 14:49:12 -08:00
Jarred Sumner
e79b512a9d Propagate debugger CLI config in single-file executables (#25600) 2025-12-19 09:49:02 -08:00
robobun
9902039b1f fix: memory leaks in error-handling code for Brotli, Zstd, and Zlib compression state machines (#25592)
## Summary

Fix several memory leaks in the compression libraries:

- **NativeBrotli/NativeZstd reset()** - Each call to `reset()` allocated
a new encoder/decoder without freeing the previous one
- **NativeBrotli/NativeZstd init() error paths** - If `setParams()`
failed after `stream.init()` succeeded, the instance was leaked
- **NativeZstd init()** - If `setPledgedSrcSize()` failed after context
creation, the context was leaked
- **ZlibCompressorArrayList** - After `deflateInit2_()` succeeded, if
`ensureTotalCapacityPrecise()` failed with OOM, zlib internal state was
never freed
- **NativeBrotli close()** - Now sets state to null to prevent potential
double-free (defensive)
- **LibdeflateState** - Added `deinit()` for API consistency

## Test plan

- [x] Added regression test that calls `reset()` 100k times and measures
memory growth
- [x] Test shows memory growth dropped from ~600MB to ~10MB for Brotli
- [x] Verified no double-frees by tracing code paths
- [x] Existing zlib tests pass (except pre-existing timeout in debug
build)

Before fix (system bun 1.3.3):
```
Memory growth after 100000 reset() calls: 624.38 MB  (BrotliCompress)
Memory growth after 100000 reset() calls: 540.63 MB  (BrotliDecompress)
```

After fix:
```
Memory growth after 100000 reset() calls: 11.84 MB   (BrotliCompress)
Memory growth after 100000 reset() calls: 0.16 MB    (BrotliDecompress)
```

🤖 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>
2025-12-18 21:42:14 -08:00
Dylan Conway
f3fd7506ef fix(windows): handle UV_UNKNOWN and UV_EAI_* error codes in libuv errno mapping (#25596)
## Summary
- Add missing `UV_UNKNOWN` and `UV_EAI_*` error code mappings to the
`errno()` function in `ReturnCode`
- Fixes panic "integer does not fit in destination type" on Windows when
libuv returns unmapped error codes
- Speculative fix for BUN-131E

## Root Cause
The `errno()` function was missing mappings for `UV_UNKNOWN` (-4094) and
all `UV_EAI_*` address info errors (-3000 to -3014). When libuv returned
these codes, the switch fell through to `else => null`, and the caller
at `sys_uv.zig:317` assumed success and tried to cast the negative
return code to `usize`, causing a panic.

This was triggered in `readFileWithOptions` -> `preadv` when:
- Memory-mapped file operations encounter exceptions (file
modified/truncated by another process, network drive issues)
- Windows returns error codes that libuv cannot map to standard errno
values

## Crash Report
```
Bun v1.3.5 (1e86ceb) on windows x86_64baseline []
panic: integer does not fit in destination type
sys_uv.zig:294: preadv
node_fs.zig:5039: readFileWithOptions
```

## Test plan
- [ ] This fix prevents a panic, converting it to a proper error.
Testing would require triggering `UV_UNKNOWN` from libuv, which is
difficult to do reliably (requires memory-mapped file exceptions or
unusual Windows errors).

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

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 21:41:41 -08:00
robobun
c21c51a0ff test(security-scanner): add TTY prompt tests using Bun.Terminal (#25587)
Co-authored-by: Alistair Smith <hi@alistair.sh>
2025-12-19 05:21:44 +00:00
robobun
0bbf6c74b5 test: add describe blocks for grouping in bun-types.test.ts (#25598)
Co-authored-by: Alistair Smith <hi@alistair.sh>
2025-12-19 04:53:18 +00:00
Dylan Conway
57cbbc09e4 fix: correct off-by-one bounds checks in bundler and package installer (#25582)
## Summary

- Fix two off-by-one bounds check errors that used `>` instead of `>=`
- Both bugs could cause undefined behavior (array out-of-bounds access)
when an index equals the array length

## The Bugs

### 1. `src/install/postinstall_optimizer.zig:62`

```zig
// Before (buggy):
if (resolution > metas.len) continue;
const meta: *const Meta = &metas[resolution];  // Out-of-bounds when resolution == metas.len

// After (fixed):
if (resolution >= metas.len) continue;
```

### 2. `src/bundler/linker_context/doStep5.zig:10`

```zig
// Before (buggy):
if (id > c.graph.meta.len) return;
const resolved_exports = &c.graph.meta.items(.resolved_exports)[id];  // Out-of-bounds when id == c.graph.meta.len

// After (fixed):
if (id >= c.graph.meta.len) return;
```

## Why These Are Bugs

Valid array indices are `0` to `len - 1`. When `index == len`:
- `index > len` evaluates to `false` → check passes
- `array[index]` accesses `array[len]` → out-of-bounds / undefined
behavior

## Codebase Patterns

The rest of the codebase correctly uses `>=` for these checks:
- `lockfile.zig:484`: `if (old_resolution >= old.packages.len)
continue;`
- `lockfile.zig:522`: `if (old_resolution >= old.packages.len)
continue;`
- `LinkerContext.zig:389`: `if (source_index >= import_records_list.len)
continue;`
- `LinkerContext.zig:1667`: `if (source_index >= c.graph.ast.len) {`

## Test plan

- [x] Verified fix aligns with existing codebase patterns
- [ ] CI passes

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 18:04:28 -08:00
Jarred Sumner
7f589ffb4b Disable coderabbit enrichment 2025-12-18 18:03:23 -08:00
Francis F
cea59d7fc0 docs(sqlite): fix .run() return value documentation (#25060)
Co-authored-by: Alistair Smith <hi@alistair.sh>
2025-12-18 20:44:35 +00:00
Jarred Sumner
4ea1454e4a Delete unused workflow 2025-12-18 12:04:28 -08:00
Dylan Conway
8941a363c3 fix: dupe ca string in .npmrc to prevent use-after-free (#25563)
## Summary

- Fix use-after-free bug when parsing `ca` option from `.npmrc`
- The `ca` string was being stored directly from the parser's arena
without duplication
- Since the parser arena is freed at the end of `loadNpmrc`, this
created a dangling pointer

## The Bug

In `src/ini.zig`, the `ca` string wasn't being duplicated like all other
string properties:

```zig
// Lines 983-986 explicitly warn about this:
// Need to be very, very careful here with strings.
// They are allocated in the Parser's arena, which of course gets
// deinitialized at the end of the scope.
// We need to dupe all strings

// Line 981: Parser arena is freed here
defer parser.deinit();

// Line 1016-1020: THE BUG - string not duped!
if (out.asProperty("ca")) |query| {
    if (query.expr.asUtf8StringLiteral()) |str| {
        install.ca = .{
            .str = str,  // ← Dangling pointer after parser.deinit()!
        };
```

All other string properties in the same function correctly duplicate:
- `registry` (line 996): `try allocator.dupe(u8, str)`
- `cache` (line 1002): `try allocator.dupe(u8, str)`
- `cafile` (line 1037): `asStringCloned(allocator)`
- `ca` array items (line 1026): `asStringCloned(allocator)`

## User Impact

When a user has `ca=<certificate>` in their `.npmrc` file:
1. The certificate string is parsed and stored
2. The parser arena is freed
3. `install.ca.str` becomes a dangling pointer
4. Later TLS/SSL operations access freed memory
5. Could cause crashes, undefined behavior, or security issues

## Test plan

- Code inspection confirms this matches the pattern used for all other
string properties
- The fix adds `try allocator.dupe(u8, str)` to match `cache`,
`registry`, etc.

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-17 19:56:25 -08:00
Dylan Conway
722ac3aa5a fix: check correct variable in subprocess stdin cleanup (#25562)
## Summary

- Fix typo in `onProcessExit` where `existing_stdin_value.isCell()` was
checked instead of `existing_value.isCell()`
- Since `existing_stdin_value` is always `.zero` at that point, the
condition was always false, making the inner block dead code

## The Bug

In `src/bun.js/api/bun/subprocess.zig:593`:

```zig
var existing_stdin_value = jsc.JSValue.zero;  // Line 590 - always .zero
if (this_jsvalue != .zero) {
    if (jsc.Codegen.JSSubprocess.stdinGetCached(this_jsvalue)) |existing_value| {
        if (existing_stdin_value.isCell()) {  // BUG! Should be existing_value
            // This block was DEAD CODE - never executed
```

Compare with the correct pattern used elsewhere:
```zig
// shell/subproc.zig:251-252 (CORRECT)
if (jsc.Codegen.JSSubprocess.stdinGetCached(subprocess.this_jsvalue)) |existing_value| {
    jsc.WebCore.FileSink.JSSink.setDestroyCallback(existing_value, 0);  // Uses existing_value
}
```

## Impact

The dead code prevented:
- Recovery of stdin from cached JS value when `weak_file_sink_stdin_ptr`
is null
- Proper cleanup via `onAttachedProcessExit` on the FileSink  
- `setDestroyCallback` cleanup in `onProcessExit`

Note: The user-visible impact was mitigated by redundant cleanup paths
in `Writable.zig` that also call `setDestroyCallback`.

## Test plan

- Code inspection confirms this is a straightforward typo fix
- Existing subprocess tests continue to pass

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-17 18:34:58 -08:00
Dylan Conway
a333d02f84 fix: correct inverted buffer allocation logic in Postgres array parsing (#25564)
## Summary

- Fix inverted buffer allocation logic when parsing strings in Postgres
arrays
- Strings larger than 16KB were incorrectly using the stack buffer
instead of dynamically allocating
- This caused spurious `InvalidByteSequence` errors for valid data

## The Bug

In `src/sql/postgres/DataCell.zig`, the condition for when to use
dynamic allocation was inverted:

```zig
// BEFORE (buggy):
const needs_dynamic_buffer = str_bytes.len < stack_buffer.len;  // TRUE when SMALL

// AFTER (fixed):
const needs_dynamic_buffer = str_bytes.len > stack_buffer.len;  // TRUE when LARGE
```

## What happened with large strings (>16KB):

1. `needs_dynamic_buffer` = false (e.g., 20000 < 16384 is false)
2. Uses `stack_buffer[0..]` which is only 16KB
3. `unescapePostgresString` hits bounds check and returns
`BufferTooSmall`
4. Error converted to `InvalidByteSequence`
5. User gets error even though data is valid

## User Impact

Users with Postgres arrays containing JSON or string elements larger
than 16KB would get spurious InvalidByteSequence errors even though
their data was perfectly valid.

## Test plan

- Code inspection confirms the logic was inverted
- The fix aligns with the intended behavior: use stack buffer for small
strings, dynamic allocation for large strings

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-17 18:34:17 -08:00
Dylan Conway
c1acb0b9a4 fix(shell): prevent double-close of fd when using &> redirect with builtins (#25568)
## Summary

- Fix double-close of file descriptor when using `&>` redirect with
shell builtin commands
- Add `dupeRef()` helper for cleaner reference counting semantics
- Add tests for `&>` and `&>>` redirects with builtins

## Test plan

- [x] Added tests in `test/js/bun/shell/file-io.test.ts` that reproduce
the bug
- [x] All file-io tests pass

## The Bug

When using `&>` to redirect both stdout and stderr to the same file with
a shell builtin command (e.g., `pwd &> file.txt`), the code was creating
two separate `IOWriter` instances that shared the same file descriptor.
When both `IOWriter`s were destroyed, they both tried to close the same
fd, causing an `EBADF` (bad file descriptor) error.

```javascript
import { $ } from "bun";
await $`pwd &> output.txt`; // Would crash with EBADF
```

## The Fix

1. Share a single `IOWriter` between stdout and stderr when both are
redirected to the same file, with proper reference counting
2. Rename `refSelf` to `dupeRef` for clarity across `IOReader`,
`IOWriter`, `CowFd`, and add it to `Blob` for consistency
3. Fix the `Body.Value` blob case to also properly reference count when
the same blob is assigned to multiple outputs

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

---------

Co-authored-by: Claude Latest model <noreply@anthropic.com>
2025-12-17 18:33:53 -08:00
Jarred Sumner
ffd2240c31 Bump 2025-12-17 11:42:54 -08:00
789 changed files with 49063 additions and 21530 deletions

View File

@@ -26,7 +26,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
wget curl git python3 python3-pip ninja-build \
software-properties-common apt-transport-https \
ca-certificates gnupg lsb-release unzip \
libxml2-dev ruby ruby-dev bison gawk perl make golang \
libxml2-dev ruby ruby-dev bison gawk perl make golang ccache \
&& add-apt-repository ppa:ubuntu-toolchain-r/test \
&& apt-get update \
&& apt-get install -y gcc-13 g++-13 libgcc-13-dev libstdc++-13-dev \
@@ -35,7 +35,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
&& wget https://apt.llvm.org/llvm.sh \
&& chmod +x llvm.sh \
&& ./llvm.sh ${LLVM_VERSION} all \
&& rm llvm.sh
&& rm llvm.sh \
&& rm -rf /var/lib/apt/lists/*
RUN --mount=type=tmpfs,target=/tmp \
@@ -48,14 +49,6 @@ RUN --mount=type=tmpfs,target=/tmp \
wget -O /tmp/cmake.sh "$cmake_url" && \
sh /tmp/cmake.sh --skip-license --prefix=/usr
RUN --mount=type=tmpfs,target=/tmp \
sccache_version="0.12.0" && \
arch=$(uname -m) && \
sccache_url="https://github.com/mozilla/sccache/releases/download/v${sccache_version}/sccache-v${sccache_version}-${arch}-unknown-linux-musl.tar.gz" && \
wget -O /tmp/sccache.tar.gz "$sccache_url" && \
tar -xzf /tmp/sccache.tar.gz -C /tmp && \
install -m755 /tmp/sccache-v${sccache_version}-${arch}-unknown-linux-musl/sccache /usr/local/bin
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 130 \
--slave /usr/bin/g++ g++ /usr/bin/g++-13 \
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-13 \
@@ -134,9 +127,7 @@ RUN ARCH=$(if [ "$TARGETARCH" = "arm64" ]; then echo "arm64"; else echo "amd64";
RUN mkdir -p /var/cache/buildkite-agent /var/log/buildkite-agent /var/run/buildkite-agent /etc/buildkite-agent /var/lib/buildkite-agent/cache/bun
# The following is necessary to configure buildkite to use a stable
# checkout directory. sccache hashes absolute paths into its cache keys,
# so if buildkite uses a different checkout path each time (which it does
# by default), sccache will be useless.
# checkout directory for ccache to be effective.
RUN mkdir -p -m 755 /var/lib/buildkite-agent/hooks && \
cat <<'EOF' > /var/lib/buildkite-agent/hooks/environment
#!/bin/sh

View File

@@ -31,7 +31,7 @@ import {
} from "../scripts/utils.mjs";
/**
* @typedef {"linux" | "darwin" | "windows" | "freebsd"} Os
* @typedef {"linux" | "darwin" | "windows"} Os
* @typedef {"aarch64" | "x64"} Arch
* @typedef {"musl"} Abi
* @typedef {"debian" | "ubuntu" | "alpine" | "amazonlinux"} Distro
@@ -114,7 +114,6 @@ const buildPlatforms = [
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.22" },
{ os: "windows", arch: "x64", release: "2019" },
{ os: "windows", arch: "x64", baseline: true, release: "2019" },
{ os: "freebsd", arch: "x64", release: "14.3" },
];
/**
@@ -572,6 +571,7 @@ function getTestBunStep(platform, options, testOptions = {}) {
if (buildId) {
args.push(`--build-id=${buildId}`);
}
if (testFiles) {
args.push(...testFiles.map(testFile => `--include=${testFile}`));
}
@@ -588,7 +588,7 @@ function getTestBunStep(platform, options, testOptions = {}) {
agents: getTestAgent(platform, options),
retry: getRetry(),
cancel_on_build_failing: isMergeQueue(),
parallelism: os === "darwin" ? 2 : 10,
parallelism: os === "darwin" ? 2 : 20,
timeout_in_minutes: profile === "asan" || os === "windows" ? 45 : 30,
env: {
ASAN_OPTIONS: "allow_user_segv_handler=1:disable_coredump=0:detect_leaks=0",
@@ -657,7 +657,7 @@ function getReleaseStep(buildPlatforms, options) {
agents: {
queue: "test-darwin",
},
depends_on: buildPlatforms.filter(p => p.os !== "freebsd").map(platform => `${getTargetKey(platform)}-build-bun`),
depends_on: buildPlatforms.map(platform => `${getTargetKey(platform)}-build-bun`),
env: {
CANARY: revision,
},
@@ -1108,9 +1108,6 @@ async function getPipeline(options = {}) {
? buildPlatforms
: buildPlatforms.filter(({ profile }) => profile !== "asan");
// run build-image but no build-bun yet
relevantBuildPlatforms = relevantBuildPlatforms.filter(({ os }) => os !== "freebsd");
steps.push(
...relevantBuildPlatforms.map(target => {
const imageKey = getImageKey(target);

View File

@@ -6,8 +6,7 @@ To do that:
- git fetch upstream
- git merge upstream main
- Fix the merge conflicts
- cd ../../ (back to bun)
- make jsc-build (this will take about 7 minutes)
- bun build.ts debug
- While it compiles, in another task review the JSC commits between the last version of Webkit and the new version. Write up a summary of the webkit changes in a file called "webkit-changes.md"
- bun run build:local (build a build of Bun with the new Webkit, make sure it compiles)
- After making sure it compiles, run some code to make sure things work. something like ./build/debug-local/bun-debug --print '42' should be all you need
@@ -21,3 +20,7 @@ To do that:
- commit + push (without adding the webkit-changes.md file)
- create PR titled "Upgrade Webkit to the <commit-sha>", paste your webkit-changes.md into the PR description
- delete the webkit-changes.md file
Things to check for a successful upgrade:
- Did JSType in vendor/WebKit/Source/JavaScriptCore have any recent changes? Does the enum values align with whats present in src/bun.js/bindings/JSType.zig?
- Were there any changes to the webcore code generator? If there are C++ compilation errors, check for differences in some of the generated code in like vendor/WebKit/source/WebCore/bindings/scripts/test/JS/

View File

@@ -0,0 +1,184 @@
---
name: implementing-jsc-classes-cpp
description: Implements JavaScript classes in C++ using JavaScriptCore. Use when creating new JS classes with C++ bindings, prototypes, or constructors.
---
# Implementing JavaScript Classes in C++
## Class Structure
For publicly accessible Constructor and Prototype, create 3 classes:
1. **`class Foo : public JSC::DestructibleObject`** - if C++ fields exist; otherwise use `JSC::constructEmptyObject` with `putDirectOffset`
2. **`class FooPrototype : public JSC::JSNonFinalObject`**
3. **`class FooConstructor : public JSC::InternalFunction`**
No public constructor? Only Prototype and class needed.
## Iso Subspaces
Classes with C++ fields need subspaces in:
- `src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h`
- `src/bun.js/bindings/webcore/DOMIsoSubspaces.h`
```cpp
template<typename MyClassT, JSC::SubspaceAccess mode>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) {
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
return nullptr;
return WebCore::subspaceForImpl<MyClassT, WebCore::UseCustomHeapCellType::No>(
vm,
[](auto& spaces) { return spaces.m_clientSubspaceForMyClassT.get(); },
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForMyClassT = std::forward<decltype(space)>(space); },
[](auto& spaces) { return spaces.m_subspaceForMyClassT.get(); },
[](auto& spaces, auto&& space) { spaces.m_subspaceForMyClassT = std::forward<decltype(space)>(space); });
}
```
## Property Definitions
```cpp
static JSC_DECLARE_HOST_FUNCTION(jsFooProtoFuncMethod);
static JSC_DECLARE_CUSTOM_GETTER(jsFooGetter_property);
static const HashTableValue JSFooPrototypeTableValues[] = {
{ "property"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsFooGetter_property, 0 } },
{ "method"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsFooProtoFuncMethod, 1 } },
};
```
## Prototype Class
```cpp
class JSFooPrototype final : public JSC::JSNonFinalObject {
public:
using Base = JSC::JSNonFinalObject;
static constexpr unsigned StructureFlags = Base::StructureFlags;
static JSFooPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) {
JSFooPrototype* prototype = new (NotNull, allocateCell<JSFooPrototype>(vm)) JSFooPrototype(vm, structure);
prototype->finishCreation(vm);
return prototype;
}
template<typename, JSC::SubspaceAccess>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) { return &vm.plainObjectSpace(); }
DECLARE_INFO;
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) {
auto* structure = JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
structure->setMayBePrototype(true);
return structure;
}
private:
JSFooPrototype(JSC::VM& vm, JSC::Structure* structure) : Base(vm, structure) {}
void finishCreation(JSC::VM& vm);
};
void JSFooPrototype::finishCreation(VM& vm) {
Base::finishCreation(vm);
reifyStaticProperties(vm, JSFoo::info(), JSFooPrototypeTableValues, *this);
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
}
```
## Getter/Setter/Function Definitions
```cpp
// Getter
JSC_DEFINE_CUSTOM_GETTER(jsFooGetter_prop, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName)) {
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSFoo* thisObject = jsDynamicCast<JSFoo*>(JSValue::decode(thisValue));
if (UNLIKELY(!thisObject)) {
Bun::throwThisTypeError(*globalObject, scope, "JSFoo"_s, "prop"_s);
return {};
}
return JSValue::encode(jsBoolean(thisObject->value()));
}
// Function
JSC_DEFINE_HOST_FUNCTION(jsFooProtoFuncMethod, (JSGlobalObject* globalObject, CallFrame* callFrame)) {
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto* thisObject = jsDynamicCast<JSFoo*>(callFrame->thisValue());
if (UNLIKELY(!thisObject)) {
Bun::throwThisTypeError(*globalObject, scope, "Foo"_s, "method"_s);
return {};
}
return JSValue::encode(thisObject->doSomething(vm, globalObject));
}
```
## Constructor Class
```cpp
class JSFooConstructor final : public JSC::InternalFunction {
public:
using Base = JSC::InternalFunction;
static constexpr unsigned StructureFlags = Base::StructureFlags;
static JSFooConstructor* create(JSC::VM& vm, JSC::Structure* structure, JSC::JSObject* prototype) {
JSFooConstructor* constructor = new (NotNull, JSC::allocateCell<JSFooConstructor>(vm)) JSFooConstructor(vm, structure);
constructor->finishCreation(vm, prototype);
return constructor;
}
DECLARE_INFO;
template<typename CellType, JSC::SubspaceAccess>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) { return &vm.internalFunctionSpace(); }
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) {
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info());
}
private:
JSFooConstructor(JSC::VM& vm, JSC::Structure* structure) : Base(vm, structure, callFoo, constructFoo) {}
void finishCreation(JSC::VM& vm, JSC::JSObject* prototype) {
Base::finishCreation(vm, 0, "Foo"_s);
putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
}
};
```
## Structure Caching
Add to `ZigGlobalObject.h`:
```cpp
JSC::LazyClassStructure m_JSFooClassStructure;
```
Initialize in `ZigGlobalObject.cpp`:
```cpp
m_JSFooClassStructure.initLater([](LazyClassStructure::Initializer& init) {
Bun::initJSFooClassStructure(init);
});
```
Visit in `visitChildrenImpl`:
```cpp
m_JSFooClassStructure.visit(visitor);
```
## Expose to Zig
```cpp
extern "C" JSC::EncodedJSValue Bun__JSFooConstructor(Zig::GlobalObject* globalObject) {
return JSValue::encode(globalObject->m_JSFooClassStructure.constructor(globalObject));
}
extern "C" EncodedJSValue Bun__Foo__toJS(Zig::GlobalObject* globalObject, Foo* foo) {
auto* structure = globalObject->m_JSFooClassStructure.get(globalObject);
return JSValue::encode(JSFoo::create(globalObject->vm(), structure, globalObject, WTFMove(foo)));
}
```
Include `#include "root.h"` at the top of C++ files.

View File

@@ -0,0 +1,206 @@
---
name: implementing-jsc-classes-zig
description: Creates JavaScript classes using Bun's Zig bindings generator (.classes.ts). Use when implementing new JS APIs in Zig with JSC integration.
---
# Bun's JavaScriptCore Class Bindings Generator
Bridge JavaScript and Zig through `.classes.ts` definitions and Zig implementations.
## Architecture
1. **Zig Implementation** (.zig files)
2. **JavaScript Interface Definition** (.classes.ts files)
3. **Generated Code** (C++/Zig files connecting them)
## Class Definition (.classes.ts)
```typescript
define({
name: "TextDecoder",
constructor: true,
JSType: "object",
finalize: true,
proto: {
decode: { args: 1 },
encoding: { getter: true, cache: true },
fatal: { getter: true },
},
});
```
Options:
- `name`: Class name
- `constructor`: Has public constructor
- `JSType`: "object", "function", etc.
- `finalize`: Needs cleanup
- `proto`: Properties/methods
- `cache`: Cache property values via WriteBarrier
## Zig Implementation
```zig
pub const TextDecoder = struct {
pub const js = JSC.Codegen.JSTextDecoder;
pub const toJS = js.toJS;
pub const fromJS = js.fromJS;
pub const fromJSDirect = js.fromJSDirect;
encoding: []const u8,
fatal: bool,
pub fn constructor(
globalObject: *JSGlobalObject,
callFrame: *JSC.CallFrame,
) bun.JSError!*TextDecoder {
return bun.new(TextDecoder, .{ .encoding = "utf-8", .fatal = false });
}
pub fn decode(
this: *TextDecoder,
globalObject: *JSGlobalObject,
callFrame: *JSC.CallFrame,
) bun.JSError!JSC.JSValue {
const args = callFrame.arguments();
if (args.len < 1 or args.ptr[0].isUndefinedOrNull()) {
return globalObject.throw("Input cannot be null", .{});
}
return JSC.JSValue.jsString(globalObject, "result");
}
pub fn getEncoding(this: *TextDecoder, globalObject: *JSGlobalObject) JSC.JSValue {
return JSC.JSValue.createStringFromUTF8(globalObject, this.encoding);
}
fn deinit(this: *TextDecoder) void {
// Release resources
}
pub fn finalize(this: *TextDecoder) void {
this.deinit();
bun.destroy(this);
}
};
```
**Key patterns:**
- Use `bun.JSError!JSValue` return type for error handling
- Use `globalObject` not `ctx`
- `deinit()` for cleanup, `finalize()` called by GC
- Update `src/bun.js/bindings/generated_classes_list.zig`
## CallFrame Access
```zig
const args = callFrame.arguments();
const first_arg = args.ptr[0]; // Access as slice
const argCount = args.len;
const thisValue = callFrame.thisValue();
```
## Property Caching
For `cache: true` properties, generated accessors:
```zig
// Get cached value
pub fn encodingGetCached(thisValue: JSC.JSValue) ?JSC.JSValue {
const result = TextDecoderPrototype__encodingGetCachedValue(thisValue);
if (result == .zero) return null;
return result;
}
// Set cached value
pub fn encodingSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void {
TextDecoderPrototype__encodingSetCachedValue(thisValue, globalObject, value);
}
```
## Error Handling
```zig
pub fn method(this: *MyClass, globalObject: *JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!JSC.JSValue {
const args = callFrame.arguments();
if (args.len < 1) {
return globalObject.throw("Missing required argument", .{});
}
return JSC.JSValue.jsString(globalObject, "Success!");
}
```
## Memory Management
```zig
pub fn deinit(this: *TextDecoder) void {
this._encoding.deref();
if (this.buffer) |buffer| {
bun.default_allocator.free(buffer);
}
}
pub fn finalize(this: *TextDecoder) void {
JSC.markBinding(@src());
this.deinit();
bun.default_allocator.destroy(this);
}
```
## Creating a New Binding
1. Define interface in `.classes.ts`:
```typescript
define({
name: "MyClass",
constructor: true,
finalize: true,
proto: {
myMethod: { args: 1 },
myProperty: { getter: true, cache: true },
},
});
```
2. Implement in `.zig`:
```zig
pub const MyClass = struct {
pub const js = JSC.Codegen.JSMyClass;
pub const toJS = js.toJS;
pub const fromJS = js.fromJS;
value: []const u8,
pub const new = bun.TrivialNew(@This());
pub fn constructor(globalObject: *JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!*MyClass {
return MyClass.new(.{ .value = "" });
}
pub fn myMethod(this: *MyClass, globalObject: *JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!JSC.JSValue {
return JSC.JSValue.jsUndefined();
}
pub fn getMyProperty(this: *MyClass, globalObject: *JSGlobalObject) JSC.JSValue {
return JSC.JSValue.jsString(globalObject, this.value);
}
pub fn deinit(this: *MyClass) void {}
pub fn finalize(this: *MyClass) void {
this.deinit();
bun.destroy(this);
}
};
```
3. Add to `src/bun.js/bindings/generated_classes_list.zig`
## Generated Components
- **C++ Classes**: `JSMyClass`, `JSMyClassPrototype`, `JSMyClassConstructor`
- **Method Bindings**: `MyClassPrototype__myMethodCallback`
- **Property Accessors**: `MyClassPrototype__myPropertyGetterWrap`
- **Zig Bindings**: External function declarations, cached value accessors

View File

@@ -0,0 +1,222 @@
---
name: writing-bundler-tests
description: Guides writing bundler tests using itBundled/expectBundled in test/bundler/. Use when creating or modifying bundler, transpiler, or code transformation tests.
---
# Writing Bundler Tests
Bundler tests use `itBundled()` from `test/bundler/expectBundled.ts` to test Bun's bundler.
## Basic Usage
```typescript
import { describe } from "bun:test";
import { itBundled, dedent } from "./expectBundled";
describe("bundler", () => {
itBundled("category/TestName", {
files: {
"index.js": `console.log("hello");`,
},
run: {
stdout: "hello",
},
});
});
```
Test ID format: `category/TestName` (e.g., `banner/CommentBanner`, `minify/Empty`)
## File Setup
```typescript
{
files: {
"index.js": `console.log("test");`,
"lib.ts": `export const foo = 123;`,
"nested/file.js": `export default {};`,
},
entryPoints: ["index.js"], // defaults to first file
runtimeFiles: { // written AFTER bundling
"extra.js": `console.log("added later");`,
},
}
```
## Bundler Options
```typescript
{
outfile: "/out.js",
outdir: "/out",
format: "esm" | "cjs" | "iife",
target: "bun" | "browser" | "node",
// Minification
minifyWhitespace: true,
minifyIdentifiers: true,
minifySyntax: true,
// Code manipulation
banner: "// copyright",
footer: "// end",
define: { "PROD": "true" },
external: ["lodash"],
// Advanced
sourceMap: "inline" | "external",
splitting: true,
treeShaking: true,
drop: ["console"],
}
```
## Runtime Verification
```typescript
{
run: {
stdout: "expected output", // exact match
stdout: /regex/, // pattern match
partialStdout: "contains this", // substring
stderr: "error output",
exitCode: 1,
env: { NODE_ENV: "production" },
runtime: "bun" | "node",
// Runtime errors
error: "ReferenceError: x is not defined",
},
}
```
## Bundle Errors/Warnings
```typescript
{
bundleErrors: {
"/file.js": ["error message 1", "error message 2"],
},
bundleWarnings: {
"/file.js": ["warning message"],
},
}
```
## Dead Code Elimination (DCE)
Add markers in source code:
```javascript
// KEEP - this should survive
const used = 1;
// REMOVE - this should be eliminated
const unused = 2;
```
```typescript
{
dce: true,
dceKeepMarkerCount: 5, // expected KEEP markers
}
```
## Capture Pattern
Verify exact transpilation with `capture()`:
```typescript
itBundled("string/Folding", {
files: {
"index.ts": `capture(\`\${1 + 1}\`);`,
},
capture: ['"2"'], // expected captured value
minifySyntax: true,
});
```
## Post-Bundle Assertions
```typescript
{
onAfterBundle(api) {
api.expectFile("out.js").toContain("console.log");
api.assertFileExists("out.js");
const content = api.readFile("out.js");
expect(content).toMatchSnapshot();
const values = api.captureFile("out.js");
expect(values).toEqual(["2"]);
},
}
```
## Common Patterns
**Simple output verification:**
```typescript
itBundled("banner/Comment", {
banner: "// copyright",
files: { "a.js": `console.log("Hello")` },
onAfterBundle(api) {
api.expectFile("out.js").toContain("// copyright");
},
});
```
**Multi-file CJS/ESM interop:**
```typescript
itBundled("cjs/ImportSyntax", {
files: {
"entry.js": `import lib from './lib.cjs'; console.log(lib);`,
"lib.cjs": `exports.foo = 'bar';`,
},
run: { stdout: '{"foo":"bar"}' },
});
```
**Error handling:**
```typescript
itBundled("edgecase/InvalidLoader", {
files: { "index.js": `...` },
bundleErrors: {
"index.js": ["Unsupported loader type"],
},
});
```
## Test Organization
```text
test/bundler/
├── bundler_banner.test.ts
├── bundler_string.test.ts
├── bundler_minify.test.ts
├── bundler_cjs.test.ts
├── bundler_edgecase.test.ts
├── bundler_splitting.test.ts
├── css/
├── transpiler/
└── expectBundled.ts
```
## Running Tests
```bash
bun bd test test/bundler/bundler_banner.test.ts
BUN_BUNDLER_TEST_FILTER="banner/Comment" bun bd test bundler_banner.test.ts
BUN_BUNDLER_TEST_DEBUG=1 bun bd test bundler_minify.test.ts
```
## Key Points
- Use `dedent` for readable multi-line code
- File paths are relative (e.g., `/index.js`)
- Use `capture()` to verify exact transpilation results
- Use `.toMatchSnapshot()` for complex outputs
- Pass array to `run` for multiple test scenarios

View File

@@ -0,0 +1,94 @@
---
name: writing-dev-server-tests
description: Guides writing HMR/Dev Server tests in test/bake/. Use when creating or modifying dev server, hot reloading, or bundling tests.
---
# Writing HMR/Dev Server Tests
Dev server tests validate hot-reloading robustness and reliability.
## File Structure
- `test/bake/bake-harness.ts` - shared utilities: `devTest`, `prodTest`, `devAndProductionTest`, `Dev` class, `Client` class
- `test/bake/client-fixture.mjs` - subprocess for `Client` (page loading, IPC queries)
- `test/bake/dev/*.test.ts` - dev server and hot reload tests
- `test/bake/dev-and-prod.ts` - tests running on both dev and production mode
## Test Categories
- `bundle.test.ts` - DevServer-specific bundling bugs
- `css.test.ts` - CSS bundling issues
- `plugins.test.ts` - development mode plugins
- `ecosystem.test.ts` - library compatibility (prefer concrete bugs over full package tests)
- `esm.test.ts` - ESM features in development
- `html.test.ts` - HTML file handling
- `react-spa.test.ts` - React, react-refresh transform, server components
- `sourcemap.test.ts` - source map correctness
## devTest Basics
```ts
import { devTest, emptyHtmlFile } from "../bake-harness";
devTest("html file is watched", {
files: {
"index.html": emptyHtmlFile({
scripts: ["/script.ts"],
body: "<h1>Hello</h1>",
}),
"script.ts": `console.log("hello");`,
},
async test(dev) {
await dev.fetch("/").expect.toInclude("<h1>Hello</h1>");
await dev.patch("index.html", { find: "Hello", replace: "World" });
await dev.fetch("/").expect.toInclude("<h1>World</h1>");
await using c = await dev.client("/");
await c.expectMessage("hello");
await c.expectReload(async () => {
await dev.patch("index.html", { find: "World", replace: "Bar" });
});
await c.expectMessage("hello");
},
});
```
## Key APIs
- **`files`**: Initial filesystem state
- **`dev.fetch()`**: HTTP requests
- **`dev.client()`**: Opens browser instance
- **`dev.write/patch/delete`**: Filesystem mutations (wait for hot-reload automatically)
- **`c.expectMessage()`**: Assert console.log output
- **`c.expectReload()`**: Wrap code that causes hard reload
**Important**: Use `dev.write/patch/delete` instead of `node:fs` - they wait for hot-reload.
## Testing Errors
```ts
devTest("import then create", {
files: {
"index.html": `<!DOCTYPE html><html><head></head><body><script type="module" src="/script.ts"></script></body></html>`,
"script.ts": `import data from "./data"; console.log(data);`,
},
async test(dev) {
const c = await dev.client("/", {
errors: ['script.ts:1:18: error: Could not resolve: "./data"'],
});
await c.expectReload(async () => {
await dev.write("data.ts", "export default 'data';");
});
await c.expectMessage("data");
},
});
```
Specify expected errors with the `errors` option:
```ts
await dev.delete("other.ts", {
errors: ['index.ts:1:16: error: Could not resolve: "./other"'],
});
```

View File

@@ -0,0 +1,268 @@
---
name: zig-system-calls
description: Guides using bun.sys for system calls and file I/O in Zig. Use when implementing file operations instead of std.fs or std.posix.
---
# System Calls & File I/O in Zig
Use `bun.sys` instead of `std.fs` or `std.posix` for cross-platform syscalls with proper error handling.
## bun.sys.File (Preferred)
For most file operations, use the `bun.sys.File` wrapper:
```zig
const File = bun.sys.File;
const file = switch (File.open(path, bun.O.RDWR, 0o644)) {
.result => |f| f,
.err => |err| return .{ .err = err },
};
defer file.close();
// Read/write
_ = try file.read(buffer).unwrap();
_ = try file.writeAll(data).unwrap();
// Get file info
const stat = try file.stat().unwrap();
const size = try file.getEndPos().unwrap();
// std.io compatible
const reader = file.reader();
const writer = file.writer();
```
### Complete Example
```zig
const File = bun.sys.File;
pub fn writeFile(path: [:0]const u8, data: []const u8) File.WriteError!void {
const file = switch (File.open(path, bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC, 0o664)) {
.result => |f| f,
.err => |err| return err.toError(),
};
defer file.close();
_ = switch (file.writeAll(data)) {
.result => {},
.err => |err| return err.toError(),
};
}
```
## Why bun.sys?
| Aspect | bun.sys | std.fs/std.posix |
| ----------- | -------------------------------- | ------------------- |
| Return Type | `Maybe(T)` with detailed Error | Generic error union |
| Windows | Full support with libuv fallback | Limited/POSIX-only |
| Error Info | errno, syscall tag, path, fd | errno only |
| EINTR | Automatic retry | Manual handling |
## Error Handling with Maybe(T)
`bun.sys` functions return `Maybe(T)` - a tagged union:
```zig
const sys = bun.sys;
// Pattern 1: Switch on result/error
switch (sys.read(fd, buffer)) {
.result => |bytes_read| {
// use bytes_read
},
.err => |err| {
// err.errno, err.syscall, err.fd, err.path
if (err.getErrno() == .AGAIN) {
// handle EAGAIN
}
},
}
// Pattern 2: Unwrap with try (converts to Zig error)
const bytes = try sys.read(fd, buffer).unwrap();
// Pattern 3: Unwrap with default
const value = sys.stat(path).unwrapOr(default_stat);
```
## Low-Level File Operations
Only use these when `bun.sys.File` doesn't meet your needs.
### Opening Files
```zig
const sys = bun.sys;
// Use bun.O flags (cross-platform normalized)
const fd = switch (sys.open(path, bun.O.RDONLY, 0)) {
.result => |fd| fd,
.err => |err| return .{ .err = err },
};
defer fd.close();
// Common flags
bun.O.RDONLY, bun.O.WRONLY, bun.O.RDWR
bun.O.CREAT, bun.O.TRUNC, bun.O.APPEND
bun.O.NONBLOCK, bun.O.DIRECTORY
```
### Reading & Writing
```zig
// Single read (may return less than buffer size)
switch (sys.read(fd, buffer)) {
.result => |n| { /* n bytes read */ },
.err => |err| { /* handle error */ },
}
// Read until EOF or buffer full
const total = try sys.readAll(fd, buffer).unwrap();
// Position-based read/write
sys.pread(fd, buffer, offset)
sys.pwrite(fd, data, offset)
// Vector I/O
sys.readv(fd, iovecs)
sys.writev(fd, iovecs)
```
### File Info
```zig
sys.stat(path) // Follow symlinks
sys.lstat(path) // Don't follow symlinks
sys.fstat(fd) // From file descriptor
sys.fstatat(fd, path)
// Linux-only: faster selective stat
sys.statx(path, &.{ .size, .mtime })
```
### Path Operations
```zig
sys.unlink(path)
sys.unlinkat(dir_fd, path)
sys.rename(from, to)
sys.renameat(from_dir, from, to_dir, to)
sys.readlink(path, buf)
sys.readlinkat(fd, path, buf)
sys.link(T, src, dest)
sys.linkat(src_fd, src, dest_fd, dest)
sys.symlink(target, dest)
sys.symlinkat(target, dirfd, dest)
sys.mkdir(path, mode)
sys.mkdirat(dir_fd, path, mode)
sys.rmdir(path)
```
### Permissions
```zig
sys.chmod(path, mode)
sys.fchmod(fd, mode)
sys.fchmodat(fd, path, mode, flags)
sys.chown(path, uid, gid)
sys.fchown(fd, uid, gid)
```
### Closing File Descriptors
Close is on `bun.FD`:
```zig
fd.close(); // Asserts on error (use in defer)
// Or if you need error info:
if (fd.closeAllowingBadFileDescriptor(null)) |err| {
// handle error
}
```
## Directory Operations
```zig
var buf: bun.PathBuffer = undefined;
const cwd = try sys.getcwd(&buf).unwrap();
const cwdZ = try sys.getcwdZ(&buf).unwrap(); // Zero-terminated
sys.chdir(path, destination)
```
### Directory Iteration
Use `bun.DirIterator` instead of `std.fs.Dir.Iterator`:
```zig
var iter = bun.iterateDir(dir_fd);
while (true) {
switch (iter.next()) {
.result => |entry| {
if (entry) |e| {
const name = e.name.slice();
const kind = e.kind; // .file, .directory, .sym_link, etc.
} else {
break; // End of directory
}
},
.err => |err| return .{ .err = err },
}
}
```
## Socket Operations
**Important**: `bun.sys` has limited socket support. For network I/O:
- **Non-blocking sockets**: Use `uws.Socket` (libuwebsockets) exclusively
- **Pipes/blocking I/O**: Use `PipeReader.zig` and `PipeWriter.zig`
Available in bun.sys:
```zig
sys.setsockopt(fd, level, optname, value)
sys.socketpair(domain, socktype, protocol, nonblocking_status)
```
Do NOT use `bun.sys` for socket read/write - use `uws.Socket` instead.
## Other Operations
```zig
sys.ftruncate(fd, size)
sys.lseek(fd, offset, whence)
sys.dup(fd)
sys.dupWithFlags(fd, flags)
sys.fcntl(fd, cmd, arg)
sys.pipe()
sys.mmap(...)
sys.munmap(memory)
sys.access(path, mode)
sys.futimens(fd, atime, mtime)
sys.utimens(path, atime, mtime)
```
## Error Type
```zig
const err: bun.sys.Error = ...;
err.errno // Raw errno value
err.getErrno() // As std.posix.E enum
err.syscall // Which syscall failed (Tag enum)
err.fd // Optional: file descriptor
err.path // Optional: path string
```
## Key Points
- Prefer `bun.sys.File` wrapper for most file operations
- Use low-level `bun.sys` functions only when needed
- Use `bun.O.*` flags instead of `std.os.O.*`
- Handle `Maybe(T)` with switch or `.unwrap()`
- Use `defer fd.close()` for cleanup
- EINTR is handled automatically in most functions
- For sockets, use `uws.Socket` not `bun.sys`

View File

@@ -3,8 +3,6 @@ language: en-US
issue_enrichment:
auto_enrich:
enabled: false
planning:
enabled: false
reviews:
profile: assertive

View File

@@ -1,41 +0,0 @@
---
description:
globs: src/**/*.cpp,src/**/*.zig
alwaysApply: false
---
### Build Commands
- **Build debug version**: `bun bd` or `bun run build:debug`
- Creates a debug build at `./build/debug/bun-debug`
- Compilation takes ~2.5 minutes
- **Run tests with your debug build**: `bun bd test <test-file>`
- **CRITICAL**: Never use `bun test` directly - it won't include your changes
- **Run any command with debug build**: `bun bd <command>`
### Run a file
To run a file, use:
```sh
bun bd <file> <...args>
```
**CRITICAL**: Never use `bun <file>` directly. It will not have your changes.
### Logging
`BUN_DEBUG_$(SCOPE)=1` enables debug logs for a specific debug log scope.
Debug logs look like this:
```zig
const log = bun.Output.scoped(.${SCOPE}, .hidden);
// ...later
log("MY DEBUG LOG", .{})
```
### Code Generation
Code generation happens automatically as part of the build process. There are no commands to run.

View File

@@ -1,139 +0,0 @@
---
description: Writing HMR/Dev Server tests
globs: test/bake/*
---
# Writing HMR/Dev Server tests
Dev server tests validate that hot-reloading is robust, correct, and reliable. Remember to write thorough, yet concise tests.
## File Structure
- `test/bake/bake-harness.ts` - shared utilities and test harness
- primary test functions `devTest` / `prodTest` / `devAndProductionTest`
- class `Dev` (controls subprocess for dev server)
- class `Client` (controls a happy-dom subprocess for having the page open)
- more helpers
- `test/bake/client-fixture.mjs` - subprocess for what `Client` controls. it loads a page and uses IPC to query parts of the page, run javascript, and much more.
- `test/bake/dev/*.test.ts` - these call `devTest` to test dev server and hot reloading
- `test/bake/dev-and-prod.ts` - these use `devAndProductionTest` to run the same test on dev and production mode. these tests cannot really test hot reloading for obvious reasons.
## Categories
bundle.test.ts - Bundle tests are tests concerning bundling bugs that only occur in DevServer.
css.test.ts - CSS tests concern bundling bugs with CSS files
plugins.test.ts - Plugin tests concern plugins in development mode.
ecosystem.test.ts - These tests involve ensuring certain libraries are correct. It is preferred to test more concrete bugs than testing entire packages.
esm.test.ts - ESM tests are about various esm features in development mode.
html.test.ts - HTML tests are tests relating to HTML files themselves.
react-spa.test.ts - Tests relating to React, our react-refresh transform, and basic server component transforms.
sourcemap.test.ts - Tests verifying source-maps are correct.
## `devTest` Basics
A test takes in two primary inputs: `files` and `async test(dev) {`
```ts
import { devTest, emptyHtmlFile } from "../bake-harness";
devTest("html file is watched", {
files: {
"index.html": emptyHtmlFile({
scripts: ["/script.ts"],
body: "<h1>Hello</h1>",
}),
"script.ts": `
console.log("hello");
`,
},
async test(dev) {
await dev.fetch("/").expect.toInclude("<h1>Hello</h1>");
await dev.fetch("/").expect.toInclude("<h1>Hello</h1>");
await dev.patch("index.html", {
find: "Hello",
replace: "World",
});
await dev.fetch("/").expect.toInclude("<h1>World</h1>");
// Works
await using c = await dev.client("/");
await c.expectMessage("hello");
// Editing HTML reloads
await c.expectReload(async () => {
await dev.patch("index.html", {
find: "World",
replace: "Hello",
});
await dev.fetch("/").expect.toInclude("<h1>Hello</h1>");
});
await c.expectMessage("hello");
await c.expectReload(async () => {
await dev.patch("index.html", {
find: "Hello",
replace: "Bar",
});
await dev.fetch("/").expect.toInclude("<h1>Bar</h1>");
});
await c.expectMessage("hello");
await c.expectReload(async () => {
await dev.patch("script.ts", {
find: "hello",
replace: "world",
});
});
await c.expectMessage("world");
},
});
```
`files` holds the initial state, and the callback runs with the server running. `dev.fetch()` runs HTTP requests, while `dev.client()` opens a browser instance to the code.
Functions `dev.write` and `dev.patch` and `dev.delete` mutate the filesystem. Do not use `node:fs` APIs, as the dev server ones are hooked to wait for hot-reload, and all connected clients to receive changes.
When a change performs a hard-reload, that must be explicitly annotated with `expectReload`. This tells `client-fixture.mjs` that the test is meant to reload the page once; All other hard reloads automatically fail the test.
Client's have `console.log` instrumented, so that any unasserted logs fail the test. This makes it more obvious when an extra reload or re-evaluation. Messages are awaited via `c.expectMessage("log")` or with multiple arguments if there are multiple logs.
## Testing for bundling errors
By default, a client opening a page to an error will fail the test. This makes testing errors explicit.
```ts
devTest("import then create", {
files: {
"index.html": `
<!DOCTYPE html>
<html>
<head></head>
<body>
<script type="module" src="/script.ts"></script>
</body>
</html>
`,
"script.ts": `
import data from "./data";
console.log(data);
`,
},
async test(dev) {
const c = await dev.client("/", {
errors: ['script.ts:1:18: error: Could not resolve: "./data"'],
});
await c.expectReload(async () => {
await dev.write("data.ts", "export default 'data';");
});
await c.expectMessage("data");
},
});
```
Many functions take an options value to allow specifying it will produce errors. For example, this delete is going to cause a resolution failure.
```ts
await dev.delete("other.ts", {
errors: ['index.ts:1:16: error: Could not resolve: "./other"'],
});
```

View File

@@ -1,413 +0,0 @@
---
description: JavaScript class implemented in C++
globs: *.cpp
alwaysApply: false
---
# Implementing JavaScript classes in C++
If there is a publicly accessible Constructor and Prototype, then there are 3 classes:
- IF there are C++ class members we need a destructor, so `class Foo : public JSC::DestructibleObject`, if no C++ class fields (only JS properties) then we don't need a class at all usually. We can instead use JSC::constructEmptyObject(vm, structure) and `putDirectOffset` like in [NodeFSStatBinding.cpp](mdc:src/bun.js/bindings/NodeFSStatBinding.cpp).
- class FooPrototype : public JSC::JSNonFinalObject
- class FooConstructor : public JSC::InternalFunction
If there is no publicly accessible Constructor, just the Prototype and the class is necessary. In some cases, we can avoid the prototype entirely (but that's rare).
If there are C++ fields on the Foo class, the Foo class will need an iso subspace added to [DOMClientIsoSubspaces.h](mdc:src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h) and [DOMIsoSubspaces.h](mdc:src/bun.js/bindings/webcore/DOMIsoSubspaces.h). Prototype and Constructor do not need subspaces.
Usually you'll need to #include "root.h" at the top of C++ files or you'll get lint errors.
Generally, defining the subspace looks like this:
```c++
class Foo : public JSC::DestructibleObject {
// ...
template<typename MyClassT, JSC::SubspaceAccess mode>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
{
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
return nullptr;
return WebCore::subspaceForImpl<MyClassT, WebCore::UseCustomHeapCellType::No>(
vm,
[](auto& spaces) { return spaces.m_clientSubspaceFor${MyClassT}.get(); },
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceFor${MyClassT} = std::forward<decltype(space)>(space); },
[](auto& spaces) { return spaces.m_subspaceFo${MyClassT}.get(); },
[](auto& spaces, auto&& space) { spaces.m_subspaceFor${MyClassT} = std::forward<decltype(space)>(space); });
}
```
It's better to put it in the .cpp file instead of the .h file, when possible.
## Defining properties
Define properties on the prototype. Use a const HashTableValues like this:
```C++
static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncCheckEmail);
static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncCheckHost);
static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncCheckIP);
static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncCheckIssued);
static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncCheckPrivateKey);
static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncToJSON);
static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncToLegacyObject);
static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncToString);
static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncVerify);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_ca);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_fingerprint);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_fingerprint256);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_fingerprint512);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_subject);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_subjectAltName);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_infoAccess);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_keyUsage);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_issuer);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_issuerCertificate);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_publicKey);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_raw);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_serialNumber);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_validFrom);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_validTo);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_validFromDate);
static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_validToDate);
static const HashTableValue JSX509CertificatePrototypeTableValues[] = {
{ "ca"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_ca, 0 } },
{ "checkEmail"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncCheckEmail, 2 } },
{ "checkHost"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncCheckHost, 2 } },
{ "checkIP"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncCheckIP, 1 } },
{ "checkIssued"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncCheckIssued, 1 } },
{ "checkPrivateKey"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncCheckPrivateKey, 1 } },
{ "fingerprint"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_fingerprint, 0 } },
{ "fingerprint256"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_fingerprint256, 0 } },
{ "fingerprint512"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_fingerprint512, 0 } },
{ "infoAccess"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_infoAccess, 0 } },
{ "issuer"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_issuer, 0 } },
{ "issuerCertificate"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_issuerCertificate, 0 } },
{ "keyUsage"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_keyUsage, 0 } },
{ "publicKey"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_publicKey, 0 } },
{ "raw"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_raw, 0 } },
{ "serialNumber"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_serialNumber, 0 } },
{ "subject"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_subject, 0 } },
{ "subjectAltName"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_subjectAltName, 0 } },
{ "toJSON"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncToJSON, 0 } },
{ "toLegacyObject"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncToLegacyObject, 0 } },
{ "toString"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncToString, 0 } },
{ "validFrom"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_validFrom, 0 } },
{ "validFromDate"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessorOrValue), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_validFromDate, 0 } },
{ "validTo"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_validTo, 0 } },
{ "validToDate"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessorOrValue), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_validToDate, 0 } },
{ "verify"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncVerify, 1 } },
};
```
### Creating a prototype class
Follow a pattern like this:
```c++
class JSX509CertificatePrototype final : public JSC::JSNonFinalObject {
public:
using Base = JSC::JSNonFinalObject;
static constexpr unsigned StructureFlags = Base::StructureFlags;
static JSX509CertificatePrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
{
JSX509CertificatePrototype* prototype = new (NotNull, allocateCell<JSX509CertificatePrototype>(vm)) JSX509CertificatePrototype(vm, structure);
prototype->finishCreation(vm);
return prototype;
}
template<typename, JSC::SubspaceAccess>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
{
return &vm.plainObjectSpace();
}
DECLARE_INFO;
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
{
auto* structure = JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
structure->setMayBePrototype(true);
return structure;
}
private:
JSX509CertificatePrototype(JSC::VM& vm, JSC::Structure* structure)
: Base(vm, structure)
{
}
void finishCreation(JSC::VM& vm);
};
const ClassInfo JSX509CertificatePrototype::s_info = { "X509Certificate"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSX509CertificatePrototype) };
void JSX509CertificatePrototype::finishCreation(VM& vm)
{
Base::finishCreation(vm);
reifyStaticProperties(vm, JSX509Certificate::info(), JSX509CertificatePrototypeTableValues, *this);
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
}
} // namespace Bun
```
### Getter definition:
```C++
JSC_DEFINE_CUSTOM_GETTER(jsX509CertificateGetter_ca, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSX509Certificate* thisObject = jsDynamicCast<JSX509Certificate*>(JSValue::decode(thisValue));
if (UNLIKELY(!thisObject)) {
Bun::throwThisTypeError(*globalObject, scope, "JSX509Certificate"_s, "ca"_s);
return {};
}
return JSValue::encode(jsBoolean(thisObject->view().isCA()));
}
```
### Setter definition
```C++
JSC_DEFINE_CUSTOM_SETTER(jsImportMetaObjectSetter_require, (JSGlobalObject * jsGlobalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue encodedValue, PropertyName propertyName))
{
ImportMetaObject* thisObject = jsDynamicCast<ImportMetaObject*>(JSValue::decode(thisValue));
if (UNLIKELY(!thisObject))
return false;
JSValue value = JSValue::decode(encodedValue);
if (!value.isCell()) {
// TODO:
return true;
}
thisObject->requireProperty.set(thisObject->vm(), thisObject, value.asCell());
return true;
}
```
### Function definition
```C++
JSC_DEFINE_HOST_FUNCTION(jsX509CertificateProtoFuncToJSON, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto *thisObject = jsDynamicCast<MyClassT*>(callFrame->thisValue());
if (UNLIKELY(!thisObject)) {
Bun::throwThisTypeError(*globalObject, scope, "MyClass"_s, "myFunctionName"_s);
return {};
}
return JSValue::encode(functionThatReturnsJSValue(vm, globalObject, thisObject));
}
```
### Constructor definition
```C++
JSC_DECLARE_HOST_FUNCTION(callStats);
JSC_DECLARE_HOST_FUNCTION(constructStats);
class JSStatsConstructor final : public JSC::InternalFunction {
public:
using Base = JSC::InternalFunction;
static constexpr unsigned StructureFlags = Base::StructureFlags;
static JSStatsConstructor* create(JSC::VM& vm, JSC::Structure* structure, JSC::JSObject* prototype)
{
JSStatsConstructor* constructor = new (NotNull, JSC::allocateCell<JSStatsConstructor>(vm)) JSStatsConstructor(vm, structure);
constructor->finishCreation(vm, prototype);
return constructor;
}
DECLARE_INFO;
template<typename CellType, JSC::SubspaceAccess>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
{
return &vm.internalFunctionSpace();
}
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
{
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info());
}
private:
JSStatsConstructor(JSC::VM& vm, JSC::Structure* structure)
: Base(vm, structure, callStats, constructStats)
{
}
void finishCreation(JSC::VM& vm, JSC::JSObject* prototype)
{
Base::finishCreation(vm, 0, "Stats"_s);
putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
}
};
```
### Structure caching
If there's a class, prototype, and constructor:
1. Add the `JSC::LazyClassStructure` to [ZigGlobalObject.h](mdc:src/bun.js/bindings/ZigGlobalObject.h)
2. Initialize the class structure in [ZigGlobalObject.cpp](mdc:src/bun.js/bindings/ZigGlobalObject.cpp) in `void GlobalObject::finishCreation(VM& vm)`
3. Visit the class structure in visitChildren in [ZigGlobalObject.cpp](mdc:src/bun.js/bindings/ZigGlobalObject.cpp) in `void GlobalObject::visitChildrenImpl`
```c++#ZigGlobalObject.cpp
void GlobalObject::finishCreation(VM& vm) {
// ...
m_JSStatsBigIntClassStructure.initLater(
[](LazyClassStructure::Initializer& init) {
// Call the function to initialize our class structure.
Bun::initJSBigIntStatsClassStructure(init);
});
```
Then, implement the function that creates the structure:
```c++
void setupX509CertificateClassStructure(LazyClassStructure::Initializer& init)
{
auto* prototypeStructure = JSX509CertificatePrototype::createStructure(init.vm, init.global, init.global->objectPrototype());
auto* prototype = JSX509CertificatePrototype::create(init.vm, init.global, prototypeStructure);
auto* constructorStructure = JSX509CertificateConstructor::createStructure(init.vm, init.global, init.global->functionPrototype());
auto* constructor = JSX509CertificateConstructor::create(init.vm, init.global, constructorStructure, prototype);
auto* structure = JSX509Certificate::createStructure(init.vm, init.global, prototype);
init.setPrototype(prototype);
init.setStructure(structure);
init.setConstructor(constructor);
}
```
If there's only a class, use `JSC::LazyProperty<JSGlobalObject, Structure>` instead of `JSC::LazyClassStructure`:
1. Add the `JSC::LazyProperty<JSGlobalObject, Structure>` to @ZigGlobalObject.h
2. Initialize the class structure in @ZigGlobalObject.cpp in `void GlobalObject::finishCreation(VM& vm)`
3. Visit the lazy property in visitChildren in @ZigGlobalObject.cpp in `void GlobalObject::visitChildrenImpl`
void GlobalObject::finishCreation(VM& vm) {
// ...
this.m_myLazyProperty.initLater([](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::Structure>::Initializer& init) {
init.set(Bun::initMyStructure(init.vm, reinterpret_cast<Zig::GlobalObject\*>(init.owner)));
});
```
Then, implement the function that creates the structure:
```c++
Structure* setupX509CertificateStructure(JSC::VM &vm, Zig::GlobalObject* globalObject)
{
// If there is a prototype:
auto* prototypeStructure = JSX509CertificatePrototype::createStructure(init.vm, init.global, init.global->objectPrototype());
auto* prototype = JSX509CertificatePrototype::create(init.vm, init.global, prototypeStructure);
// If there is no prototype or it only has
auto* structure = JSX509Certificate::createStructure(init.vm, init.global, prototype);
init.setPrototype(prototype);
init.setStructure(structure);
init.setConstructor(constructor);
}
```
Then, use the structure by calling `globalObject.m_myStructureName.get(globalObject)`
```C++
JSC_DEFINE_HOST_FUNCTION(x509CertificateConstructorConstruct, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (!callFrame->argumentCount()) {
Bun::throwError(globalObject, scope, ErrorCode::ERR_MISSING_ARGS, "X509Certificate constructor requires at least one argument"_s);
return {};
}
JSValue arg = callFrame->uncheckedArgument(0);
if (!arg.isCell()) {
Bun::throwError(globalObject, scope, ErrorCode::ERR_INVALID_ARG_TYPE, "X509Certificate constructor argument must be a Buffer, TypedArray, or string"_s);
return {};
}
auto* zigGlobalObject = defaultGlobalObject(globalObject);
Structure* structure = zigGlobalObject->m_JSX509CertificateClassStructure.get(zigGlobalObject);
JSValue newTarget = callFrame->newTarget();
if (UNLIKELY(zigGlobalObject->m_JSX509CertificateClassStructure.constructor(zigGlobalObject) != newTarget)) {
auto scope = DECLARE_THROW_SCOPE(vm);
if (!newTarget) {
throwTypeError(globalObject, scope, "Class constructor X509Certificate cannot be invoked without 'new'"_s);
return {};
}
auto* functionGlobalObject = defaultGlobalObject(getFunctionRealm(globalObject, newTarget.getObject()));
RETURN_IF_EXCEPTION(scope, {});
structure = InternalFunction::createSubclassStructure(globalObject, newTarget.getObject(), functionGlobalObject->NodeVMScriptStructure());
RETURN_IF_EXCEPTION(scope, {});
}
return JSValue::encode(createX509Certificate(vm, globalObject, structure, arg));
}
```
### Expose to Zig
To expose the constructor to zig:
```c++
extern "C" JSC::EncodedJSValue Bun__JSBigIntStatsObjectConstructor(Zig::GlobalObject* globalobject)
{
return JSValue::encode(globalobject->m_JSStatsBigIntClassStructure.constructor(globalobject));
}
```
Zig:
```zig
extern "c" fn Bun__JSBigIntStatsObjectConstructor(*JSC.JSGlobalObject) JSC.JSValue;
pub const getBigIntStatsConstructor = Bun__JSBigIntStatsObjectConstructor;
```
To create an object (instance) of a JS class defined in C++ from Zig, follow the \_\_toJS convention like this:
```c++
// X509* is whatever we need to create the object
extern "C" EncodedJSValue Bun__X509__toJS(Zig::GlobalObject* globalObject, X509* cert)
{
// ... implementation details
auto* structure = globalObject->m_JSX509CertificateClassStructure.get(globalObject);
return JSValue::encode(JSX509Certificate::create(globalObject->vm(), structure, globalObject, WTFMove(cert)));
}
```
And from Zig:
```zig
const X509 = opaque {
// ... class
extern fn Bun__X509__toJS(*JSC.JSGlobalObject, *X509) JSC.JSValue;
pub fn toJS(this: *X509, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
return Bun__X509__toJS(globalObject, this);
}
};
```

View File

@@ -1,203 +0,0 @@
# Registering Functions, Objects, and Modules in Bun
This guide documents the process of adding new functionality to the Bun global object and runtime.
## Overview
Bun's architecture exposes functionality to JavaScript through a set of carefully registered functions, objects, and modules. Most core functionality is implemented in Zig, with JavaScript bindings that make these features accessible to users.
There are several key ways to expose functionality in Bun:
1. **Global Functions**: Direct methods on the `Bun` object (e.g., `Bun.serve()`)
2. **Getter Properties**: Lazily initialized properties on the `Bun` object (e.g., `Bun.sqlite`)
3. **Constructor Classes**: Classes available through the `Bun` object (e.g., `Bun.ValkeyClient`)
4. **Global Modules**: Modules that can be imported directly (e.g., `import {X} from "bun:*"`)
## The Registration Process
Adding new functionality to Bun involves several coordinated steps across multiple files:
### 1. Implement the Core Functionality in Zig
First, implement your feature in Zig, typically in its own directory in `src/`. Examples:
- `src/valkey/` for Redis/Valkey client
- `src/semver/` for SemVer functionality
- `src/smtp/` for SMTP client
### 2. Create JavaScript Bindings
Create bindings that expose your Zig functionality to JavaScript:
- Create a class definition file (e.g., `js_bindings.classes.ts`) to define the JavaScript interface
- Implement `JSYourFeature` struct in a file like `js_your_feature.zig`
Example from a class definition file:
```typescript
// Example from a .classes.ts file
import { define } from "../../codegen/class-definitions";
export default [
define({
name: "YourFeature",
construct: true,
finalize: true,
hasPendingActivity: true,
memoryCost: true,
klass: {},
JSType: "0b11101110",
proto: {
yourMethod: {
fn: "yourZigMethod",
length: 1,
},
property: {
getter: "getProperty",
},
},
values: ["cachedValues"],
}),
];
```
### 3. Register with BunObject in `src/bun.js/bindings/BunObject+exports.h`
Add an entry to the `FOR_EACH_GETTER` macro:
```c
// In BunObject+exports.h
#define FOR_EACH_GETTER(macro) \
macro(CSRF) \
macro(CryptoHasher) \
... \
macro(YourFeature) \
```
### 4. Create a Getter Function in `src/bun.js/api/BunObject.zig`
Implement a getter function in `BunObject.zig` that returns your feature:
```zig
// In BunObject.zig
pub const YourFeature = toJSGetter(Bun.getYourFeatureConstructor);
// In the exportAll() function:
@export(&BunObject.YourFeature, .{ .name = getterName("YourFeature") });
```
### 5. Implement the Getter Function in a Relevant Zig File
Implement the function that creates your object:
```zig
// In your main module file (e.g., src/your_feature/your_feature.zig)
pub fn getYourFeatureConstructor(globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject) JSC.JSValue {
return JSC.API.YourFeature.getConstructor(globalThis);
}
```
### 6. Add to Build System
Ensure your files are included in the build system by adding them to the appropriate targets.
## Example: Adding a New Module
Here's a comprehensive example of adding a hypothetical SMTP module:
1. Create implementation files in `src/smtp/`:
- `index.zig`: Main entry point that exports everything
- `SmtpClient.zig`: Core SMTP client implementation
- `js_smtp.zig`: JavaScript bindings
- `js_bindings.classes.ts`: Class definition
2. Define your JS class in `js_bindings.classes.ts`:
```typescript
import { define } from "../../codegen/class-definitions";
export default [
define({
name: "EmailClient",
construct: true,
finalize: true,
hasPendingActivity: true,
configurable: false,
memoryCost: true,
klass: {},
JSType: "0b11101110",
proto: {
send: {
fn: "send",
length: 1,
},
verify: {
fn: "verify",
length: 0,
},
close: {
fn: "close",
length: 0,
},
},
values: ["connectionPromise"],
}),
];
```
3. Add getter to `BunObject+exports.h`:
```c
#define FOR_EACH_GETTER(macro) \
macro(CSRF) \
... \
macro(SMTP) \
```
4. Add getter function to `BunObject.zig`:
```zig
pub const SMTP = toJSGetter(Bun.getSmtpConstructor);
// In exportAll:
@export(&BunObject.SMTP, .{ .name = getterName("SMTP") });
```
5. Implement getter in your module:
```zig
pub fn getSmtpConstructor(globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject) JSC.JSValue {
return JSC.API.JSEmailClient.getConstructor(globalThis);
}
```
## Best Practices
1. **Follow Naming Conventions**: Align your naming with existing patterns
2. **Reference Existing Modules**: Study similar modules like Valkey or S3Client for guidance
3. **Memory Management**: Be careful with memory management and reference counting
4. **Error Handling**: Use `bun.JSError!JSValue` for proper error propagation
5. **Documentation**: Add JSDoc comments to your JavaScript bindings
6. **Testing**: Add tests for your new functionality
## Common Gotchas
- Be sure to handle reference counting properly with `ref()`/`deref()`
- Always implement proper cleanup in `deinit()` and `finalize()`
- For network operations, manage socket lifetimes correctly
- Use `JSC.Codegen` correctly to generate necessary binding code
## Related Files
- `src/bun.js/bindings/BunObject+exports.h`: Registration of getters and functions
- `src/bun.js/api/BunObject.zig`: Implementation of getters and object creation
- `src/bun.js/api/BunObject.classes.ts`: Class definitions
- `.cursor/rules/zig-javascriptcore-classes.mdc`: More details on class bindings
## Additional Resources
For more detailed information on specific topics:
- See `zig-javascriptcore-classes.mdc` for details on creating JS class bindings
- Review existing modules like `valkey`, `sqlite`, or `s3` for real-world examples

View File

@@ -1,91 +0,0 @@
---
description: Writing tests for Bun
globs:
---
# Writing tests for Bun
## Where tests are found
You'll find all of Bun's tests in the `test/` directory.
* `test/`
* `cli/` - CLI command tests, like `bun install` or `bun init`
* `js/` - JavaScript & TypeScript tests
* `bun/` - `Bun` APIs tests, separated by category, for example: `glob/` for `Bun.Glob` tests
* `node/` - Node.js module tests, separated by module, for example: `assert/` for `node:assert` tests
* `test/` - Vendored Node.js tests, taken from the Node.js repository (does not conform to Bun's test style)
* `web/` - Web API tests, separated by category, for example: `fetch/` for `Request` and `Response` tests
* `third_party/` - npm package tests, to validate that basic usage works in Bun
* `napi/` - N-API tests
* `v8/` - V8 C++ API tests
* `bundler/` - Bundler, transpiler, CSS, and `bun build` tests
* `regression/issue/[number]` - Regression tests, always make one when fixing a particular issue
## How tests are written
Bun's tests are written as JavaScript and TypeScript files with the Jest-style APIs, like `test`, `describe`, and `expect`. They are tested using Bun's own test runner, `bun test`.
```js
import { describe, test, expect } from "bun:test";
import assert, { AssertionError } from "assert";
describe("assert(expr)", () => {
test.each([true, 1, "foo"])(`assert(%p) does not throw`, expr => {
expect(() => assert(expr)).not.toThrow();
});
test.each([false, 0, "", null, undefined])(`assert(%p) throws`, expr => {
expect(() => assert(expr)).toThrow(AssertionError);
});
});
```
## Testing conventions
* See `test/harness.ts` for common test utilities and helpers
* Be rigorous and test for edge-cases and unexpected inputs
* Use data-driven tests, e.g. `test.each`, to reduce boilerplate when possible
* When you need to test Bun as a CLI, use the following pattern:
```js
import { test, expect } from "bun:test";
import { spawn } from "bun";
import { bunExe, bunEnv } from "harness";
test("bun --version", async () => {
const { exited, stdout: stdoutStream, stderr: stderrStream } = spawn({
cmd: [bunExe(), "--version"],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [ exitCode, stdout, stderr ] = await Promise.all([
exited,
new Response(stdoutStream).text(),
new Response(stderrStream).text(),
]);
expect({ exitCode, stdout, stderr }).toMatchObject({
exitCode: 0,
stdout: expect.stringContaining(Bun.version),
stderr: "",
});
});
```
## Before writing a test
* If you are fixing a bug, write the test first and make sure it fails (as expected) with the canary version of Bun
* If you are fixing a Node.js compatibility bug, create a throw-away snippet of code and test that it works as you expect in Node.js, then that it fails (as expected) with the canary version of Bun
* When the expected behaviour is ambigious, defer to matching what happens in Node.js
* Always attempt to find related tests in an existing test file before creating a new test file

View File

@@ -1,509 +0,0 @@
---
description: How Zig works with JavaScriptCore bindings generator
globs:
alwaysApply: false
---
# Bun's JavaScriptCore Class Bindings Generator
This document explains how Bun's class bindings generator works to bridge Zig and JavaScript code through JavaScriptCore (JSC).
## Architecture Overview
Bun's binding system creates a seamless bridge between JavaScript and Zig, allowing Zig implementations to be exposed as JavaScript classes. The system has several key components:
1. **Zig Implementation** (.zig files)
2. **JavaScript Interface Definition** (.classes.ts files)
3. **Generated Code** (C++/Zig files that connect everything)
## Class Definition Files
### JavaScript Interface (.classes.ts)
The `.classes.ts` files define the JavaScript API using a declarative approach:
```typescript
// Example: encoding.classes.ts
define({
name: "TextDecoder",
constructor: true,
JSType: "object",
finalize: true,
proto: {
decode: {
// Function definition
args: 1,
},
encoding: {
// Getter with caching
getter: true,
cache: true,
},
fatal: {
// Read-only property
getter: true,
},
ignoreBOM: {
// Read-only property
getter: true,
},
},
});
```
Each class definition specifies:
- The class name
- Whether it has a constructor
- JavaScript type (object, function, etc.)
- Properties and methods in the `proto` field
- Caching strategy for properties
- Finalization requirements
### Zig Implementation (.zig)
The Zig files implement the native functionality:
```zig
// Example: TextDecoder.zig
pub const TextDecoder = struct {
// Expose generated bindings as `js` namespace with trait conversion methods
pub const js = JSC.Codegen.JSTextDecoder;
pub const toJS = js.toJS;
pub const fromJS = js.fromJS;
pub const fromJSDirect = js.fromJSDirect;
// Internal state
encoding: []const u8,
fatal: bool,
ignoreBOM: bool,
// Constructor implementation - note use of globalObject
pub fn constructor(
globalObject: *JSGlobalObject,
callFrame: *JSC.CallFrame,
) bun.JSError!*TextDecoder {
// Implementation
return bun.new(TextDecoder, .{
// Fields
});
}
// Prototype methods - note return type includes JSError
pub fn decode(
this: *TextDecoder,
globalObject: *JSGlobalObject,
callFrame: *JSC.CallFrame,
) bun.JSError!JSC.JSValue {
// Implementation
}
// Getters
pub fn getEncoding(this: *TextDecoder, globalObject: *JSGlobalObject) JSC.JSValue {
return JSC.JSValue.createStringFromUTF8(globalObject, this.encoding);
}
pub fn getFatal(this: *TextDecoder, globalObject: *JSGlobalObject) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.fatal);
}
// Cleanup - note standard pattern of using deinit/deref
fn deinit(this: *TextDecoder) void {
// Release any retained resources
// Free the pointer at the end.
bun.destroy(this);
}
// Finalize - called by JS garbage collector. This should call deinit, or deref if reference counted.
pub fn finalize(this: *TextDecoder) void {
this.deinit();
}
};
```
Key components in the Zig file:
- The struct containing native state
- `pub const js = JSC.Codegen.JS<ClassName>` to include generated code
- Constructor and methods using `bun.JSError!JSValue` return type for proper error handling
- Consistent use of `globalObject` parameter name instead of `ctx`
- Methods matching the JavaScript interface
- Getters/setters for properties
- Proper resource cleanup pattern with `deinit()` and `finalize()`
- Update `src/bun.js/bindings/generated_classes_list.zig` to include the new class
## Code Generation System
The binding generator produces C++ code that connects JavaScript and Zig:
1. **JSC Class Structure**: Creates C++ classes for the JS object, prototype, and constructor
2. **Memory Management**: Handles GC integration through JSC's WriteBarrier
3. **Method Binding**: Connects JS function calls to Zig implementations
4. **Type Conversion**: Converts between JS values and Zig types
5. **Property Caching**: Implements the caching system for properties
The generated C++ code includes:
- A JSC wrapper class (`JSTextDecoder`)
- A prototype class (`JSTextDecoderPrototype`)
- A constructor function (`JSTextDecoderConstructor`)
- Function bindings (`TextDecoderPrototype__decodeCallback`)
- Property getters/setters (`TextDecoderPrototype__encodingGetterWrap`)
## CallFrame Access
The `CallFrame` object provides access to JavaScript execution context:
```zig
pub fn decode(
this: *TextDecoder,
globalObject: *JSGlobalObject,
callFrame: *JSC.CallFrame
) bun.JSError!JSC.JSValue {
// Get arguments
const input = callFrame.argument(0);
const options = callFrame.argument(1);
// Get this value
const thisValue = callFrame.thisValue();
// Implementation with error handling
if (input.isUndefinedOrNull()) {
return globalObject.throw("Input cannot be null or undefined", .{});
}
// Return value or throw error
return JSC.JSValue.jsString(globalObject, "result");
}
```
CallFrame methods include:
- `argument(i)`: Get the i-th argument
- `argumentCount()`: Get the number of arguments
- `thisValue()`: Get the `this` value
- `callee()`: Get the function being called
## Property Caching and GC-Owned Values
The `cache: true` option in property definitions enables JSC's WriteBarrier to efficiently store values:
```typescript
encoding: {
getter: true,
cache: true, // Enable caching
}
```
### C++ Implementation
In the generated C++ code, caching uses JSC's WriteBarrier:
```cpp
JSC_DEFINE_CUSTOM_GETTER(TextDecoderPrototype__encodingGetterWrap, (...)) {
auto& vm = JSC::getVM(lexicalGlobalObject);
Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
auto throwScope = DECLARE_THROW_SCOPE(vm);
JSTextDecoder* thisObject = jsCast<JSTextDecoder*>(JSValue::decode(encodedThisValue));
JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
// Check for cached value and return if present
if (JSValue cachedValue = thisObject->m_encoding.get())
return JSValue::encode(cachedValue);
// Get value from Zig implementation
JSC::JSValue result = JSC::JSValue::decode(
TextDecoderPrototype__getEncoding(thisObject->wrapped(), globalObject)
);
RETURN_IF_EXCEPTION(throwScope, {});
// Store in cache for future access
thisObject->m_encoding.set(vm, thisObject, result);
RELEASE_AND_RETURN(throwScope, JSValue::encode(result));
}
```
### Zig Accessor Functions
For each cached property, the generator creates Zig accessor functions that allow Zig code to work with these GC-owned values:
```zig
// External function declarations
extern fn TextDecoderPrototype__encodingSetCachedValue(JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) callconv(JSC.conv) void;
extern fn TextDecoderPrototype__encodingGetCachedValue(JSC.JSValue) callconv(JSC.conv) JSC.JSValue;
/// `TextDecoder.encoding` setter
/// This value will be visited by the garbage collector.
pub fn encodingSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void {
JSC.markBinding(@src());
TextDecoderPrototype__encodingSetCachedValue(thisValue, globalObject, value);
}
/// `TextDecoder.encoding` getter
/// This value will be visited by the garbage collector.
pub fn encodingGetCached(thisValue: JSC.JSValue) ?JSC.JSValue {
JSC.markBinding(@src());
const result = TextDecoderPrototype__encodingGetCachedValue(thisValue);
if (result == .zero)
return null;
return result;
}
```
### Benefits of GC-Owned Values
This system provides several key benefits:
1. **Automatic Memory Management**: The JavaScriptCore GC tracks and manages these values
2. **Proper Garbage Collection**: The WriteBarrier ensures values are properly visited during GC
3. **Consistent Access**: Zig code can easily get/set these cached JS values
4. **Performance**: Cached values avoid repeated computation or serialization
### Use Cases
GC-owned cached values are particularly useful for:
1. **Computed Properties**: Store expensive computation results
2. **Lazily Created Objects**: Create objects only when needed, then cache them
3. **References to Other Objects**: Store references to other JS objects that need GC tracking
4. **Memoization**: Cache results based on input parameters
The WriteBarrier mechanism ensures that any JS values stored in this way are properly tracked by the garbage collector.
## Memory Management and Finalization
The binding system handles memory management across the JavaScript/Zig boundary:
1. **Object Creation**: JavaScript `new TextDecoder()` creates both a JS wrapper and a Zig struct
2. **Reference Tracking**: JSC's GC tracks all JS references to the object
3. **Finalization**: When the JS object is collected, the finalizer releases Zig resources
Bun uses a consistent pattern for resource cleanup:
```zig
// Resource cleanup method - separate from finalization
pub fn deinit(this: *TextDecoder) void {
// Release resources like strings
this._encoding.deref(); // String deref pattern
// Free any buffers
if (this.buffer) |buffer| {
bun.default_allocator.free(buffer);
}
}
// Called by the GC when object is collected
pub fn finalize(this: *TextDecoder) void {
JSC.markBinding(@src()); // For debugging
this.deinit(); // Clean up resources
bun.default_allocator.destroy(this); // Free the object itself
}
```
Some objects that hold references to other JS objects use `.deref()` instead:
```zig
pub fn finalize(this: *SocketAddress) void {
JSC.markBinding(@src());
this._presentation.deref(); // Release references
this.destroy();
}
```
## Error Handling with JSError
Bun uses `bun.JSError!JSValue` return type for proper error handling:
```zig
pub fn decode(
this: *TextDecoder,
globalObject: *JSGlobalObject,
callFrame: *JSC.CallFrame
) bun.JSError!JSC.JSValue {
// Throwing an error
if (callFrame.argumentCount() < 1) {
return globalObject.throw("Missing required argument", .{});
}
// Or returning a success value
return JSC.JSValue.jsString(globalObject, "Success!");
}
```
This pattern allows Zig functions to:
1. Return JavaScript values on success
2. Throw JavaScript exceptions on error
3. Propagate errors automatically through the call stack
## Type Safety and Error Handling
The binding system includes robust error handling:
```cpp
// Example of type checking in generated code
JSTextDecoder* thisObject = jsDynamicCast<JSTextDecoder*>(callFrame->thisValue());
if (UNLIKELY(!thisObject)) {
scope.throwException(lexicalGlobalObject,
Bun::createInvalidThisError(lexicalGlobalObject, callFrame->thisValue(), "TextDecoder"_s));
return {};
}
```
## Prototypal Inheritance
The binding system creates proper JavaScript prototype chains:
1. **Constructor**: JSTextDecoderConstructor with standard .prototype property
2. **Prototype**: JSTextDecoderPrototype with methods and properties
3. **Instances**: Each JSTextDecoder instance with **proto** pointing to prototype
This ensures JavaScript inheritance works as expected:
```cpp
// From generated code
void JSTextDecoderConstructor::finishCreation(VM& vm, JSC::JSGlobalObject* globalObject, JSTextDecoderPrototype* prototype)
{
Base::finishCreation(vm, 0, "TextDecoder"_s, PropertyAdditionMode::WithoutStructureTransition);
// Set up the prototype chain
putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
ASSERT(inherits(info()));
}
```
## Performance Considerations
The binding system is optimized for performance:
1. **Direct Pointer Access**: JavaScript objects maintain a direct pointer to Zig objects
2. **Property Caching**: WriteBarrier caching avoids repeated native calls for stable properties
3. **Memory Management**: JSC garbage collection integrated with Zig memory management
4. **Type Conversion**: Fast paths for common JavaScript/Zig type conversions
## Creating a New Class Binding
To create a new class binding in Bun:
1. **Define the class interface** in a `.classes.ts` file:
```typescript
define({
name: "MyClass",
constructor: true,
finalize: true,
proto: {
myMethod: {
args: 1,
},
myProperty: {
getter: true,
cache: true,
},
},
});
```
2. **Implement the native functionality** in a `.zig` file:
```zig
pub const MyClass = struct {
// Generated bindings
pub const js = JSC.Codegen.JSMyClass;
pub const toJS = js.toJS;
pub const fromJS = js.fromJS;
pub const fromJSDirect = js.fromJSDirect;
// State
value: []const u8,
pub const new = bun.TrivialNew(@This());
// Constructor
pub fn constructor(
globalObject: *JSGlobalObject,
callFrame: *JSC.CallFrame,
) bun.JSError!*MyClass {
const arg = callFrame.argument(0);
// Implementation
}
// Method
pub fn myMethod(
this: *MyClass,
globalObject: *JSGlobalObject,
callFrame: *JSC.CallFrame,
) bun.JSError!JSC.JSValue {
// Implementation
}
// Getter
pub fn getMyProperty(this: *MyClass, globalObject: *JSGlobalObject) JSC.JSValue {
return JSC.JSValue.jsString(globalObject, this.value);
}
// Resource cleanup
pub fn deinit(this: *MyClass) void {
// Clean up resources
}
pub fn finalize(this: *MyClass) void {
this.deinit();
bun.destroy(this);
}
};
```
3. **The binding generator** creates all necessary C++ and Zig glue code to connect JavaScript and Zig, including:
- C++ class definitions
- Method and property bindings
- Memory management utilities
- GC integration code
## Generated Code Structure
The binding generator produces several components:
### 1. C++ Classes
For each Zig class, the system generates:
- **JS<Class>**: Main wrapper that holds a pointer to the Zig object (`JSTextDecoder`)
- **JS<Class>Prototype**: Contains methods and properties (`JSTextDecoderPrototype`)
- **JS<Class>Constructor**: Implementation of the JavaScript constructor (`JSTextDecoderConstructor`)
### 2. C++ Methods and Properties
- **Method Callbacks**: `TextDecoderPrototype__decodeCallback`
- **Property Getters/Setters**: `TextDecoderPrototype__encodingGetterWrap`
- **Initialization Functions**: `finishCreation` methods for setting up the class
### 3. Zig Bindings
- **External Function Declarations**:
```zig
extern fn TextDecoderPrototype__decode(*TextDecoder, *JSC.JSGlobalObject, *JSC.CallFrame) callconv(JSC.conv) JSC.EncodedJSValue;
```
- **Cached Value Accessors**:
```zig
pub fn encodingGetCached(thisValue: JSC.JSValue) ?JSC.JSValue { ... }
pub fn encodingSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { ... }
```
- **Constructor Helpers**:
```zig
pub fn create(globalObject: *JSC.JSGlobalObject) bun.JSError!JSC.JSValue { ... }
```
### 4. GC Integration
- **Memory Cost Calculation**: `estimatedSize` method
- **Child Visitor Methods**: `visitChildrenImpl` and `visitAdditionalChildren`
- **Heap Analysis**: `analyzeHeap` for debugging memory issues
This architecture makes it possible to implement high-performance native functionality in Zig while exposing a clean, idiomatic JavaScript API to users.

View File

@@ -1,58 +0,0 @@
name: Codex Test Sync
on:
pull_request:
types: [labeled, opened]
env:
BUN_VERSION: "1.2.15"
jobs:
sync-node-tests:
runs-on: ubuntu-latest
if: |
(github.event.action == 'labeled' && github.event.label.name == 'codex') ||
(github.event.action == 'opened' && contains(github.event.pull_request.labels.*.name, 'codex')) ||
contains(github.head_ref, 'codex')
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: ${{ env.BUN_VERSION }}
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v44
with:
files: |
test/js/node/test/parallel/**/*.{js,mjs,ts}
test/js/node/test/sequential/**/*.{js,mjs,ts}
- name: Sync tests
if: steps.changed-files.outputs.any_changed == 'true'
shell: bash
run: |
echo "Changed test files:"
echo "${{ steps.changed-files.outputs.all_changed_files }}"
# Process each changed test file
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
# Extract test name from file path
test_name=$(basename "$file" | sed 's/\.[^.]*$//')
echo "Syncing test: $test_name"
bun node:test:cp "$test_name"
done
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "Sync Node.js tests with upstream"

2
.gitignore vendored
View File

@@ -1,4 +1,5 @@
.claude/settings.local.json
.direnv
.DS_Store
.env
.envrc
@@ -9,6 +10,7 @@
.ninja_deps
.ninja_log
.npm
.npmrc
.npm.gz
.parcel-cache
.swcrc

View File

@@ -10,6 +10,8 @@ This is the Bun repository - an all-in-one JavaScript runtime & toolkit designed
- **Run tests with your debug build**: `bun bd test <test-file>`
- **CRITICAL**: Never use `bun test` directly - it won't include your changes
- **Run any command with debug build**: `bun bd <command>`
- **Run with JavaScript exception scope verification**: `BUN_JSC_validateExceptionChecks=1
BUN_JSC_dumpSimulatedThrows=1 bun bd <command>`
Tip: Bun is already installed and in $PATH. The `bd` subcommand is a package.json script.
@@ -209,3 +211,24 @@ Built-in JavaScript modules use special syntax and are organized as:
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.
## Debugging CI Failures
Use `scripts/buildkite-failures.ts` to fetch and analyze CI build failures:
```bash
# View failures for current branch
bun run scripts/buildkite-failures.ts
# View failures for a specific build number
bun run scripts/buildkite-failures.ts 35051
# View failures for a GitHub PR
bun run scripts/buildkite-failures.ts #26173
bun run scripts/buildkite-failures.ts https://github.com/oven-sh/bun/pull/26173
# Wait for build to complete (polls every 10s until pass/fail)
bun run scripts/buildkite-failures.ts --wait
```
The script fetches logs from BuildKite's public API and saves complete logs to `/tmp/bun-build-{number}-{platform}-{step}.log`. It displays a summary of errors and the file path for each failed job. Use `--wait` to poll continuously until the build completes or fails.

View File

@@ -47,15 +47,7 @@ include(SetupEsbuild)
include(SetupZig)
include(SetupRust)
find_program(SCCACHE_PROGRAM sccache)
if(SCCACHE_PROGRAM AND NOT DEFINED ENV{NO_SCCACHE})
include(SetupSccache)
else()
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
include(SetupCcache)
endif()
endif()
include(SetupCcache)
# Generate dependency versions header
include(GenerateDependencyVersions)

View File

@@ -23,7 +23,7 @@ Using your system's package manager, install Bun's dependencies:
{% codetabs group="os" %}
```bash#macOS (Homebrew)
$ brew install automake cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rust ruby sccache
$ brew install automake ccache cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rust ruby
```
```bash#Ubuntu/Debian
@@ -65,43 +65,28 @@ $ brew install bun
{% /codetabs %}
### Optional: Install `sccache`
### Optional: Install `ccache`
sccache is used to cache compilation artifacts, significantly speeding up builds. It must be installed with S3 support:
ccache is used to cache compilation artifacts, significantly speeding up builds:
```bash
# For macOS
$ brew install sccache
$ brew install ccache
# For Linux. Note that the version in your package manager may not have S3 support.
$ cargo install sccache --features=s3
# For Ubuntu/Debian
$ sudo apt install ccache
# For Arch
$ sudo pacman -S ccache
# For Fedora
$ sudo dnf install ccache
# For openSUSE
$ sudo zypper install ccache
```
This will install `sccache` with S3 support. Our build scripts will automatically detect and use `sccache` with our shared S3 cache. **Note**: Not all versions of `sccache` are compiled with S3 support, hence we recommend installing it via `cargo`.
#### Registering AWS Credentials for `sccache` (Core Developers Only)
Core developers have write access to the shared S3 cache. To enable write access, you must log in with AWS credentials. The easiest way to do this is to use the [`aws` CLI](https://aws.amazon.com/cli/) and invoke [`aws configure` to provide your AWS security info](https://docs.aws.amazon.com/cli/latest/reference/configure/).
The `cmake` scripts should automatically detect your AWS credentials from the environment or the `~/.aws/credentials` file.
<details>
<summary>Logging in to the `aws` CLI</summary>
1. Install the AWS CLI by following [the official guide](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html).
2. Log in to your AWS account console. A team member should provide you with your credentials.
3. Click your name in the top right > Security credentials.
4. Scroll to "Access keys" and create a new access key.
5. Run `aws configure` in your terminal and provide the access key ID and secret access key when prompted.
</details>
<details>
<summary>Common Issues You May Encounter</summary>
- To confirm that the cache is being used, you can use the `sccache --show-stats` command right after a build. This will expose very useful statistics, including cache hits/misses.
- If you have multiple AWS profiles configured, ensure that the correct profile is set in the `AWS_PROFILE` environment variable.
- `sccache` follows a server-client model. If you run into weird issues where `sccache` refuses to use S3, even though you have AWS credentials configured, try killing any running `sccache` servers with `sccache --stop-server` and then re-running the build.
</details>
Our build scripts will automatically detect and use `ccache` if available. You can check cache statistics with `ccache --show-stats`.
## Install LLVM

2
LATEST
View File

@@ -1 +1 @@
1.3.4
1.3.6

View File

@@ -23,7 +23,9 @@
"react-dom": "^18.3.1",
"string-width": "7.1.0",
"strip-ansi": "^7.1.0",
"tar": "^7.4.3",
"tinycolor2": "^1.6.0",
"wrap-ansi": "^9.0.0",
"zx": "^7.2.3",
},
"devDependencies": {
@@ -108,6 +110,8 @@
"@fastify/proxy-addr": ["@fastify/proxy-addr@5.0.0", "", { "dependencies": { "@fastify/forwarded": "^3.0.0", "ipaddr.js": "^2.1.0" } }, "sha512-37qVVA1qZ5sgH7KpHkkC4z9SK6StIsIcOmpjvMPXNb3vx2GQxhZocogVYbr2PbbeLCQxYIPDok307xEvRZOzGA=="],
"@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.1.1", "", { "dependencies": { "@jridgewell/set-array": "^1.0.0", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.0", "", {}, "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="],
@@ -166,7 +170,7 @@
"ansi-regex": ["ansi-regex@6.0.1", "", {}, "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA=="],
"ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
"ansi-styles": ["ansi-styles@6.2.3", "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/ansi-styles/-/ansi-styles-6.2.3.tgz", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
"atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="],
@@ -182,6 +186,8 @@
"chalk": ["chalk@5.3.0", "", {}, "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w=="],
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
"color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
@@ -362,6 +368,10 @@
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
"minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="],
"mitata": ["mitata@1.0.25", "", {}, "sha512-0v5qZtVW5vwj9FDvYfraR31BMDcRLkhSFWPTLaxx/Z3/EvScfVtAAWtMI2ArIbBcwh7P86dXh0lQWKiXQPlwYA=="],
"ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
@@ -458,6 +468,8 @@
"supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="],
"tar": ["tar@7.5.2", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg=="],
"thread-stream": ["thread-stream@3.1.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A=="],
"through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="],
@@ -482,7 +494,9 @@
"which": ["which@3.0.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg=="],
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
"wrap-ansi": ["wrap-ansi@9.0.2", "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/wrap-ansi/-/wrap-ansi-9.0.2.tgz", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
"yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
"yaml": ["yaml@2.3.4", "", {}, "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA=="],
@@ -492,8 +506,6 @@
"@babel/highlight/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
"ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
"avvio/fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
"cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
@@ -502,8 +514,14 @@
"light-my-request/process-warning": ["process-warning@4.0.1", "", {}, "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q=="],
"lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
"ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
"@babel/highlight/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
"@babel/highlight/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
"@babel/highlight/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
}
}

View File

@@ -18,7 +18,9 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"string-width": "7.1.0",
"wrap-ansi": "^9.0.0",
"strip-ansi": "^7.1.0",
"tar": "^7.4.3",
"tinycolor2": "^1.6.0",
"zx": "^7.2.3"
},

View File

@@ -14,3 +14,4 @@ export function run(opts = {}) {
export const bench = Mitata.bench;
export const group = Mitata.group;
export const summary = Mitata.summary;

477
bench/snippets/archive.mjs Normal file
View File

@@ -0,0 +1,477 @@
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { Pack, Unpack } from "tar";
import { bench, group, run } from "../runner.mjs";
// Check if Bun.Archive is available
const hasBunArchive = typeof Bun !== "undefined" && typeof Bun.Archive !== "undefined";
// Test data sizes
const smallContent = "Hello, World!";
const mediumContent = Buffer.alloc(10 * 1024, "x").toString(); // 10KB
const largeContent = Buffer.alloc(100 * 1024, "x").toString(); // 100KB
// Create test files for node-tar (it reads from filesystem)
const setupDir = mkdtempSync(join(tmpdir(), "archive-bench-setup-"));
function setupNodeTarFiles(prefix, files) {
const dir = join(setupDir, prefix);
mkdirSync(dir, { recursive: true });
for (const [name, content] of Object.entries(files)) {
const filePath = join(dir, name);
const fileDir = join(filePath, "..");
mkdirSync(fileDir, { recursive: true });
writeFileSync(filePath, content);
}
return dir;
}
// Setup directories for different test cases
const smallFilesDir = setupNodeTarFiles("small", {
"file1.txt": smallContent,
"file2.txt": smallContent,
"file3.txt": smallContent,
});
const mediumFilesDir = setupNodeTarFiles("medium", {
"file1.txt": mediumContent,
"file2.txt": mediumContent,
"file3.txt": mediumContent,
});
const largeFilesDir = setupNodeTarFiles("large", {
"file1.txt": largeContent,
"file2.txt": largeContent,
"file3.txt": largeContent,
});
const manyFilesEntries = {};
for (let i = 0; i < 100; i++) {
manyFilesEntries[`file${i}.txt`] = smallContent;
}
const manyFilesDir = setupNodeTarFiles("many", manyFilesEntries);
// Pre-create archives for extraction benchmarks
let smallTarGzBuffer, mediumTarGzBuffer, largeTarGzBuffer, manyFilesTarGzBuffer;
let smallTarBuffer, mediumTarBuffer, largeTarBuffer, manyFilesTarBuffer;
let smallBunArchiveGz, mediumBunArchiveGz, largeBunArchiveGz, manyFilesBunArchiveGz;
let smallBunArchive, mediumBunArchive, largeBunArchive, manyFilesBunArchive;
// Create tar buffer using node-tar (with optional gzip)
async function createNodeTarBuffer(cwd, files, gzip = false) {
return new Promise(resolve => {
const pack = new Pack({ cwd, gzip });
const bufs = [];
pack.on("data", chunk => bufs.push(chunk));
pack.on("end", () => resolve(Buffer.concat(bufs)));
for (const file of files) {
pack.add(file);
}
pack.end();
});
}
// Extract tar buffer using node-tar
async function extractNodeTarBuffer(buffer, cwd) {
return new Promise((resolve, reject) => {
const unpack = new Unpack({ cwd });
unpack.on("end", resolve);
unpack.on("error", reject);
unpack.end(buffer);
});
}
// Initialize gzipped archives
smallTarGzBuffer = await createNodeTarBuffer(smallFilesDir, ["file1.txt", "file2.txt", "file3.txt"], true);
mediumTarGzBuffer = await createNodeTarBuffer(mediumFilesDir, ["file1.txt", "file2.txt", "file3.txt"], true);
largeTarGzBuffer = await createNodeTarBuffer(largeFilesDir, ["file1.txt", "file2.txt", "file3.txt"], true);
manyFilesTarGzBuffer = await createNodeTarBuffer(manyFilesDir, Object.keys(manyFilesEntries), true);
// Initialize uncompressed archives
smallTarBuffer = await createNodeTarBuffer(smallFilesDir, ["file1.txt", "file2.txt", "file3.txt"], false);
mediumTarBuffer = await createNodeTarBuffer(mediumFilesDir, ["file1.txt", "file2.txt", "file3.txt"], false);
largeTarBuffer = await createNodeTarBuffer(largeFilesDir, ["file1.txt", "file2.txt", "file3.txt"], false);
manyFilesTarBuffer = await createNodeTarBuffer(manyFilesDir, Object.keys(manyFilesEntries), false);
const smallFiles = { "file1.txt": smallContent, "file2.txt": smallContent, "file3.txt": smallContent };
const mediumFiles = { "file1.txt": mediumContent, "file2.txt": mediumContent, "file3.txt": mediumContent };
const largeFiles = { "file1.txt": largeContent, "file2.txt": largeContent, "file3.txt": largeContent };
if (hasBunArchive) {
smallBunArchiveGz = await Bun.Archive.from(smallFiles).bytes("gzip");
mediumBunArchiveGz = await Bun.Archive.from(mediumFiles).bytes("gzip");
largeBunArchiveGz = await Bun.Archive.from(largeFiles).bytes("gzip");
manyFilesBunArchiveGz = await Bun.Archive.from(manyFilesEntries).bytes("gzip");
smallBunArchive = await Bun.Archive.from(smallFiles).bytes();
mediumBunArchive = await Bun.Archive.from(mediumFiles).bytes();
largeBunArchive = await Bun.Archive.from(largeFiles).bytes();
manyFilesBunArchive = await Bun.Archive.from(manyFilesEntries).bytes();
}
// Create reusable extraction directories (overwriting is fine)
const extractDirNodeTar = mkdtempSync(join(tmpdir(), "archive-bench-extract-node-"));
const extractDirBun = mkdtempSync(join(tmpdir(), "archive-bench-extract-bun-"));
const writeDirNodeTar = mkdtempSync(join(tmpdir(), "archive-bench-write-node-"));
const writeDirBun = mkdtempSync(join(tmpdir(), "archive-bench-write-bun-"));
// ============================================================================
// Create .tar (uncompressed) benchmarks
// ============================================================================
group("create .tar (3 small files)", () => {
bench("node-tar", async () => {
await createNodeTarBuffer(smallFilesDir, ["file1.txt", "file2.txt", "file3.txt"], false);
});
if (hasBunArchive) {
bench("Bun.Archive", async () => {
await Bun.Archive.from(smallFiles).bytes();
});
}
});
group("create .tar (3 x 100KB files)", () => {
bench("node-tar", async () => {
await createNodeTarBuffer(largeFilesDir, ["file1.txt", "file2.txt", "file3.txt"], false);
});
if (hasBunArchive) {
bench("Bun.Archive", async () => {
await Bun.Archive.from(largeFiles).bytes();
});
}
});
group("create .tar (100 small files)", () => {
bench("node-tar", async () => {
await createNodeTarBuffer(manyFilesDir, Object.keys(manyFilesEntries), false);
});
if (hasBunArchive) {
bench("Bun.Archive", async () => {
await Bun.Archive.from(manyFilesEntries).bytes();
});
}
});
// ============================================================================
// Create .tar.gz (compressed) benchmarks
// ============================================================================
group("create .tar.gz (3 small files)", () => {
bench("node-tar", async () => {
await createNodeTarBuffer(smallFilesDir, ["file1.txt", "file2.txt", "file3.txt"], true);
});
if (hasBunArchive) {
bench("Bun.Archive", async () => {
await Bun.Archive.from(smallFiles).bytes("gzip");
});
}
});
group("create .tar.gz (3 x 100KB files)", () => {
bench("node-tar", async () => {
await createNodeTarBuffer(largeFilesDir, ["file1.txt", "file2.txt", "file3.txt"], true);
});
if (hasBunArchive) {
bench("Bun.Archive", async () => {
await Bun.Archive.from(largeFiles).bytes("gzip");
});
}
});
group("create .tar.gz (100 small files)", () => {
bench("node-tar", async () => {
await createNodeTarBuffer(manyFilesDir, Object.keys(manyFilesEntries), true);
});
if (hasBunArchive) {
bench("Bun.Archive", async () => {
await Bun.Archive.from(manyFilesEntries).bytes("gzip");
});
}
});
// ============================================================================
// Extract .tar (uncompressed) benchmarks
// ============================================================================
group("extract .tar (3 small files)", () => {
bench("node-tar", async () => {
await extractNodeTarBuffer(smallTarBuffer, extractDirNodeTar);
});
if (hasBunArchive) {
bench("Bun.Archive", async () => {
await Bun.Archive.from(smallBunArchive).extract(extractDirBun);
});
}
});
group("extract .tar (3 x 100KB files)", () => {
bench("node-tar", async () => {
await extractNodeTarBuffer(largeTarBuffer, extractDirNodeTar);
});
if (hasBunArchive) {
bench("Bun.Archive", async () => {
await Bun.Archive.from(largeBunArchive).extract(extractDirBun);
});
}
});
group("extract .tar (100 small files)", () => {
bench("node-tar", async () => {
await extractNodeTarBuffer(manyFilesTarBuffer, extractDirNodeTar);
});
if (hasBunArchive) {
bench("Bun.Archive", async () => {
await Bun.Archive.from(manyFilesBunArchive).extract(extractDirBun);
});
}
});
// ============================================================================
// Extract .tar.gz (compressed) benchmarks
// ============================================================================
group("extract .tar.gz (3 small files)", () => {
bench("node-tar", async () => {
await extractNodeTarBuffer(smallTarGzBuffer, extractDirNodeTar);
});
if (hasBunArchive) {
bench("Bun.Archive", async () => {
await Bun.Archive.from(smallBunArchiveGz).extract(extractDirBun);
});
}
});
group("extract .tar.gz (3 x 100KB files)", () => {
bench("node-tar", async () => {
await extractNodeTarBuffer(largeTarGzBuffer, extractDirNodeTar);
});
if (hasBunArchive) {
bench("Bun.Archive", async () => {
await Bun.Archive.from(largeBunArchiveGz).extract(extractDirBun);
});
}
});
group("extract .tar.gz (100 small files)", () => {
bench("node-tar", async () => {
await extractNodeTarBuffer(manyFilesTarGzBuffer, extractDirNodeTar);
});
if (hasBunArchive) {
bench("Bun.Archive", async () => {
await Bun.Archive.from(manyFilesBunArchiveGz).extract(extractDirBun);
});
}
});
// ============================================================================
// Write .tar to disk benchmarks
// ============================================================================
let writeCounter = 0;
group("write .tar to disk (3 small files)", () => {
bench("node-tar + writeFileSync", async () => {
const buffer = await createNodeTarBuffer(smallFilesDir, ["file1.txt", "file2.txt", "file3.txt"], false);
writeFileSync(join(writeDirNodeTar, `archive-${writeCounter++}.tar`), buffer);
});
if (hasBunArchive) {
bench("Bun.Archive.write", async () => {
await Bun.Archive.write(join(writeDirBun, `archive-${writeCounter++}.tar`), smallFiles);
});
}
});
group("write .tar to disk (3 x 100KB files)", () => {
bench("node-tar + writeFileSync", async () => {
const buffer = await createNodeTarBuffer(largeFilesDir, ["file1.txt", "file2.txt", "file3.txt"], false);
writeFileSync(join(writeDirNodeTar, `archive-${writeCounter++}.tar`), buffer);
});
if (hasBunArchive) {
bench("Bun.Archive.write", async () => {
await Bun.Archive.write(join(writeDirBun, `archive-${writeCounter++}.tar`), largeFiles);
});
}
});
group("write .tar to disk (100 small files)", () => {
bench("node-tar + writeFileSync", async () => {
const buffer = await createNodeTarBuffer(manyFilesDir, Object.keys(manyFilesEntries), false);
writeFileSync(join(writeDirNodeTar, `archive-${writeCounter++}.tar`), buffer);
});
if (hasBunArchive) {
bench("Bun.Archive.write", async () => {
await Bun.Archive.write(join(writeDirBun, `archive-${writeCounter++}.tar`), manyFilesEntries);
});
}
});
// ============================================================================
// Write .tar.gz to disk benchmarks
// ============================================================================
group("write .tar.gz to disk (3 small files)", () => {
bench("node-tar + writeFileSync", async () => {
const buffer = await createNodeTarBuffer(smallFilesDir, ["file1.txt", "file2.txt", "file3.txt"], true);
writeFileSync(join(writeDirNodeTar, `archive-${writeCounter++}.tar.gz`), buffer);
});
if (hasBunArchive) {
bench("Bun.Archive.write", async () => {
await Bun.Archive.write(join(writeDirBun, `archive-${writeCounter++}.tar.gz`), smallFiles, "gzip");
});
}
});
group("write .tar.gz to disk (3 x 100KB files)", () => {
bench("node-tar + writeFileSync", async () => {
const buffer = await createNodeTarBuffer(largeFilesDir, ["file1.txt", "file2.txt", "file3.txt"], true);
writeFileSync(join(writeDirNodeTar, `archive-${writeCounter++}.tar.gz`), buffer);
});
if (hasBunArchive) {
bench("Bun.Archive.write", async () => {
await Bun.Archive.write(join(writeDirBun, `archive-${writeCounter++}.tar.gz`), largeFiles, "gzip");
});
}
});
group("write .tar.gz to disk (100 small files)", () => {
bench("node-tar + writeFileSync", async () => {
const buffer = await createNodeTarBuffer(manyFilesDir, Object.keys(manyFilesEntries), true);
writeFileSync(join(writeDirNodeTar, `archive-${writeCounter++}.tar.gz`), buffer);
});
if (hasBunArchive) {
bench("Bun.Archive.write", async () => {
await Bun.Archive.write(join(writeDirBun, `archive-${writeCounter++}.tar.gz`), manyFilesEntries, "gzip");
});
}
});
// ============================================================================
// Get files array from archive (files() method) benchmarks
// ============================================================================
// Helper to get files array from node-tar (reads all entries into memory)
async function getFilesArrayNodeTar(buffer) {
return new Promise((resolve, reject) => {
const files = new Map();
let pending = 0;
let closed = false;
const maybeResolve = () => {
if (closed && pending === 0) {
resolve(files);
}
};
const unpack = new Unpack({
onReadEntry: entry => {
if (entry.type === "File") {
pending++;
const chunks = [];
entry.on("data", chunk => chunks.push(chunk));
entry.on("end", () => {
const content = Buffer.concat(chunks);
// Create a File-like object similar to Bun.Archive.files()
files.set(entry.path, new Blob([content]));
pending--;
maybeResolve();
});
}
entry.resume(); // Drain the entry
},
});
unpack.on("close", () => {
closed = true;
maybeResolve();
});
unpack.on("error", reject);
unpack.end(buffer);
});
}
group("files() - get all files as Map (3 small files)", () => {
bench("node-tar", async () => {
await getFilesArrayNodeTar(smallTarBuffer);
});
if (hasBunArchive) {
bench("Bun.Archive.files()", async () => {
await Bun.Archive.from(smallBunArchive).files();
});
}
});
group("files() - get all files as Map (3 x 100KB files)", () => {
bench("node-tar", async () => {
await getFilesArrayNodeTar(largeTarBuffer);
});
if (hasBunArchive) {
bench("Bun.Archive.files()", async () => {
await Bun.Archive.from(largeBunArchive).files();
});
}
});
group("files() - get all files as Map (100 small files)", () => {
bench("node-tar", async () => {
await getFilesArrayNodeTar(manyFilesTarBuffer);
});
if (hasBunArchive) {
bench("Bun.Archive.files()", async () => {
await Bun.Archive.from(manyFilesBunArchive).files();
});
}
});
group("files() - get all files as Map from .tar.gz (3 small files)", () => {
bench("node-tar", async () => {
await getFilesArrayNodeTar(smallTarGzBuffer);
});
if (hasBunArchive) {
bench("Bun.Archive.files()", async () => {
await Bun.Archive.from(smallBunArchiveGz).files();
});
}
});
group("files() - get all files as Map from .tar.gz (100 small files)", () => {
bench("node-tar", async () => {
await getFilesArrayNodeTar(manyFilesTarGzBuffer);
});
if (hasBunArchive) {
bench("Bun.Archive.files()", async () => {
await Bun.Archive.from(manyFilesBunArchiveGz).files();
});
}
});
await run();
// Cleanup
rmSync(setupDir, { recursive: true, force: true });
rmSync(extractDirNodeTar, { recursive: true, force: true });
rmSync(extractDirBun, { recursive: true, force: true });
rmSync(writeDirNodeTar, { recursive: true, force: true });
rmSync(writeDirBun, { recursive: true, force: true });

335
bench/snippets/array-of.js Normal file
View File

@@ -0,0 +1,335 @@
import { bench, run } from "../runner.mjs";
let sink;
// Integers
bench("int: Array.of(1,2,3,4,5)", () => {
sink = Array.of(1, 2, 3, 4, 5);
});
bench("int: Array.of(100 elements)", () => {
sink = Array.of(
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
58,
59,
60,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70,
71,
72,
73,
74,
75,
76,
77,
78,
79,
80,
81,
82,
83,
84,
85,
86,
87,
88,
89,
90,
91,
92,
93,
94,
95,
96,
97,
98,
99,
);
});
// Doubles
bench("double: Array.of(1.1,2.2,3.3,4.4,5.5)", () => {
sink = Array.of(1.1, 2.2, 3.3, 4.4, 5.5);
});
bench("double: Array.of(100 elements)", () => {
sink = Array.of(
0.1,
1.1,
2.1,
3.1,
4.1,
5.1,
6.1,
7.1,
8.1,
9.1,
10.1,
11.1,
12.1,
13.1,
14.1,
15.1,
16.1,
17.1,
18.1,
19.1,
20.1,
21.1,
22.1,
23.1,
24.1,
25.1,
26.1,
27.1,
28.1,
29.1,
30.1,
31.1,
32.1,
33.1,
34.1,
35.1,
36.1,
37.1,
38.1,
39.1,
40.1,
41.1,
42.1,
43.1,
44.1,
45.1,
46.1,
47.1,
48.1,
49.1,
50.1,
51.1,
52.1,
53.1,
54.1,
55.1,
56.1,
57.1,
58.1,
59.1,
60.1,
61.1,
62.1,
63.1,
64.1,
65.1,
66.1,
67.1,
68.1,
69.1,
70.1,
71.1,
72.1,
73.1,
74.1,
75.1,
76.1,
77.1,
78.1,
79.1,
80.1,
81.1,
82.1,
83.1,
84.1,
85.1,
86.1,
87.1,
88.1,
89.1,
90.1,
91.1,
92.1,
93.1,
94.1,
95.1,
96.1,
97.1,
98.1,
99.1,
);
});
// Objects
bench("object: Array.of(obj x5)", () => {
sink = Array.of({ a: 1 }, { a: 2 }, { a: 3 }, { a: 4 }, { a: 5 });
});
bench("object: Array.of(100 elements)", () => {
sink = Array.of(
{ a: 0 },
{ a: 1 },
{ a: 2 },
{ a: 3 },
{ a: 4 },
{ a: 5 },
{ a: 6 },
{ a: 7 },
{ a: 8 },
{ a: 9 },
{ a: 10 },
{ a: 11 },
{ a: 12 },
{ a: 13 },
{ a: 14 },
{ a: 15 },
{ a: 16 },
{ a: 17 },
{ a: 18 },
{ a: 19 },
{ a: 20 },
{ a: 21 },
{ a: 22 },
{ a: 23 },
{ a: 24 },
{ a: 25 },
{ a: 26 },
{ a: 27 },
{ a: 28 },
{ a: 29 },
{ a: 30 },
{ a: 31 },
{ a: 32 },
{ a: 33 },
{ a: 34 },
{ a: 35 },
{ a: 36 },
{ a: 37 },
{ a: 38 },
{ a: 39 },
{ a: 40 },
{ a: 41 },
{ a: 42 },
{ a: 43 },
{ a: 44 },
{ a: 45 },
{ a: 46 },
{ a: 47 },
{ a: 48 },
{ a: 49 },
{ a: 50 },
{ a: 51 },
{ a: 52 },
{ a: 53 },
{ a: 54 },
{ a: 55 },
{ a: 56 },
{ a: 57 },
{ a: 58 },
{ a: 59 },
{ a: 60 },
{ a: 61 },
{ a: 62 },
{ a: 63 },
{ a: 64 },
{ a: 65 },
{ a: 66 },
{ a: 67 },
{ a: 68 },
{ a: 69 },
{ a: 70 },
{ a: 71 },
{ a: 72 },
{ a: 73 },
{ a: 74 },
{ a: 75 },
{ a: 76 },
{ a: 77 },
{ a: 78 },
{ a: 79 },
{ a: 80 },
{ a: 81 },
{ a: 82 },
{ a: 83 },
{ a: 84 },
{ a: 85 },
{ a: 86 },
{ a: 87 },
{ a: 88 },
{ a: 89 },
{ a: 90 },
{ a: 91 },
{ a: 92 },
{ a: 93 },
{ a: 94 },
{ a: 95 },
{ a: 96 },
{ a: 97 },
{ a: 98 },
{ a: 99 },
);
});
await run();

View File

@@ -0,0 +1,38 @@
// @runtime bun,node
import { Buffer } from "node:buffer";
import { bench, group, run } from "../runner.mjs";
// Small arrays (common case)
const int32Array8 = [1, 2, 3, 4, 5, 6, 7, 8];
const doubleArray8 = [1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5];
// Medium arrays
const int32Array64 = Array.from({ length: 64 }, (_, i) => i % 256);
const doubleArray64 = Array.from({ length: 64 }, (_, i) => i + 0.5);
// Large arrays
const int32Array1024 = Array.from({ length: 1024 }, (_, i) => i % 256);
// Array-like objects (fallback path)
const arrayLike8 = { 0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, length: 8 };
// Empty array
const emptyArray = [];
group("Buffer.from(array) - Int32 arrays", () => {
bench("Buffer.from(int32[8])", () => Buffer.from(int32Array8));
bench("Buffer.from(int32[64])", () => Buffer.from(int32Array64));
bench("Buffer.from(int32[1024])", () => Buffer.from(int32Array1024));
});
group("Buffer.from(array) - Double arrays", () => {
bench("Buffer.from(double[8])", () => Buffer.from(doubleArray8));
bench("Buffer.from(double[64])", () => Buffer.from(doubleArray64));
});
group("Buffer.from(array) - Edge cases", () => {
bench("Buffer.from([])", () => Buffer.from(emptyArray));
bench("Buffer.from(arrayLike[8])", () => Buffer.from(arrayLike8));
});
await run();

View File

@@ -0,0 +1,4 @@
// Child process for IPC benchmarks - echoes messages back to parent
process.on("message", message => {
process.send(message);
});

View File

@@ -0,0 +1,45 @@
import { fork } from "node:child_process";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { bench, run } from "../runner.mjs";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const childPath = path.join(__dirname, "ipc-json-child.mjs");
const smallMessage = { type: "ping", id: 1 };
const largeString = Buffer.alloc(10 * 1024 * 1024, "A").toString();
const largeMessage = { type: "ping", id: 1, data: largeString };
async function runBenchmark(message, count) {
let received = 0;
const { promise, resolve } = Promise.withResolvers();
const child = fork(childPath, [], {
stdio: ["ignore", "ignore", "ignore", "ipc"],
serialization: "json",
});
child.on("message", () => {
received++;
if (received >= count) {
resolve();
}
});
for (let i = 0; i < count; i++) {
child.send(message);
}
await promise;
child.kill();
}
bench("ipc json - small messages (1000 roundtrips)", async () => {
await runBenchmark(smallMessage, 1000);
});
bench("ipc json - 10MB messages (10 roundtrips)", async () => {
await runBenchmark(largeMessage, 10);
});
await run();

View File

@@ -0,0 +1,57 @@
import { bench, run } from "../runner.mjs";
const obj = { a: 1, b: 2, c: 3 };
const objDeep = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8 };
const sym = Symbol("test");
const objWithSymbol = { [sym]: 1, a: 2 };
const objs = [
{ f: 50 },
{ f: 50, g: 70 },
{ g: 50, f: 70 },
{ h: 50, f: 70 },
{ z: 50, f: 70 },
{ k: 50, f: 70 },
];
bench("Object.hasOwn - hit", () => {
return Object.hasOwn(obj, "a");
});
bench("Object.hasOwn - miss", () => {
return Object.hasOwn(obj, "z");
});
bench("Object.hasOwn - symbol hit", () => {
return Object.hasOwn(objWithSymbol, sym);
});
bench("Object.hasOwn - symbol miss", () => {
return Object.hasOwn(objWithSymbol, Symbol("other"));
});
bench("Object.hasOwn - multiple shapes", () => {
let result = true;
for (let i = 0; i < objs.length; i++) {
result = Object.hasOwn(objs[i], "f") && result;
}
return result;
});
bench("Object.prototype.hasOwnProperty - hit", () => {
return obj.hasOwnProperty("a");
});
bench("Object.prototype.hasOwnProperty - miss", () => {
return obj.hasOwnProperty("z");
});
bench("in operator - hit", () => {
return "a" in obj;
});
bench("in operator - miss", () => {
return "z" in obj;
});
await run();

View File

@@ -0,0 +1,7 @@
import { bench, run } from "../runner.mjs";
bench("Promise.race([p1, p2])", async function () {
return await Promise.race([Promise.resolve(1), Promise.resolve(2)]);
});
await run();

View File

@@ -112,12 +112,40 @@ const obj = {
},
};
bench("Response.json(obj)", async () => {
const smallObj = { id: 1, name: "test" };
const arrayObj = {
items: Array.from({ length: 100 }, (_, i) => ({ id: i, value: `item-${i}` })),
};
bench("Response.json(obj)", () => {
return Response.json(obj);
});
bench("Response.json(obj).json()", async () => {
return await Response.json(obj).json();
bench("new Response(JSON.stringify(obj))", () => {
return new Response(JSON.stringify(obj), {
headers: { "Content-Type": "application/json" },
});
});
bench("Response.json(smallObj)", () => {
return Response.json(smallObj);
});
bench("new Response(JSON.stringify(smallObj))", () => {
return new Response(JSON.stringify(smallObj), {
headers: { "Content-Type": "application/json" },
});
});
bench("Response.json(arrayObj)", () => {
return Response.json(arrayObj);
});
bench("new Response(JSON.stringify(arrayObj))", () => {
return new Response(JSON.stringify(arrayObj), {
headers: { "Content-Type": "application/json" },
});
});
await run();

View File

@@ -0,0 +1,34 @@
import { bench, run } from "../runner.mjs";
const shortStr = "The quick brown fox jumps over the lazy dog";
const longStr = shortStr.repeat(100);
bench("String.includes - short, hit (middle)", () => {
return shortStr.includes("jumps");
});
bench("String.includes - short, hit (start)", () => {
return shortStr.includes("The");
});
bench("String.includes - short, hit (end)", () => {
return shortStr.includes("dog");
});
bench("String.includes - short, miss", () => {
return shortStr.includes("cat");
});
bench("String.includes - long, hit (middle)", () => {
return longStr.includes("jumps");
});
bench("String.includes - long, miss", () => {
return longStr.includes("cat");
});
bench("String.includes - with position", () => {
return shortStr.includes("fox", 10);
});
await run();

103
bench/snippets/wrap-ansi.js Normal file
View File

@@ -0,0 +1,103 @@
import wrapAnsi from "wrap-ansi";
import { bench, run, summary } from "../runner.mjs";
// Test fixtures
const shortText = "The quick brown fox jumped over the lazy dog.";
const mediumText = "The quick brown fox jumped over the lazy dog and then ran away with the unicorn. ".repeat(10);
const longText = "The quick brown fox jumped over the lazy dog and then ran away with the unicorn. ".repeat(100);
// ANSI colored text
const red = s => `\u001B[31m${s}\u001B[39m`;
const green = s => `\u001B[32m${s}\u001B[39m`;
const blue = s => `\u001B[34m${s}\u001B[39m`;
const coloredShort = `The quick ${red("brown fox")} jumped over the ${green("lazy dog")}.`;
const coloredMedium =
`The quick ${red("brown fox jumped over")} the ${green("lazy dog and then ran away")} with the ${blue("unicorn")}. `.repeat(
10,
);
const coloredLong =
`The quick ${red("brown fox jumped over")} the ${green("lazy dog and then ran away")} with the ${blue("unicorn")}. `.repeat(
100,
);
// Full-width characters (Japanese)
const japaneseText = "日本語のテキストを折り返すテストです。全角文字は幅2としてカウントされます。".repeat(5);
// Emoji text
const emojiText = "Hello 👋 World 🌍! Let's test 🧪 some emoji 😀 wrapping 📦!".repeat(5);
// Hyperlink text
const hyperlinkText = "Check out \u001B]8;;https://bun.sh\u0007Bun\u001B]8;;\u0007, it's fast! ".repeat(10);
// Options
const hardOpts = { hard: true };
const noTrimOpts = { trim: false };
// Basic text benchmarks
summary(() => {
bench("Short text (45 chars) - npm", () => wrapAnsi(shortText, 20));
bench("Short text (45 chars) - Bun", () => Bun.wrapAnsi(shortText, 20));
});
summary(() => {
bench("Medium text (810 chars) - npm", () => wrapAnsi(mediumText, 40));
bench("Medium text (810 chars) - Bun", () => Bun.wrapAnsi(mediumText, 40));
});
summary(() => {
bench("Long text (8100 chars) - npm", () => wrapAnsi(longText, 80));
bench("Long text (8100 chars) - Bun", () => Bun.wrapAnsi(longText, 80));
});
// ANSI colored text benchmarks
summary(() => {
bench("Colored short - npm", () => wrapAnsi(coloredShort, 20));
bench("Colored short - Bun", () => Bun.wrapAnsi(coloredShort, 20));
});
summary(() => {
bench("Colored medium - npm", () => wrapAnsi(coloredMedium, 40));
bench("Colored medium - Bun", () => Bun.wrapAnsi(coloredMedium, 40));
});
summary(() => {
bench("Colored long - npm", () => wrapAnsi(coloredLong, 80));
bench("Colored long - Bun", () => Bun.wrapAnsi(coloredLong, 80));
});
// Hard wrap benchmarks
summary(() => {
bench("Hard wrap long - npm", () => wrapAnsi(longText, 80, hardOpts));
bench("Hard wrap long - Bun", () => Bun.wrapAnsi(longText, 80, hardOpts));
});
summary(() => {
bench("Hard wrap colored - npm", () => wrapAnsi(coloredLong, 80, hardOpts));
bench("Hard wrap colored - Bun", () => Bun.wrapAnsi(coloredLong, 80, hardOpts));
});
// Unicode benchmarks
summary(() => {
bench("Japanese (full-width) - npm", () => wrapAnsi(japaneseText, 40));
bench("Japanese (full-width) - Bun", () => Bun.wrapAnsi(japaneseText, 40));
});
summary(() => {
bench("Emoji text - npm", () => wrapAnsi(emojiText, 30));
bench("Emoji text - Bun", () => Bun.wrapAnsi(emojiText, 30));
});
// Hyperlink benchmarks
summary(() => {
bench("Hyperlink (OSC 8) - npm", () => wrapAnsi(hyperlinkText, 40));
bench("Hyperlink (OSC 8) - Bun", () => Bun.wrapAnsi(hyperlinkText, 40));
});
// No trim option
summary(() => {
bench("No trim long - npm", () => wrapAnsi(longText, 80, noTrimOpts));
bench("No trim long - Bun", () => Bun.wrapAnsi(longText, 80, noTrimOpts));
});
await run();

View File

@@ -4,7 +4,7 @@ register_repository(
REPOSITORY
oven-sh/boringssl
COMMIT
f1ffd9e83d4f5c28a9c70d73f9a4e6fcf310062f
4f4f5ef8ebc6e23cbf393428f0ab1b526773f7ac
)
register_cmake_command(

View File

@@ -419,12 +419,9 @@ execute_process(
--command=list-outputs
--sources=${BUN_BINDGENV2_SOURCES_COMMA_SEPARATED}
--codegen-path=${CODEGEN_PATH}
RESULT_VARIABLE bindgen_result
OUTPUT_VARIABLE bindgen_outputs
COMMAND_ERROR_IS_FATAL ANY
)
if(${bindgen_result})
message(FATAL_ERROR "bindgenv2/script.ts exited with non-zero status")
endif()
foreach(output IN LISTS bindgen_outputs)
if(output MATCHES "\.cpp$")
list(APPEND BUN_BINDGENV2_CPP_OUTPUTS ${output})
@@ -1313,6 +1310,9 @@ if(WIN32)
wsock32 # ws2_32 required by TransmitFile aka sendfile on windows
delayimp.lib
)
# Required for static ICU linkage - without this, ICU headers expect DLL linkage
# which causes ABI mismatch and crashes (STATUS_STACK_BUFFER_OVERRUN)
target_compile_definitions(${bun} PRIVATE U_STATIC_IMPLEMENTATION)
endif()
# --- Packaging ---

View File

@@ -48,6 +48,9 @@ if(NOT BUILDKITE_BUILD_STATUS EQUAL 0)
endif()
file(READ ${BUILDKITE_BUILD_PATH}/build.json BUILDKITE_BUILD)
# Escape backslashes so CMake doesn't interpret JSON escape sequences (e.g., \n in commit messages)
string(REPLACE "\\" "\\\\" BUILDKITE_BUILD "${BUILDKITE_BUILD}")
string(JSON BUILDKITE_BUILD_UUID GET ${BUILDKITE_BUILD} id)
string(JSON BUILDKITE_JOBS GET ${BUILDKITE_BUILD} jobs)
string(JSON BUILDKITE_JOBS_COUNT LENGTH ${BUILDKITE_JOBS})

View File

@@ -5,18 +5,12 @@ if(NOT ENABLE_CCACHE OR CACHE_STRATEGY STREQUAL "none")
return()
endif()
if (CI AND NOT APPLE)
setenv(CCACHE_DISABLE 1)
return()
endif()
find_command(
VARIABLE
CCACHE_PROGRAM
COMMAND
ccache
REQUIRED
${CI}
)
if(NOT CCACHE_PROGRAM)

View File

@@ -1,123 +0,0 @@
# Setup sccache as the C and C++ compiler launcher to speed up builds by caching
if(CACHE_STRATEGY STREQUAL "none")
return()
endif()
set(SCCACHE_SHARED_CACHE_REGION "us-west-1")
set(SCCACHE_SHARED_CACHE_BUCKET "bun-build-sccache-store")
# Function to check if the system AWS credentials have access to the sccache S3 bucket.
function(check_aws_credentials OUT_VAR)
# Install dependencies first
execute_process(
COMMAND ${BUN_EXECUTABLE} install --frozen-lockfile
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/scripts/build-cache
RESULT_VARIABLE INSTALL_EXIT_CODE
OUTPUT_VARIABLE INSTALL_OUTPUT
ERROR_VARIABLE INSTALL_ERROR
)
if(NOT INSTALL_EXIT_CODE EQUAL 0)
message(FATAL_ERROR "Failed to install dependencies in scripts/build-cache\n"
"Exit code: ${INSTALL_EXIT_CODE}\n"
"Output: ${INSTALL_OUTPUT}\n"
"Error: ${INSTALL_ERROR}")
endif()
# Check AWS credentials
execute_process(
COMMAND
${BUN_EXECUTABLE}
run
have-access.ts
--bucket ${SCCACHE_SHARED_CACHE_BUCKET}
--region ${SCCACHE_SHARED_CACHE_REGION}
WORKING_DIRECTORY
${CMAKE_SOURCE_DIR}/scripts/build-cache
RESULT_VARIABLE HAVE_ACCESS_EXIT_CODE
)
if(HAVE_ACCESS_EXIT_CODE EQUAL 0)
set(HAS_CREDENTIALS TRUE)
else()
set(HAS_CREDENTIALS FALSE)
endif()
set(${OUT_VAR} ${HAS_CREDENTIALS} PARENT_SCOPE)
endfunction()
# Configure sccache to use the local cache only.
function(sccache_configure_local_filesystem)
unsetenv(SCCACHE_BUCKET)
unsetenv(SCCACHE_REGION)
setenv(SCCACHE_DIR "${CACHE_PATH}/sccache")
endfunction()
# Configure sccache to use the distributed cache (S3 + local).
function(sccache_configure_distributed)
setenv(SCCACHE_BUCKET "${SCCACHE_SHARED_CACHE_BUCKET}")
setenv(SCCACHE_REGION "${SCCACHE_SHARED_CACHE_REGION}")
setenv(SCCACHE_DIR "${CACHE_PATH}/sccache")
endfunction()
function(sccache_configure_environment_ci)
if(CACHE_STRATEGY STREQUAL "auto" OR CACHE_STRATEGY STREQUAL "distributed")
check_aws_credentials(HAS_AWS_CREDENTIALS)
if(HAS_AWS_CREDENTIALS)
sccache_configure_distributed()
message(NOTICE "sccache: Using distributed cache strategy.")
else()
message(FATAL_ERROR "CI CACHE_STRATEGY is set to '${CACHE_STRATEGY}', but no valid AWS "
"credentials were found. Note that 'auto' requires AWS credentials to access the shared "
"cache in CI.")
endif()
elseif(CACHE_STRATEGY STREQUAL "local")
# We disallow this because we want our CI runs to always used the shared cache to accelerate
# builds.
# none, distributed and auto are all okay.
#
# If local is configured, it's as good as "none", so this is probably user error.
message(FATAL_ERROR "CI CACHE_STRATEGY is set to 'local', which is not allowed.")
endif()
endfunction()
function(sccache_configure_environment_developer)
# Local environments can use any strategy they like. S3 is set up in such a way so as to clean
# itself from old entries automatically.
if (CACHE_STRATEGY STREQUAL "auto" OR CACHE_STRATEGY STREQUAL "local")
# In the local environment, we prioritize using the local cache. This is because sccache takes
# into consideration the whole absolute path of the files being compiled, and it's very
# unlikely users will have the same absolute paths on their local machines.
sccache_configure_local_filesystem()
message(NOTICE "sccache: Using local cache strategy.")
elseif(CACHE_STRATEGY STREQUAL "distributed")
check_aws_credentials(HAS_AWS_CREDENTIALS)
if(HAS_AWS_CREDENTIALS)
sccache_configure_distributed()
message(NOTICE "sccache: Using distributed cache strategy.")
else()
message(FATAL_ERROR "CACHE_STRATEGY is set to 'distributed', but no valid AWS credentials "
"were found.")
endif()
endif()
endfunction()
find_command(VARIABLE SCCACHE_PROGRAM COMMAND sccache REQUIRED ${CI})
if(NOT SCCACHE_PROGRAM)
message(WARNING "sccache not found. Your builds will be slower.")
return()
endif()
set(SCCACHE_ARGS CMAKE_C_COMPILER_LAUNCHER CMAKE_CXX_COMPILER_LAUNCHER)
foreach(arg ${SCCACHE_ARGS})
setx(${arg} ${SCCACHE_PROGRAM})
list(APPEND CMAKE_ARGS -D${arg}=${${arg}})
endforeach()
setenv(SCCACHE_LOG "info")
if (CI)
sccache_configure_environment_ci()
else()
sccache_configure_environment_developer()
endif()

View File

@@ -2,7 +2,7 @@ option(WEBKIT_VERSION "The version of WebKit to use")
option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading")
if(NOT WEBKIT_VERSION)
set(WEBKIT_VERSION 6d0f3aac0b817cc01a846b3754b21271adedac12)
set(WEBKIT_VERSION 5ae5f07f29059c183a8db2eef2c9aabd474ec73c)
endif()
string(SUBSTRING ${WEBKIT_VERSION} 0 16 WEBKIT_VERSION_PREFIX)
@@ -28,12 +28,13 @@ if(WEBKIT_LOCAL)
# make jsc-compile-debug jsc-copy-headers
include_directories(
${WEBKIT_PATH}
${WEBKIT_PATH}/JavaScriptCore/Headers
${WEBKIT_PATH}/JavaScriptCore/Headers/JavaScriptCore
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders
${WEBKIT_PATH}/bmalloc/Headers
${WEBKIT_PATH}/WTF/Headers
${WEBKIT_PATH}/JavaScriptCore/DerivedSources/inspector
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders/JavaScriptCore
${WEBKIT_PATH}/JavaScriptCore/DerivedSources/inspector
)
endif()
@@ -90,7 +91,14 @@ if(EXISTS ${WEBKIT_PATH}/package.json)
endif()
endif()
file(DOWNLOAD ${WEBKIT_DOWNLOAD_URL} ${CACHE_PATH}/${WEBKIT_FILENAME} SHOW_PROGRESS)
file(
DOWNLOAD ${WEBKIT_DOWNLOAD_URL} ${CACHE_PATH}/${WEBKIT_FILENAME} SHOW_PROGRESS
STATUS WEBKIT_DOWNLOAD_STATUS
)
if(NOT "${WEBKIT_DOWNLOAD_STATUS}" MATCHES "^0;")
message(FATAL_ERROR "Failed to download WebKit: ${WEBKIT_DOWNLOAD_STATUS}")
endif()
file(ARCHIVE_EXTRACT INPUT ${CACHE_PATH}/${WEBKIT_FILENAME} DESTINATION ${CACHE_PATH} TOUCH)
file(REMOVE ${CACHE_PATH}/${WEBKIT_FILENAME})
file(REMOVE_RECURSE ${WEBKIT_PATH})

View File

@@ -35,8 +35,8 @@ end
set -l bun_install_boolean_flags yarn production optional development no-save dry-run force no-cache silent verbose global
set -l bun_install_boolean_flags_descriptions "Write a yarn.lock file (yarn v1)" "Don't install devDependencies" "Add dependency to optionalDependencies" "Add dependency to devDependencies" "Don't update package.json or save a lockfile" "Don't install anything" "Always request the latest versions from the registry & reinstall all dependencies" "Ignore manifest cache entirely" "Don't output anything" "Excessively verbose logging" "Use global folder"
set -l bun_builtin_cmds_without_run dev create help bun upgrade discord install remove add init pm x
set -l bun_builtin_cmds_accepting_flags create help bun upgrade discord run init link unlink pm x
set -l bun_builtin_cmds_without_run dev create help bun upgrade discord install remove add update init pm x
set -l bun_builtin_cmds_accepting_flags create help bun upgrade discord run init link unlink pm x update
function __bun_complete_bins_scripts --inherit-variable bun_builtin_cmds_without_run -d "Emit bun completions for bins and scripts"
# Do nothing if we already have a builtin subcommand,
@@ -148,14 +148,14 @@ complete -c bun \
for i in (seq (count $bun_install_boolean_flags))
complete -c bun \
-n "__fish_seen_subcommand_from install add remove" -l "$bun_install_boolean_flags[$i]" -d "$bun_install_boolean_flags_descriptions[$i]"
-n "__fish_seen_subcommand_from install add remove update" -l "$bun_install_boolean_flags[$i]" -d "$bun_install_boolean_flags_descriptions[$i]"
end
complete -c bun \
-n "__fish_seen_subcommand_from install add remove" -l 'cwd' -d 'Change working directory'
-n "__fish_seen_subcommand_from install add remove update" -l 'cwd' -d 'Change working directory'
complete -c bun \
-n "__fish_seen_subcommand_from install add remove" -l 'cache-dir' -d 'Choose a cache directory (default: $HOME/.bun/install/cache)'
-n "__fish_seen_subcommand_from install add remove update" -l 'cache-dir' -d 'Choose a cache directory (default: $HOME/.bun/install/cache)'
complete -c bun \
-n "__fish_seen_subcommand_from add" -d 'Popular' -a '(__fish__get_bun_packages)'
@@ -183,4 +183,5 @@ complete -c bun -n "__fish_use_subcommand" -a "unlink" -d "Unregister a local np
complete -c bun -n "__fish_use_subcommand" -a "pm" -d "Additional package management utilities" -f
complete -c bun -n "__fish_use_subcommand" -a "x" -d "Execute a package binary, installing if needed" -f
complete -c bun -n "__fish_use_subcommand" -a "outdated" -d "Display the latest versions of outdated dependencies" -f
complete -c bun -n "__fish_use_subcommand" -a "update" -d "Update dependencies to their latest versions" -f
complete -c bun -n "__fish_use_subcommand" -a "publish" -d "Publish your package from local to npm" -f

View File

@@ -220,6 +220,78 @@ An array of paths corresponding to the entrypoints of our application. One bundl
</Tab>
</Tabs>
### files
A map of file paths to their contents for in-memory bundling. This allows you to bundle virtual files that don't exist on disk, or override the contents of files that do exist. This option is only available in the JavaScript API.
File contents can be provided as a `string`, `Blob`, `TypedArray`, or `ArrayBuffer`.
#### Bundle entirely from memory
You can bundle code without any files on disk by providing all sources via `files`:
```ts title="build.ts" icon="/icons/typescript.svg"
const result = await Bun.build({
entrypoints: ["/app/index.ts"],
files: {
"/app/index.ts": `
import { greet } from "./greet.ts";
console.log(greet("World"));
`,
"/app/greet.ts": `
export function greet(name: string) {
return "Hello, " + name + "!";
}
`,
},
});
const output = await result.outputs[0].text();
console.log(output);
```
When all entrypoints are in the `files` map, the current working directory is used as the root.
#### Override files on disk
In-memory files take priority over files on disk. This lets you override specific files while keeping the rest of your codebase unchanged:
```ts title="build.ts" icon="/icons/typescript.svg"
// Assume ./src/config.ts exists on disk with development settings
await Bun.build({
entrypoints: ["./src/index.ts"],
files: {
// Override config.ts with production values
"./src/config.ts": `
export const API_URL = "https://api.production.com";
export const DEBUG = false;
`,
},
outdir: "./dist",
});
```
#### Mix disk and virtual files
Real files on disk can import virtual files, and virtual files can import real files:
```ts title="build.ts" icon="/icons/typescript.svg"
// ./src/index.ts exists on disk and imports "./generated.ts"
await Bun.build({
entrypoints: ["./src/index.ts"],
files: {
// Provide a virtual file that index.ts imports
"./src/generated.ts": `
export const BUILD_ID = "${crypto.randomUUID()}";
export const BUILD_TIME = ${Date.now()};
`,
},
outdir: "./dist",
});
```
This is useful for code generation, injecting build-time constants, or testing with mock modules.
### outdir
The directory where output files will be written.
@@ -1219,6 +1291,79 @@ declare module "bun:bundle" {
Ensure the file is included in your `tsconfig.json` (e.g., `"include": ["src", "env.d.ts"]`). Now `feature()` only accepts those flags, and invalid strings like `feature("TYPO")` become type errors.
### metafile
Generate metadata about the build in a structured format. The metafile contains information about all input files, output files, their sizes, imports, and exports. This is useful for:
- **Bundle analysis**: Understand what's contributing to bundle size
- **Visualization**: Feed into tools like [esbuild's bundle analyzer](https://esbuild.github.io/analyze/) or other visualization tools
- **Dependency tracking**: See the full import graph of your application
- **CI integration**: Track bundle size changes over time
<Tabs>
<Tab title="JavaScript">
```ts title="build.ts" icon="/icons/typescript.svg"
const result = await Bun.build({
entrypoints: ['./src/index.ts'],
outdir: './dist',
metafile: true,
});
if (result.metafile) {
// Analyze inputs
for (const [path, meta] of Object.entries(result.metafile.inputs)) {
console.log(`${path}: ${meta.bytes} bytes`);
}
// Analyze outputs
for (const [path, meta] of Object.entries(result.metafile.outputs)) {
console.log(`${path}: ${meta.bytes} bytes`);
}
// Save for external analysis tools
await Bun.write('./dist/meta.json', JSON.stringify(result.metafile));
}
```
</Tab>
<Tab title="CLI">
```bash terminal icon="terminal"
bun build ./src/index.ts --outdir ./dist --metafile ./dist/meta.json
```
</Tab>
</Tabs>
The metafile structure contains:
```ts
interface BuildMetafile {
inputs: {
[path: string]: {
bytes: number;
imports: Array<{
path: string;
kind: ImportKind;
original?: string; // Original specifier before resolution
external?: boolean;
}>;
format?: "esm" | "cjs" | "json" | "css";
};
};
outputs: {
[path: string]: {
bytes: number;
inputs: {
[path: string]: { bytesInOutput: number };
};
imports: Array<{ path: string; kind: ImportKind }>;
exports: string[];
entryPoint?: string;
cssBundle?: string; // Associated CSS file for JS entry points
};
};
}
```
## Outputs
The `Bun.build` function returns a `Promise<BuildOutput>`, defined as:
@@ -1228,6 +1373,7 @@ interface BuildOutput {
outputs: BuildArtifact[];
success: boolean;
logs: Array<object>; // see docs for details
metafile?: BuildMetafile; // only when metafile: true
}
interface BuildArtifact extends Blob {

View File

@@ -121,6 +121,7 @@
"/runtime/file-io",
"/runtime/streams",
"/runtime/binary-data",
"/runtime/archive",
"/runtime/sql",
"/runtime/sqlite",
"/runtime/s3",

View File

@@ -33,7 +33,7 @@ Alternatively, you can create a PM2 configuration file. Create a file named `pm2
```js pm2.config.js icon="file-code"
module.exports = {
title: "app", // Name of your application
name: "app", // Name of your application
script: "index.ts", // Entry point of your application
interpreter: "bun", // Bun interpreter
env: {

View File

@@ -26,21 +26,6 @@ The `bun` CLI contains a Node.js-compatible package manager designed to be a dra
</Note>
<Accordion title="For Linux users">
The recommended minimum Linux Kernel version is 5.6. If you're on Linux kernel 5.1 - 5.5, `bun install` will work, but HTTP requests will be slow due to a lack of support for io_uring's `connect()` operation.
If you're using Ubuntu 20.04, here's how to install a [newer kernel](https://wiki.ubuntu.com/Kernel/LTSEnablementStack):
```bash terminal icon="terminal"
# If this returns a version >= 5.6, you don't need to do anything
uname -r
# Install the official Ubuntu hardware enablement kernel
sudo apt install --install-recommends linux-generic-hwe-20.04
```
</Accordion>
To install all dependencies of a project:
```bash terminal icon="terminal"

View File

@@ -189,7 +189,7 @@ Isolated installs are conceptually similar to pnpm, so migration should be strai
```bash terminal icon="terminal"
# Remove pnpm files
$ rm -rf node_modules pnpm-lock.yaml
rm -rf node_modules pnpm-lock.yaml
# Install with Bun's isolated linker
bun install --linker isolated

View File

@@ -72,6 +72,7 @@ The following options are supported:
- `username`
- `_password` (base64 encoded password)
- `_auth` (base64 encoded username:password, e.g. `btoa(username + ":" + password)`)
- `email`
The equivalent `bunfig.toml` option is to add a key in [`install.scopes`](/runtime/bunfig#install-registry):
@@ -109,3 +110,136 @@ The equivalent `bunfig.toml` option is [`install.exact`](/runtime/bunfig#install
[install]
exact = true
```
### `ignore-scripts`: Skip lifecycle scripts
Prevents running lifecycle scripts during installation:
```ini .npmrc icon="npm"
ignore-scripts=true
```
This is equivalent to using the `--ignore-scripts` flag with `bun install`.
### `dry-run`: Preview changes without installing
Shows what would be installed without actually installing:
```ini .npmrc icon="npm"
dry-run=true
```
The equivalent `bunfig.toml` option is [`install.dryRun`](/runtime/bunfig#install-dryrun):
```toml bunfig.toml icon="settings"
[install]
dryRun = true
```
### `cache`: Configure cache directory
Set the cache directory path, or disable caching:
```ini .npmrc icon="npm"
# set a custom cache directory
cache=/path/to/cache
# or disable caching
cache=false
```
The equivalent `bunfig.toml` option is [`install.cache`](/runtime/bunfig#install-cache):
```toml bunfig.toml icon="settings"
[install.cache]
# set a custom cache directory
dir = "/path/to/cache"
# or disable caching
disable = true
```
### `ca` and `cafile`: Configure CA certificates
Configure custom CA certificates for registry connections:
```ini .npmrc icon="npm"
# single CA certificate
ca="-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
# multiple CA certificates
ca[]="-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
ca[]="-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
# or specify a path to a CA file
cafile=/path/to/ca-bundle.crt
```
### `omit` and `include`: Control dependency types
Control which dependency types are installed:
```ini .npmrc icon="npm"
# omit dev dependencies
omit=dev
# omit multiple types
omit[]=dev
omit[]=optional
# include specific types (overrides omit)
include=dev
```
Valid values: `dev`, `peer`, `optional`
### `install-strategy` and `node-linker`: Installation strategy
Control how packages are installed in `node_modules`. Bun supports two different configuration options for compatibility with different package managers.
**npm's `install-strategy`:**
```ini .npmrc icon="npm"
# flat node_modules structure (default)
install-strategy=hoisted
# symlinked structure
install-strategy=linked
```
**pnpm/yarn's `node-linker`:**
The `node-linker` option controls the installation mode. Bun supports values from both pnpm and yarn:
| Value | Description | Accepted by |
| -------------- | ----------------------------------------------- | ----------- |
| `isolated` | Symlinked structure with isolated dependencies | pnpm |
| `hoisted` | Flat node_modules structure | pnpm |
| `pnpm` | Symlinked structure (same as `isolated`) | yarn |
| `node-modules` | Flat node_modules structure (same as `hoisted`) | yarn |
```ini .npmrc icon="npm"
# symlinked/isolated mode
node-linker=isolated
node-linker=pnpm
# flat/hoisted mode
node-linker=hoisted
node-linker=node-modules
```
### `public-hoist-pattern` and `hoist-pattern`: Control hoisting
Control which packages are hoisted to the root `node_modules`:
```ini .npmrc icon="npm"
# packages matching this pattern will be hoisted to the root
public-hoist-pattern=*eslint*
# multiple patterns
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*
# control general hoisting behavior
hoist-pattern=*
```

View File

@@ -14,7 +14,7 @@ It is strongly recommended to use [PowerShell 7 (`pwsh.exe`)](https://learn.micr
By default, running unverified scripts are blocked.
```ps1
> Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted
```
### System Dependencies
@@ -22,7 +22,7 @@ By default, running unverified scripts are blocked.
Bun v1.1 or later. We use Bun to run it's own code generators.
```ps1
> irm bun.sh/install.ps1 | iex
irm bun.sh/install.ps1 | iex
```
[Visual Studio](https://visualstudio.microsoft.com) with the "Desktop Development with C++" workload. While installing, make sure to install Git as well, if Git for Windows is not already installed.
@@ -30,7 +30,7 @@ Bun v1.1 or later. We use Bun to run it's own code generators.
Visual Studio can be installed graphically using the wizard or through WinGet:
```ps1
> winget install "Visual Studio Community 2022" --override "--add Microsoft.VisualStudio.Workload.NativeDesktop Microsoft.VisualStudio.Component.Git " -s msstore
winget install "Visual Studio Community 2022" --override "--add Microsoft.VisualStudio.Workload.NativeDesktop Microsoft.VisualStudio.Component.Git " -s msstore
```
After Visual Studio, you need the following:
@@ -48,10 +48,10 @@ After Visual Studio, you need the following:
[Scoop](https://scoop.sh) can be used to install these remaining tools easily.
```ps1 Scoop
> irm https://get.scoop.sh | iex
> scoop install nodejs-lts go rust nasm ruby perl sccache
irm https://get.scoop.sh | iex
scoop install nodejs-lts go rust nasm ruby perl ccache
# scoop seems to be buggy if you install llvm and the rest at the same time
> scoop install llvm@19.1.7
scoop install llvm@19.1.7
```
<Note>
@@ -63,19 +63,19 @@ After Visual Studio, you need the following:
If you intend on building WebKit locally (optional), you should install these packages:
```ps1 Scoop
> scoop install make cygwin python
scoop install make cygwin python
```
From here on out, it is **expected you use a PowerShell Terminal with `.\scripts\vs-shell.ps1` sourced**. This script is available in the Bun repository and can be loaded by executing it:
```ps1
> .\scripts\vs-shell.ps1
.\scripts\vs-shell.ps1
```
To verify, you can check for an MSVC-only command line such as `mt.exe`
```ps1
> Get-Command mt
Get-Command mt
```
<Note>
@@ -86,16 +86,16 @@ To verify, you can check for an MSVC-only command line such as `mt.exe`
## Building
```ps1
> bun run build
bun run build
# after the initial `bun run build` you can use the following to build
> ninja -Cbuild/debug
ninja -Cbuild/debug
```
If this was successful, you should have a `bun-debug.exe` in the `build/debug` folder.
```ps1
> .\build\debug\bun-debug.exe --revision
.\build\debug\bun-debug.exe --revision
```
You should add this to `$Env:PATH`. The simplest way to do so is to open the start menu, type "Path", and then navigate the environment variables menu to add `C:\.....\bun\build\debug` to the user environment variable `PATH`. You should then restart your editor (if it does not update still, log out and log back in).
@@ -111,15 +111,15 @@ You can run the test suite either using `bun test <path>` or by using the wrappe
```ps1
# Setup
> bun i --cwd packages\bun-internal-test
bun i --cwd packages\bun-internal-test
# Run the entire test suite with reporter
# the package.json script "test" uses "build/debug/bun-debug.exe" by default
> bun run test
bun run test
# Run an individual test file:
> bun-debug test node\fs
> bun-debug test "C:\bun\test\js\bun\resolve\import-meta.test.js"
bun-debug test node\fs
bun-debug test "C:\bun\test\js\bun\resolve\import-meta.test.js"
```
## Troubleshooting

View File

@@ -28,23 +28,23 @@ Using your system's package manager, install Bun's dependencies:
<CodeGroup>
```bash macOS (Homebrew)
$ brew install automake cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rust ruby sccache
brew install automake ccache cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rust ruby
```
```bash Ubuntu/Debian
$ sudo apt install curl wget lsb-release software-properties-common cargo cmake git golang libtool ninja-build pkg-config rustc ruby-full xz-utils
sudo apt install curl wget lsb-release software-properties-common cargo cmake git golang libtool ninja-build pkg-config rustc ruby-full xz-utils
```
```bash Arch
$ sudo pacman -S base-devel cmake git go libiconv libtool make ninja pkg-config python rust sed unzip ruby
sudo pacman -S base-devel cmake git go libiconv libtool make ninja pkg-config python rust sed unzip ruby
```
```bash Fedora
$ sudo dnf install cargo clang19 llvm19 lld19 cmake git golang libtool ninja-build pkg-config rustc ruby libatomic-static libstdc++-static sed unzip which libicu-devel 'perl(Math::BigInt)'
sudo dnf install cargo clang19 llvm19 lld19 cmake git golang libtool ninja-build pkg-config rustc ruby libatomic-static libstdc++-static sed unzip which libicu-devel 'perl(Math::BigInt)'
```
```bash openSUSE Tumbleweed
$ sudo zypper install go cmake ninja automake git icu rustup && rustup toolchain install stable
sudo zypper install go cmake ninja automake git icu rustup && rustup toolchain install stable
```
</CodeGroup>
@@ -56,59 +56,42 @@ Before starting, you will need to already have a release build of Bun installed,
<CodeGroup>
```bash Native
$ curl -fsSL https://bun.com/install | bash
curl -fsSL https://bun.com/install | bash
```
```bash npm
$ npm install -g bun
npm install -g bun
```
```bash Homebrew
$ brew tap oven-sh/bun
$ brew install bun
brew tap oven-sh/bun
brew install bun
```
</CodeGroup>
### Optional: Install `sccache`
### Optional: Install `ccache`
sccache is used to cache compilation artifacts, significantly speeding up builds. It must be installed with S3 support:
ccache is used to cache compilation artifacts, significantly speeding up builds:
```bash
# For macOS
$ brew install sccache
brew install ccache
# For Linux. Note that the version in your package manager may not have S3 support.
$ cargo install sccache --features=s3
# For Ubuntu/Debian
sudo apt install ccache
# For Arch
sudo pacman -S ccache
# For Fedora
sudo dnf install ccache
# For openSUSE
sudo zypper install ccache
```
This will install `sccache` with S3 support. Our build scripts will automatically detect and use `sccache` with our shared S3 cache. **Note**: Not all versions of `sccache` are compiled with S3 support, hence we recommend installing it via `cargo`.
#### Registering AWS Credentials for `sccache` (Core Developers Only)
Core developers have write access to the shared S3 cache. To enable write access, you must log in with AWS credentials. The easiest way to do this is to use the [`aws` CLI](https://aws.amazon.com/cli/) and invoke [`aws configure` to provide your AWS security info](https://docs.aws.amazon.com/cli/latest/reference/configure/).
The `cmake` scripts should automatically detect your AWS credentials from the environment or the `~/.aws/credentials` file.
<details>
<summary>Logging in to the `aws` CLI</summary>
1. Install the AWS CLI by following [the official guide](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html).
2. Log in to your AWS account console. A team member should provide you with your credentials.
3. Click your name in the top right > Security credentials.
4. Scroll to "Access keys" and create a new access key.
5. Run `aws configure` in your terminal and provide the access key ID and secret access key when prompted.
</details>
<details>
<summary>Common Issues You May Encounter</summary>
- To confirm that the cache is being used, you can use the `sccache --show-stats` command right after a build. This will expose very useful statistics, including cache hits/misses.
- If you have multiple AWS profiles configured, ensure that the correct profile is set in the `AWS_PROFILE` environment variable.
- `sccache` follows a server-client model. If you run into weird issues where `sccache` refuses to use S3, even though you have AWS credentials configured, try killing any running `sccache` servers with `sccache --stop-server` and then re-running the build.
</details>
Our build scripts will automatically detect and use `ccache` if available. You can check cache statistics with `ccache --show-stats`.
## Install LLVM
@@ -117,24 +100,24 @@ Bun requires LLVM 19 (`clang` is part of LLVM). This version requirement is to m
<CodeGroup>
```bash macOS (Homebrew)
$ brew install llvm@19
brew install llvm@19
```
```bash Ubuntu/Debian
$ # LLVM has an automatic installation script that is compatible with all versions of Ubuntu
$ wget https://apt.llvm.org/llvm.sh -O - | sudo bash -s -- 19 all
# LLVM has an automatic installation script that is compatible with all versions of Ubuntu
wget https://apt.llvm.org/llvm.sh -O - | sudo bash -s -- 19 all
```
```bash Arch
$ sudo pacman -S llvm clang lld
sudo pacman -S llvm clang lld
```
```bash Fedora
$ sudo dnf install llvm clang lld-devel
sudo dnf install llvm clang lld-devel
```
```bash openSUSE Tumbleweed
$ sudo zypper install clang19 lld19 llvm19
sudo zypper install clang19 lld19 llvm19
```
</CodeGroup>
@@ -144,7 +127,7 @@ If none of the above solutions apply, you will have to install it [manually](htt
Make sure Clang/LLVM 19 is in your path:
```bash
$ which clang-19
which clang-19
```
If not, run this to manually add it:
@@ -154,12 +137,12 @@ If not, run this to manually add it:
```bash macOS (Homebrew)
# use fish_add_path if you're using fish
# use path+="$(brew --prefix llvm@19)/bin" if you are using zsh
$ export PATH="$(brew --prefix llvm@19)/bin:$PATH"
export PATH="$(brew --prefix llvm@19)/bin:$PATH"
```
```bash Arch
# use fish_add_path if you're using fish
$ export PATH="$PATH:/usr/lib/llvm19/bin"
export PATH="$PATH:/usr/lib/llvm19/bin"
```
</CodeGroup>
@@ -179,7 +162,7 @@ bun run build
The binary will be located at `./build/debug/bun-debug`. It is recommended to add this to your `$PATH`. To verify the build worked, let's print the version number on the development build of Bun.
```bash
$ build/debug/bun-debug --version
build/debug/bun-debug --version
x.y.z_debug
```
@@ -278,17 +261,17 @@ WebKit is not cloned by default (to save time and disk space). To clone and buil
```bash
# Clone WebKit into ./vendor/WebKit
$ git clone https://github.com/oven-sh/WebKit vendor/WebKit
git clone https://github.com/oven-sh/WebKit vendor/WebKit
# Check out the commit hash specified in `set(WEBKIT_VERSION <commit_hash>)` in cmake/tools/SetupWebKit.cmake
$ git -C vendor/WebKit checkout <commit_hash>
git -C vendor/WebKit checkout <commit_hash>
# Make a debug build of JSC. This will output build artifacts in ./vendor/WebKit/WebKitBuild/Debug
# Optionally, you can use `bun run jsc:build` for a release build
bun run jsc:build:debug && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
# After an initial run of `make jsc-debug`, you can rebuild JSC with:
$ cmake --build vendor/WebKit/WebKitBuild/Debug --target jsc && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
cmake --build vendor/WebKit/WebKitBuild/Debug --target jsc && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
# Build bun with the local JSC build
bun run build:local
@@ -339,20 +322,20 @@ is not able to compile a simple test program.
To fix the error, we need to update the GCC version to 11. To do this, we'll need to check if the latest version is available in the distribution's official repositories or use a third-party repository that provides GCC 11 packages. Here are general steps:
```bash
$ sudo apt update
$ sudo apt install gcc-11 g++-11
sudo apt update
sudo apt install gcc-11 g++-11
# If the above command fails with `Unable to locate package gcc-11` we need
# to add the APT repository
$ sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
# Now run `apt install` again
$ sudo apt install gcc-11 g++-11
sudo apt install gcc-11 g++-11
```
Now, we need to set GCC 11 as the default compiler:
```bash
$ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100
$ sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 100
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 100
```
### libarchive
@@ -360,7 +343,7 @@ $ sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 100
If you see an error on macOS when compiling `libarchive`, run:
```bash
$ brew install pkg-config
brew install pkg-config
```
### macOS `library not found for -lSystem`
@@ -368,7 +351,7 @@ $ brew install pkg-config
If you see this error when compiling, run:
```bash
$ xcode-select --install
xcode-select --install
```
### Cannot find `libatomic.a`

452
docs/runtime/archive.mdx Normal file
View File

@@ -0,0 +1,452 @@
---
title: Archive
description: Create and extract tar archives with Bun's fast native implementation
---
Bun provides a fast, native implementation for working with tar archives through `Bun.Archive`. It supports creating archives from in-memory data, extracting archives to disk, and reading archive contents without extraction.
## Quickstart
**Create an archive from files:**
```ts
const archive = new Bun.Archive({
"hello.txt": "Hello, World!",
"data.json": JSON.stringify({ foo: "bar" }),
"nested/file.txt": "Nested content",
});
// Write to disk
await Bun.write("bundle.tar", archive);
```
**Extract an archive:**
```ts
const tarball = await Bun.file("package.tar.gz").bytes();
const archive = new Bun.Archive(tarball);
const entryCount = await archive.extract("./output");
console.log(`Extracted ${entryCount} entries`);
```
**Read archive contents without extracting:**
```ts
const tarball = await Bun.file("package.tar.gz").bytes();
const archive = new Bun.Archive(tarball);
const files = await archive.files();
for (const [path, file] of files) {
console.log(`${path}: ${await file.text()}`);
}
```
## Creating Archives
Use `new Bun.Archive()` to create an archive from an object where keys are file paths and values are file contents. By default, archives are uncompressed:
```ts
// Creates an uncompressed tar archive (default)
const archive = new Bun.Archive({
"README.md": "# My Project",
"src/index.ts": "console.log('Hello');",
"package.json": JSON.stringify({ name: "my-project" }),
});
```
File contents can be:
- **Strings** - Text content
- **Blobs** - Binary data
- **ArrayBufferViews** (e.g., `Uint8Array`) - Raw bytes
- **ArrayBuffers** - Raw binary data
```ts
const data = "binary data";
const arrayBuffer = new ArrayBuffer(8);
const archive = new Bun.Archive({
"text.txt": "Plain text",
"blob.bin": new Blob([data]),
"bytes.bin": new Uint8Array([1, 2, 3, 4]),
"buffer.bin": arrayBuffer,
});
```
### Writing Archives to Disk
Use `Bun.write()` to write an archive to disk:
```ts
// Write uncompressed tar (default)
const archive = new Bun.Archive({
"file1.txt": "content1",
"file2.txt": "content2",
});
await Bun.write("output.tar", archive);
// Write gzipped tar
const compressed = new Bun.Archive({ "src/index.ts": "console.log('Hello');" }, { compress: "gzip" });
await Bun.write("output.tar.gz", compressed);
```
### Getting Archive Bytes
Get the archive data as bytes or a Blob:
```ts
const archive = new Bun.Archive({ "hello.txt": "Hello, World!" });
// As Uint8Array
const bytes = await archive.bytes();
// As Blob
const blob = await archive.blob();
// With gzip compression (set at construction)
const gzipped = new Bun.Archive({ "hello.txt": "Hello, World!" }, { compress: "gzip" });
const gzippedBytes = await gzipped.bytes();
const gzippedBlob = await gzipped.blob();
```
## Extracting Archives
### From Existing Archive Data
Create an archive from existing tar/tar.gz data:
```ts
// From a file
const tarball = await Bun.file("package.tar.gz").bytes();
const archiveFromFile = new Bun.Archive(tarball);
```
```ts
// From a fetch response
const response = await fetch("https://example.com/archive.tar.gz");
const archiveFromFetch = new Bun.Archive(await response.blob());
```
### Extracting to Disk
Use `.extract()` to write all files to a directory:
```ts
const tarball = await Bun.file("package.tar.gz").bytes();
const archive = new Bun.Archive(tarball);
const count = await archive.extract("./extracted");
console.log(`Extracted ${count} entries`);
```
The target directory is created automatically if it doesn't exist. Existing files are overwritten. The returned count includes files, directories, and symlinks (on POSIX systems).
**Note**: On Windows, symbolic links in archives are always skipped during extraction. Bun does not attempt to create them regardless of privilege level. On Linux and macOS, symlinks are extracted normally.
**Security note**: Bun.Archive validates paths during extraction, rejecting absolute paths (POSIX `/`, Windows drive letters like `C:\` or `C:/`, and UNC paths like `\\server\share`). Path traversal components (`..`) are normalized away (e.g., `dir/sub/../file` becomes `dir/file`) to prevent directory escape attacks.
### Filtering Extracted Files
Use glob patterns to extract only specific files. Patterns are matched against archive entry paths normalized to use forward slashes (`/`). Positive patterns specify what to include, and negative patterns (prefixed with `!`) specify what to exclude. Negative patterns are applied after positive patterns, so **using only negative patterns will match nothing** (you must include a positive pattern like `**` first):
```ts
const tarball = await Bun.file("package.tar.gz").bytes();
const archive = new Bun.Archive(tarball);
// Extract only TypeScript files
const tsCount = await archive.extract("./extracted", { glob: "**/*.ts" });
// Extract files from multiple directories
const multiCount = await archive.extract("./extracted", {
glob: ["src/**", "lib/**"],
});
```
Use negative patterns (prefixed with `!`) to exclude files. When mixing positive and negative patterns, entries must match at least one positive pattern and not match any negative pattern:
```ts
// Extract everything except node_modules
const distCount = await archive.extract("./extracted", {
glob: ["**", "!node_modules/**"],
});
// Extract source files but exclude tests
const srcCount = await archive.extract("./extracted", {
glob: ["src/**", "!**/*.test.ts", "!**/__tests__/**"],
});
```
## Reading Archive Contents
### Get All Files
Use `.files()` to get archive contents as a `Map` of `File` objects without extracting to disk. Unlike `extract()` which processes all entry types, `files()` returns only regular files (no directories):
```ts
const tarball = await Bun.file("package.tar.gz").bytes();
const archive = new Bun.Archive(tarball);
const files = await archive.files();
for (const [path, file] of files) {
console.log(`${path}: ${file.size} bytes`);
console.log(await file.text());
}
```
Each `File` object includes:
- `name` - The file path within the archive (always uses forward slashes `/` as separators)
- `size` - File size in bytes
- `lastModified` - Modification timestamp
- Standard `Blob` methods: `text()`, `arrayBuffer()`, `stream()`, etc.
**Note**: `files()` loads file contents into memory. For large archives, consider using `extract()` to write directly to disk instead.
### Error Handling
Archive operations can fail due to corrupted data, I/O errors, or invalid paths. Use try/catch to handle these cases:
```ts
try {
const tarball = await Bun.file("package.tar.gz").bytes();
const archive = new Bun.Archive(tarball);
const count = await archive.extract("./output");
console.log(`Extracted ${count} entries`);
} catch (e: unknown) {
if (e instanceof Error) {
const error = e as Error & { code?: string };
if (error.code === "EACCES") {
console.error("Permission denied");
} else if (error.code === "ENOSPC") {
console.error("Disk full");
} else {
console.error("Archive error:", error.message);
}
} else {
console.error("Archive error:", String(e));
}
}
```
Common error scenarios:
- **Corrupted/truncated archives** - `new Archive()` loads the archive data; errors may be deferred until read/extract operations
- **Permission denied** - `extract()` throws if the target directory is not writable
- **Disk full** - `extract()` throws if there's insufficient space
- **Invalid paths** - Operations throw for malformed file paths
The count returned by `extract()` includes all successfully written entries (files, directories, and symlinks on POSIX systems).
**Security note**: Bun.Archive automatically validates paths during extraction. Absolute paths (POSIX `/`, Windows drive letters, UNC paths) and unsafe symlink targets are rejected. Path traversal components (`..`) are normalized away to prevent directory escape.
For additional security with untrusted archives, you can enumerate and validate paths before extraction:
```ts
const archive = new Bun.Archive(untrustedData);
const files = await archive.files();
// Optional: Custom validation for additional checks
for (const [path] of files) {
// Example: Reject hidden files
if (path.startsWith(".") || path.includes("/.")) {
throw new Error(`Hidden file rejected: ${path}`);
}
// Example: Whitelist specific directories
if (!path.startsWith("src/") && !path.startsWith("lib/")) {
throw new Error(`Unexpected path: ${path}`);
}
}
// Extract to a controlled destination
await archive.extract("./safe-output");
```
When using `files()` with a glob pattern, an empty `Map` is returned if no files match:
```ts
const matches = await archive.files("*.nonexistent");
if (matches.size === 0) {
console.log("No matching files found");
}
```
### Filtering with Glob Patterns
Pass a glob pattern to filter which files are returned:
```ts
// Get only TypeScript files
const tsFiles = await archive.files("**/*.ts");
// Get files in src directory
const srcFiles = await archive.files("src/*");
// Get all JSON files (recursive)
const jsonFiles = await archive.files("**/*.json");
// Get multiple file types with array of patterns
const codeFiles = await archive.files(["**/*.ts", "**/*.js"]);
```
Supported glob patterns (subset of [Bun.Glob](/docs/api/glob) syntax):
- `*` - Match any characters except `/`
- `**` - Match any characters including `/`
- `?` - Match single character
- `[abc]` - Match character set
- `{a,b}` - Match alternatives
- `!pattern` - Exclude files matching pattern (negation). Must be combined with positive patterns; using only negative patterns matches nothing.
See [Bun.Glob](/docs/api/glob) for the full glob syntax including escaping and advanced patterns.
## Compression
Bun.Archive creates uncompressed tar archives by default. Use `{ compress: "gzip" }` to enable gzip compression:
```ts
// Default: uncompressed tar
const archive = new Bun.Archive({ "hello.txt": "Hello, World!" });
// Reading: automatically detects gzip
const gzippedTarball = await Bun.file("archive.tar.gz").bytes();
const readArchive = new Bun.Archive(gzippedTarball);
// Enable gzip compression
const compressed = new Bun.Archive({ "hello.txt": "Hello, World!" }, { compress: "gzip" });
// Gzip with custom level (1-12)
const maxCompression = new Bun.Archive({ "hello.txt": "Hello, World!" }, { compress: "gzip", level: 12 });
```
The options accept:
- No options or `undefined` - Uncompressed tar (default)
- `{ compress: "gzip" }` - Enable gzip compression at level 6
- `{ compress: "gzip", level: number }` - Gzip with custom level 1-12 (1 = fastest, 12 = smallest)
## Examples
### Bundle Project Files
```ts
import { Glob } from "bun";
// Collect source files
const files: Record<string, string> = {};
const glob = new Glob("src/**/*.ts");
for await (const path of glob.scan(".")) {
// Normalize path separators to forward slashes for cross-platform compatibility
const archivePath = path.replaceAll("\\", "/");
files[archivePath] = await Bun.file(path).text();
}
// Add package.json
files["package.json"] = await Bun.file("package.json").text();
// Create compressed archive and write to disk
const archive = new Bun.Archive(files, { compress: "gzip" });
await Bun.write("bundle.tar.gz", archive);
```
### Extract and Process npm Package
```ts
const response = await fetch("https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz");
const archive = new Bun.Archive(await response.blob());
// Get package.json
const files = await archive.files("package/package.json");
const packageJson = files.get("package/package.json");
if (packageJson) {
const pkg = JSON.parse(await packageJson.text());
console.log(`Package: ${pkg.name}@${pkg.version}`);
}
```
### Create Archive from Directory
```ts
import { readdir } from "node:fs/promises";
import { join } from "node:path";
async function archiveDirectory(dir: string, compress = false): Promise<Bun.Archive> {
const files: Record<string, Blob> = {};
async function walk(currentDir: string, prefix: string = "") {
const entries = await readdir(currentDir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = join(currentDir, entry.name);
const archivePath = prefix ? `${prefix}/${entry.name}` : entry.name;
if (entry.isDirectory()) {
await walk(fullPath, archivePath);
} else {
files[archivePath] = Bun.file(fullPath);
}
}
}
await walk(dir);
return new Bun.Archive(files, compress ? { compress: "gzip" } : undefined);
}
const archive = await archiveDirectory("./my-project", true);
await Bun.write("my-project.tar.gz", archive);
```
## Reference
> **Note**: The following type signatures are simplified for documentation purposes. See [`packages/bun-types/bun.d.ts`](https://github.com/oven-sh/bun/blob/main/packages/bun-types/bun.d.ts) for the full type definitions.
```ts
type ArchiveInput =
| Record<string, string | Blob | Bun.ArrayBufferView | ArrayBufferLike>
| Blob
| Bun.ArrayBufferView
| ArrayBufferLike;
type ArchiveOptions = {
/** Compression algorithm. Currently only "gzip" is supported. */
compress?: "gzip";
/** Compression level 1-12 (default 6 when gzip is enabled). */
level?: number;
};
interface ArchiveExtractOptions {
/** Glob pattern(s) to filter extraction. Supports negative patterns with "!" prefix. */
glob?: string | readonly string[];
}
class Archive {
/**
* Create an Archive from input data
* @param data - Files to archive (as object) or existing archive data (as bytes/blob)
* @param options - Compression options. Uncompressed by default.
* Pass { compress: "gzip" } to enable compression.
*/
constructor(data: ArchiveInput, options?: ArchiveOptions);
/**
* Extract archive to a directory
* @returns Number of entries extracted (files, directories, and symlinks)
*/
extract(path: string, options?: ArchiveExtractOptions): Promise<number>;
/**
* Get archive as a Blob (uses compression setting from constructor)
*/
blob(): Promise<Blob>;
/**
* Get archive as a Uint8Array (uses compression setting from constructor)
*/
bytes(): Promise<Uint8Array<ArrayBuffer>>;
/**
* Get archive contents as File objects (regular files only, no directories)
*/
files(glob?: string | readonly string[]): Promise<Map<string, File>>;
}
```

View File

@@ -115,6 +115,26 @@ Currently we do not collect telemetry and this setting is only used for enabling
telemetry = false
```
### `env`
Configure automatic `.env` file loading. By default, Bun automatically loads `.env` files. To disable this behavior:
```toml title="bunfig.toml" icon="settings"
# Disable automatic .env file loading
env = false
```
You can also use object syntax with the `file` property:
```toml title="bunfig.toml" icon="settings"
[env]
file = false
```
This is useful in production environments or CI/CD pipelines where you want to rely solely on system environment variables.
Note: Explicitly provided environment files via `--env-file` will still be loaded even when default loading is disabled.
### `console`
Configure console output behavior.

View File

@@ -358,6 +358,8 @@ Bun represents [pointers](<https://en.wikipedia.org/wiki/Pointer_(computer_progr
**Why not `BigInt`?** `BigInt` is slower. JavaScript engines allocate a separate `BigInt` which means they can't fit into a regular JavaScript value. If you pass a `BigInt` to a function, it will be converted to a `number`
**Windows Note**: The Windows API type HANDLE does not represent a virtual address, and using `ptr` for it will _not_ work as expected. Use `u64` to safely represent HANDLE values.
</Accordion>
To convert from a `TypedArray` to a pointer:

View File

@@ -127,3 +127,54 @@ const socket = await Bun.udpSocket({
},
});
```
### Socket options
UDP sockets support setting various socket options:
```ts
const socket = await Bun.udpSocket({});
// Enable broadcasting to send packets to a broadcast address
socket.setBroadcast(true);
// Set the IP TTL (time to live) for outgoing packets
socket.setTTL(64);
```
### Multicast
Bun supports multicast operations for UDP sockets. Use `addMembership` and `dropMembership` to join and leave multicast groups:
```ts
const socket = await Bun.udpSocket({});
// Join a multicast group
socket.addMembership("224.0.0.1");
// Join with a specific interface
socket.addMembership("224.0.0.1", "192.168.1.100");
// Leave a multicast group
socket.dropMembership("224.0.0.1");
```
Additional multicast options:
```ts
// Set TTL for multicast packets (number of network hops)
socket.setMulticastTTL(2);
// Control whether multicast packets loop back to the local socket
socket.setMulticastLoopback(true);
// Specify which interface to use for outgoing multicast packets
socket.setMulticastInterface("192.168.1.100");
```
For source-specific multicast (SSM), use `addSourceSpecificMembership` and `dropSourceSpecificMembership`:
```ts
socket.addSourceSpecificMembership("10.0.0.1", "232.0.0.1");
socket.dropSourceSpecificMembership("10.0.0.1", "232.0.0.1");
```

View File

@@ -303,7 +303,7 @@ Internally, this calls [`sqlite3_reset`](https://www.sqlite.org/capi3ref.html#sq
### `.run()`
Use `.run()` to run a query and get back `undefined`. This is useful for schema-modifying queries (e.g. `CREATE TABLE`) or bulk write operations.
Use `.run()` to run a query and get back an object with execution metadata. This is useful for schema-modifying queries (e.g. `CREATE TABLE`) or bulk write operations.
```ts db.ts icon="/icons/typescript.svg" highlight={2}
const query = db.query(`create table foo;`);

View File

@@ -19,7 +19,7 @@ If you're looking to create a brand new empty project, use [`bun init`](/runtime
`bun create ./MyComponent.tsx` turns an existing React component into a complete dev environment with hot reload and production builds in one command.
```bash
$ bun create ./MyComponent.jsx # .tsx also supported
bun create ./MyComponent.jsx # .tsx also supported
```
<Frame>

View File

@@ -149,25 +149,25 @@ div.callout .code-block {
margin-bottom: 0px;
}
.code-block[language="shellscript"] code span.line:not(:empty):has(span)::before {
[language="shellscript"] code span.line:not(:empty):has(span)::before {
content: "$ ";
color: #6272a4;
user-select: none;
}
.code-block[language="shellscript"] code span.line:has(> span:first-child[style*="color: rgb(98, 114, 164)"])::before,
.code-block[language="shellscript"] code span.line:has(> span:first-child[style*="#6272A4"])::before {
[language="shellscript"] code span.line:has(> span:first-child[style*="color: rgb(98, 114, 164)"])::before,
[language="shellscript"] code span.line:has(> span:first-child[style*="#6272A4"])::before {
content: "";
}
.code-block[language="powershell"] code span.line:not(:empty):has(span)::before {
[language="powershell"] code span.line:not(:empty):has(span)::before {
content: "> ";
color: #6272a4;
user-select: none;
}
.code-block[language="powershell"] code span.line:has(> span:first-child[style*="color: rgb(98, 114, 164)"])::before,
.code-block[language="powershell"] code span.line:has(> span:first-child[style*="#6272A4"])::before {
[language="powershell"] code span.line:has(> span:first-child[style*="color: rgb(98, 114, 164)"])::before,
[language="powershell"] code span.line:has(> span:first-child[style*="#6272A4"])::before {
content: "";
}

View File

@@ -376,16 +376,18 @@ timeout = 10000
## Environment Variables
You can also set environment variables in your configuration that affect test behavior:
Environment variables for tests should be set using `.env` files. Bun automatically loads `.env` files from your project root. For test-specific variables, create a `.env.test` file:
```toml title="bunfig.toml" icon="settings"
[env]
NODE_ENV = "test"
DATABASE_URL = "postgresql://localhost:5432/test_db"
LOG_LEVEL = "error"
```ini title=".env.test" icon="settings"
NODE_ENV=test
DATABASE_URL=postgresql://localhost:5432/test_db
LOG_LEVEL=error
```
[test]
coverage = true
Then load it with `--env-file`:
```bash terminal icon="terminal"
bun test --env-file=.env.test
```
## Complete Configuration Example
@@ -398,13 +400,6 @@ Here's a comprehensive example showing all available test configuration options:
registry = "https://registry.npmjs.org/"
exact = true
[env]
# Environment variables for tests
NODE_ENV = "test"
DATABASE_URL = "postgresql://localhost:5432/test_db"
API_URL = "http://localhost:3001"
LOG_LEVEL = "error"
[test]
# Test discovery
root = "src"

View File

@@ -40,7 +40,7 @@
pkgs.cmake # Expected: 3.30+ on nixos-unstable as of 2025-10
pkgs.ninja
pkgs.pkg-config
pkgs.sccache
pkgs.ccache
# Compilers and toolchain - version pinned to LLVM 19
clang
@@ -131,6 +131,7 @@
stdenv = pkgs.clangStdenv;
}) {
inherit packages;
hardeningDisable = [ "fortify" ];
shellHook = ''
# Set up build environment

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "bun",
"version": "1.3.5",
"version": "1.3.7",
"workspaces": [
"./packages/bun-types",
"./packages/@types/bun"

View File

@@ -610,6 +610,97 @@ declare module "bun" {
*/
function stripANSI(input: string): string;
interface WrapAnsiOptions {
/**
* If `true`, break words in the middle if they don't fit on a line.
* If `false`, only break at word boundaries.
*
* @default false
*/
hard?: boolean;
/**
* If `true`, wrap at word boundaries when possible.
* If `false`, don't perform word wrapping (only wrap at explicit newlines).
*
* @default true
*/
wordWrap?: boolean;
/**
* If `true`, trim leading and trailing whitespace from each line.
* If `false`, preserve whitespace.
*
* @default true
*/
trim?: boolean;
/**
* When it's ambiguous and `true`, count ambiguous width characters as 1 character wide.
* If `false`, count them as 2 characters wide.
*
* @default true
*/
ambiguousIsNarrow?: boolean;
}
/**
* Wrap a string to fit within the specified column width, preserving ANSI escape codes.
*
* This function is designed to be compatible with the popular "wrap-ansi" NPM package.
*
* Features:
* - Preserves ANSI escape codes (colors, styles) across line breaks
* - Supports SGR codes (colors, bold, italic, etc.) and OSC 8 hyperlinks
* - Respects Unicode display widths (full-width characters, emoji)
* - Word wrapping at word boundaries (configurable)
*
* @category Utilities
*
* @param input The string to wrap
* @param columns The maximum column width
* @param options Wrapping options
* @returns The wrapped string
*
* @example
* ```ts
* import { wrapAnsi } from "bun";
*
* console.log(wrapAnsi("hello world", 5));
* // Output:
* // hello
* // world
*
* // Preserves ANSI colors across line breaks
* console.log(wrapAnsi("\u001b[31mhello world\u001b[0m", 5));
* // Output:
* // \u001b[31mhello\u001b[0m
* // \u001b[31mworld\u001b[0m
*
* // Hard wrap long words
* console.log(wrapAnsi("abcdefghij", 3, { hard: true }));
* // Output:
* // abc
* // def
* // ghi
* // j
* ```
*/
function wrapAnsi(
/**
* The string to wrap
*/
input: string,
/**
* The maximum column width
*/
columns: number,
/**
* Wrapping options
*/
options?: WrapAnsiOptions,
): string;
/**
* TOML related APIs
*/
@@ -625,6 +716,33 @@ declare module "bun" {
export function parse(input: string): object;
}
/**
* JSONC related APIs
*/
namespace JSONC {
/**
* Parse a JSONC (JSON with Comments) string into a JavaScript value.
*
* Supports both single-line (`//`) and block comments (`/* ... *\/`), as well as
* trailing commas in objects and arrays.
*
* @category Utilities
*
* @param input The JSONC string to parse
* @returns A JavaScript value
*
* @example
* ```js
* const result = Bun.JSONC.parse(`{
* // This is a comment
* "name": "my-app",
* "version": "1.0.0", // trailing comma is allowed
* }`);
* ```
*/
export function parse(input: string): unknown;
}
/**
* YAML related APIs
*/
@@ -723,7 +841,7 @@ declare module "bun" {
*/
function write(
destination: BunFile | S3File | PathLike,
input: Blob | NodeJS.TypedArray | ArrayBufferLike | string | BlobPart[],
input: Blob | NodeJS.TypedArray | ArrayBufferLike | string | BlobPart[] | Archive,
options?: {
/**
* If writing to a PathLike, set the permissions of the file.
@@ -815,6 +933,20 @@ declare module "bun" {
destination: BunFile,
input: BunFile,
options?: {
/**
* Set the file permissions of the destination when it is created or overwritten.
*
* Must be a valid Unix permission mode (0 to 0o777 / 511 in decimal).
* If omitted, defaults to the system default based on umask (typically 0o644).
*
* @throws {RangeError} If the mode is outside the valid range (0 to 0o777).
*
* @example
* ```ts
* await Bun.write(Bun.file("./secret.txt"), Bun.file("./source.txt"), { mode: 0o600 });
* ```
*/
mode?: number;
/**
* If `true`, create the parent directory if it doesn't exist. By default, this is `true`.
*
@@ -848,6 +980,20 @@ declare module "bun" {
destinationPath: PathLike,
input: BunFile,
options?: {
/**
* Set the file permissions of the destination when it is created or overwritten.
*
* Must be a valid Unix permission mode (0 to 0o777 / 511 in decimal).
* If omitted, defaults to the system default based on umask (typically 0o644).
*
* @throws {RangeError} If the mode is outside the valid range (0 to 0o777).
*
* @example
* ```ts
* await Bun.write("./secret.txt", Bun.file("./source.txt"), { mode: 0o600 });
* ```
*/
mode?: number;
/**
* If `true`, create the parent directory if it doesn't exist. By default, this is `true`.
*
@@ -1942,6 +2088,107 @@ declare module "bun" {
development?: boolean;
};
/**
* Enable React Fast Refresh transform.
*
* This adds the necessary code transformations for React Fast Refresh (hot module
* replacement for React components), but does not emit hot-module code itself.
*
* @default false
*/
reactFastRefresh?: boolean;
/**
* A map of file paths to their contents for in-memory bundling.
*
* This allows you to bundle virtual files that don't exist on disk, or override
* the contents of files that do exist on disk. The keys are file paths (which should
* match how they're imported) and the values are the file contents.
*
* File contents can be provided as:
* - `string` - The source code as a string
* - `Blob` - A Blob containing the source code
* - `NodeJS.TypedArray` - A typed array (e.g., `Uint8Array`) containing the source code
* - `ArrayBufferLike` - An ArrayBuffer containing the source code
*
* @example
* ```ts
* // Bundle entirely from memory (no files on disk needed)
* await Bun.build({
* entrypoints: ["/app/index.ts"],
* files: {
* "/app/index.ts": `
* import { helper } from "./helper.ts";
* console.log(helper());
* `,
* "/app/helper.ts": `
* export function helper() {
* return "Hello from memory!";
* }
* `,
* },
* });
* ```
*
* @example
* ```ts
* // Override a file on disk with in-memory contents
* await Bun.build({
* entrypoints: ["./src/index.ts"],
* files: {
* // This will be used instead of the actual ./src/config.ts file
* "./src/config.ts": `export const API_URL = "https://production.api.com";`,
* },
* });
* ```
*
* @example
* ```ts
* // Mix disk files with in-memory files
* // Entry point is on disk, but imports a virtual file
* await Bun.build({
* entrypoints: ["./src/index.ts"], // Real file on disk
* files: {
* // Virtual file that ./src/index.ts can import via "./generated.ts"
* "./src/generated.ts": `export const BUILD_TIME = ${Date.now()};`,
* },
* });
* ```
*/
files?: Record<string, string | Blob | NodeJS.TypedArray | ArrayBufferLike>;
/**
* Generate a JSON file containing metadata about the build.
*
* The metafile contains information about inputs, outputs, imports, and exports
* which can be used for bundle analysis, visualization, or integration with
* other tools.
*
* When `true`, the metafile JSON string is included in the {@link BuildOutput.metafile} property.
*
* @default false
*
* @example
* ```ts
* const result = await Bun.build({
* entrypoints: ['./src/index.ts'],
* outdir: './dist',
* metafile: true,
* });
*
* // Write metafile to disk for analysis
* if (result.metafile) {
* await Bun.write('./dist/meta.json', result.metafile);
* }
*
* // Parse and analyze the metafile
* const meta = JSON.parse(result.metafile!);
* console.log('Input files:', Object.keys(meta.inputs));
* console.log('Output files:', Object.keys(meta.outputs));
* ```
*/
metafile?: boolean;
outdir?: string;
}
@@ -1970,6 +2217,26 @@ declare module "bun" {
* @default true
*/
autoloadBunfig?: boolean;
/**
* Whether to autoload tsconfig.json when the standalone executable runs
*
* Standalone-only: applies only when building/running the standalone executable.
*
* Equivalent CLI flags: `--compile-autoload-tsconfig`, `--no-compile-autoload-tsconfig`
*
* @default false
*/
autoloadTsconfig?: boolean;
/**
* Whether to autoload package.json when the standalone executable runs
*
* Standalone-only: applies only when building/running the standalone executable.
*
* Equivalent CLI flags: `--compile-autoload-package-json`, `--no-compile-autoload-package-json`
*
* @default false
*/
autoloadPackageJson?: boolean;
windows?: {
hideConsole?: boolean;
icon?: string;
@@ -2573,6 +2840,106 @@ declare module "bun" {
outputs: BuildArtifact[];
success: boolean;
logs: Array<BuildMessage | ResolveMessage>;
/**
* Metadata about the build including inputs, outputs, and their relationships.
*
* Only present when {@link BuildConfig.metafile} is `true`.
*
* The metafile contains detailed information about:
* - **inputs**: All source files that were bundled, their byte sizes, imports, and format
* - **outputs**: All generated output files, their byte sizes, which inputs contributed to each output, imports between chunks, and exports
*
* This can be used for:
* - Bundle size analysis and visualization
* - Detecting unused code or dependencies
* - Understanding the dependency graph
* - Integration with bundle analyzer tools
*
* @example
* ```ts
* const result = await Bun.build({
* entrypoints: ['./src/index.ts'],
* outdir: './dist',
* metafile: true,
* });
*
* if (result.metafile) {
* // Analyze input files
* for (const [path, input] of Object.entries(result.metafile.inputs)) {
* console.log(`${path}: ${input.bytes} bytes, ${input.imports.length} imports`);
* }
*
* // Analyze output files
* for (const [path, output] of Object.entries(result.metafile.outputs)) {
* console.log(`${path}: ${output.bytes} bytes`);
* for (const [inputPath, info] of Object.entries(output.inputs)) {
* console.log(` - ${inputPath}: ${info.bytesInOutput} bytes`);
* }
* }
*
* // Write to disk for external analysis tools
* await Bun.write('./dist/meta.json', JSON.stringify(result.metafile));
* }
* ```
*/
metafile?: BuildMetafile;
}
/**
* Metafile structure containing build metadata for analysis.
*
* @category Bundler
*/
interface BuildMetafile {
/** Information about all input source files */
inputs: {
[path: string]: {
/** Size of the input file in bytes */
bytes: number;
/** List of imports from this file */
imports: Array<{
/** Resolved path of the imported file */
path: string;
/** Type of import statement */
kind: ImportKind;
/** Original import specifier before resolution (if different from path) */
original?: string;
/** Whether this import is external to the bundle */
external?: boolean;
/** Import attributes (e.g., `{ type: "json" }`) */
with?: Record<string, string>;
}>;
/** Module format of the input file */
format?: "esm" | "cjs" | "json" | "css";
};
};
/** Information about all output files */
outputs: {
[path: string]: {
/** Size of the output file in bytes */
bytes: number;
/** Map of input files to their contribution in this output */
inputs: {
[path: string]: {
/** Number of bytes this input contributed to the output */
bytesInOutput: number;
};
};
/** List of imports to other chunks */
imports: Array<{
/** Path to the imported chunk */
path: string;
/** Type of import */
kind: ImportKind;
}>;
/** List of exported names from this output */
exports: string[];
/** Entry point path if this output is an entry point */
entryPoint?: string;
/** Path to the associated CSS bundle (for JS entry points with CSS) */
cssBundle?: string;
};
};
}
/**
@@ -3042,16 +3409,29 @@ declare module "bun" {
type WebSocketOptionsTLS = {
/**
* Options for the TLS connection
* Options for the TLS connection.
*
* Supports full TLS configuration including custom CA certificates,
* client certificates, and other TLS settings (same as fetch).
*
* @example
* ```ts
* // Using BunFile for certificates
* const ws = new WebSocket("wss://example.com", {
* tls: {
* ca: Bun.file("./ca.pem")
* }
* });
*
* // Using Buffer
* const ws = new WebSocket("wss://example.com", {
* tls: {
* ca: fs.readFileSync("./ca.pem")
* }
* });
* ```
*/
tls?: {
/**
* Whether to reject the connection if the certificate is not valid
*
* @default true
*/
rejectUnauthorized?: boolean;
};
tls?: TLSOptions;
};
type WebSocketOptionsHeaders = {
@@ -3061,10 +3441,57 @@ declare module "bun" {
headers?: import("node:http").OutgoingHttpHeaders;
};
type WebSocketOptionsProxy = {
/**
* HTTP proxy to use for the WebSocket connection.
*
* Can be a string URL or an object with `url` and optional `headers`.
*
* @example
* ```ts
* // String format
* const ws = new WebSocket("wss://example.com", {
* proxy: "http://proxy.example.com:8080"
* });
*
* // With credentials
* const ws = new WebSocket("wss://example.com", {
* proxy: "http://user:pass@proxy.example.com:8080"
* });
*
* // Object format with custom headers
* const ws = new WebSocket("wss://example.com", {
* proxy: {
* url: "http://proxy.example.com:8080",
* headers: {
* "Proxy-Authorization": "Bearer token"
* }
* }
* });
* ```
*/
proxy?:
| string
| {
/**
* The proxy URL (http:// or https://)
*/
url: string;
/**
* Custom headers to send to the proxy server.
* Supports plain objects or Headers class instances.
*/
headers?: import("node:http").OutgoingHttpHeaders | Headers;
};
};
/**
* Constructor options for the `Bun.WebSocket` client
*/
type WebSocketOptions = WebSocketOptionsProtocolsOrProtocol & WebSocketOptionsTLS & WebSocketOptionsHeaders;
type WebSocketOptions = WebSocketOptionsProtocolsOrProtocol &
WebSocketOptionsTLS &
WebSocketOptionsHeaders &
WebSocketOptionsProxy;
interface WebSocketEventMap {
close: CloseEvent;
@@ -5308,6 +5735,67 @@ declare module "bun" {
ref(): void;
unref(): void;
close(): void;
/**
* Enable or disable SO_BROADCAST socket option.
* @param enabled Whether to enable broadcast
* @returns The enabled value
*/
setBroadcast(enabled: boolean): boolean;
/**
* Set the IP_TTL socket option.
* @param ttl Time to live value
* @returns The TTL value
*/
setTTL(ttl: number): number;
/**
* Set the IP_MULTICAST_TTL socket option.
* @param ttl Time to live value for multicast packets
* @returns The TTL value
*/
setMulticastTTL(ttl: number): number;
/**
* Enable or disable IP_MULTICAST_LOOP socket option.
* @param enabled Whether to enable multicast loopback
* @returns The enabled value
*/
setMulticastLoopback(enabled: boolean): boolean;
/**
* Set the IP_MULTICAST_IF socket option to specify the outgoing interface
* for multicast packets.
* @param interfaceAddress The address of the interface to use
* @returns true on success
*/
setMulticastInterface(interfaceAddress: string): boolean;
/**
* Join a multicast group.
* @param multicastAddress The multicast group address
* @param interfaceAddress Optional interface address to use
* @returns true on success
*/
addMembership(multicastAddress: string, interfaceAddress?: string): boolean;
/**
* Leave a multicast group.
* @param multicastAddress The multicast group address
* @param interfaceAddress Optional interface address to use
* @returns true on success
*/
dropMembership(multicastAddress: string, interfaceAddress?: string): boolean;
/**
* Join a source-specific multicast group.
* @param sourceAddress The source address
* @param groupAddress The multicast group address
* @param interfaceAddress Optional interface address to use
* @returns true on success
*/
addSourceSpecificMembership(sourceAddress: string, groupAddress: string, interfaceAddress?: string): boolean;
/**
* Leave a source-specific multicast group.
* @param sourceAddress The source address
* @param groupAddress The multicast group address
* @param interfaceAddress Optional interface address to use
* @returns true on success
*/
dropSourceSpecificMembership(sourceAddress: string, groupAddress: string, interfaceAddress?: string): boolean;
}
export interface ConnectedSocket<DataBinaryType extends BinaryType> extends BaseUDPSocket {
@@ -6568,6 +7056,356 @@ declare module "bun" {
match(str: string): boolean;
}
/**
* Input data for creating an archive. Can be:
* - An object mapping paths to file contents (string, Blob, TypedArray, or ArrayBuffer)
* - A Blob containing existing archive data
* - A TypedArray or ArrayBuffer containing existing archive data
*/
type ArchiveInput = Record<string, BlobPart> | Blob | ArrayBufferView | ArrayBufferLike;
/**
* Compression format for archive output.
* Currently only `"gzip"` is supported.
*/
type ArchiveCompression = "gzip";
/**
* Options for creating an Archive instance.
*
* By default, archives are not compressed. Use `{ compress: "gzip" }` to enable compression.
*
* @example
* ```ts
* // No compression (default)
* new Bun.Archive(data);
*
* // Enable gzip with default level (6)
* new Bun.Archive(data, { compress: "gzip" });
*
* // Specify compression level
* new Bun.Archive(data, { compress: "gzip", level: 9 });
* ```
*/
interface ArchiveOptions {
/**
* Compression algorithm to use.
* Currently only "gzip" is supported.
* If not specified, no compression is applied.
*/
compress?: ArchiveCompression;
/**
* Compression level (1-12). Only applies when `compress` is set.
* - 1: Fastest compression, lowest ratio
* - 6: Default balance of speed and ratio
* - 12: Best compression ratio, slowest
*
* @default 6
*/
level?: number;
}
/**
* Options for extracting archive contents.
*/
interface ArchiveExtractOptions {
/**
* Glob pattern(s) to filter which entries are extracted.
* Uses the same syntax as {@link Bun.Glob}, including support for wildcards (`*`, `**`),
* character classes (`[abc]`), alternation (`{a,b}`), and negation (`!pattern`).
*
* Patterns are matched against archive entry paths normalized to use forward slashes (`/`),
* regardless of the host operating system. Always write patterns using `/` as the separator.
*
* - Positive patterns: Only entries matching at least one pattern will be extracted.
* - Negative patterns (prefixed with `!`): Entries matching these patterns will be excluded.
* Negative patterns are applied after positive patterns.
*
* If not specified, all entries are extracted.
*
* @example
* ```ts
* // Extract only TypeScript files
* await archive.extract("./out", { glob: "**" + "/*.ts" });
*
* // Extract files from multiple directories
* await archive.extract("./out", { glob: ["src/**", "lib/**"] });
*
* // Exclude node_modules using negative pattern
* await archive.extract("./out", { glob: ["**", "!node_modules/**"] });
*
* // Extract source files but exclude tests
* await archive.extract("./out", { glob: ["src/**", "!**" + "/*.test.ts"] });
* ```
*/
glob?: string | readonly string[];
}
/**
* A class for creating and extracting tar archives with optional gzip compression.
*
* `Bun.Archive` provides a fast, native implementation for working with tar archives.
* It supports creating archives from in-memory data or extracting existing archives
* to disk or memory.
*
* @example
* **Create an archive from an object:**
* ```ts
* const archive = new Bun.Archive({
* "hello.txt": "Hello, World!",
* "data.json": JSON.stringify({ foo: "bar" }),
* "binary.bin": new Uint8Array([1, 2, 3, 4]),
* });
* ```
*
* @example
* **Create a gzipped archive:**
* ```ts
* const archive = new Bun.Archive({
* "hello.txt": "Hello, World!",
* }, { compress: "gzip" });
*
* // Or with a specific compression level (1-12)
* const archive = new Bun.Archive(data, { compress: "gzip", level: 9 });
* ```
*
* @example
* **Extract an archive to disk:**
* ```ts
* const archive = new Bun.Archive(tarballBytes);
* const entryCount = await archive.extract("./output");
* console.log(`Extracted ${entryCount} entries`);
* ```
*
* @example
* **Get archive contents as a Map of File objects:**
* ```ts
* const archive = new Bun.Archive(tarballBytes);
* const entries = await archive.files();
* for (const [path, file] of entries) {
* console.log(path, await file.text());
* }
* ```
*
* @example
* **Write a gzipped archive directly to disk:**
* ```ts
* await Bun.Archive.write("bundle.tar.gz", {
* "src/index.ts": sourceCode,
* "package.json": packageJson,
* }, { compress: "gzip" });
* ```
*/
export class Archive {
/**
* Create an `Archive` instance from input data.
*
* By default, archives are not compressed. Use `{ compress: "gzip" }` to enable compression.
*
* @param data - The input data for the archive:
* - **Object**: Creates a new tarball with the object's keys as file paths and values as file contents
* - **Blob/TypedArray/ArrayBuffer**: Wraps existing archive data (tar or tar.gz)
* @param options - Optional archive options including compression settings.
* Defaults to no compression if omitted.
*
* @example
* **From an object (creates uncompressed tarball):**
* ```ts
* const archive = new Bun.Archive({
* "hello.txt": "Hello, World!",
* "nested/file.txt": "Nested content",
* });
* ```
*
* @example
* **With gzip compression:**
* ```ts
* const archive = new Bun.Archive(data, { compress: "gzip" });
* ```
*
* @example
* **With explicit gzip compression level:**
* ```ts
* const archive = new Bun.Archive(data, { compress: "gzip", level: 12 });
* ```
*
* @example
* **From existing archive data:**
* ```ts
* const response = await fetch("https://example.com/package.tar.gz");
* const archive = new Bun.Archive(await response.blob());
* ```
*/
constructor(data: ArchiveInput, options?: ArchiveOptions);
/**
* Create and write an archive directly to disk in one operation.
*
* This is more efficient than creating an archive and then writing it separately,
* as it streams the data directly to disk.
*
* @param path - The file path to write the archive to
* @param data - The input data for the archive (same as `new Archive()`)
* @param options - Optional archive options including compression settings
*
* @returns A promise that resolves when the write is complete
*
* @example
* **Write uncompressed tarball:**
* ```ts
* await Bun.Archive.write("output.tar", {
* "file1.txt": "content1",
* "file2.txt": "content2",
* });
* ```
*
* @example
* **Write gzipped tarball:**
* ```ts
* await Bun.Archive.write("output.tar.gz", files, { compress: "gzip" });
* ```
*/
static write(path: string, data: ArchiveInput | Archive, options?: ArchiveOptions): Promise<void>;
/**
* Extract the archive contents to a directory on disk.
*
* Creates the target directory and any necessary parent directories if they don't exist.
* Existing files will be overwritten.
*
* @param path - The directory path to extract to
* @param options - Optional extraction options
* @param options.glob - Glob pattern(s) to filter entries (positive patterns include, negative patterns starting with `!` exclude)
* @returns A promise that resolves with the number of entries extracted (files, directories, and symlinks)
*
* @example
* **Extract all entries:**
* ```ts
* const archive = new Bun.Archive(tarballBytes);
* const count = await archive.extract("./extracted");
* console.log(`Extracted ${count} entries`);
* ```
*
* @example
* **Extract only TypeScript files:**
* ```ts
* const count = await archive.extract("./src", { glob: "**" + "/*.ts" });
* ```
*
* @example
* **Extract everything except tests:**
* ```ts
* const count = await archive.extract("./dist", { glob: ["**", "!**" + "/*.test.*"] });
* ```
*
* @example
* **Extract source files but exclude tests:**
* ```ts
* const count = await archive.extract("./output", {
* glob: ["src/**", "lib/**", "!**" + "/*.test.ts", "!**" + "/__tests__/**"]
* });
* ```
*/
extract(path: string, options?: ArchiveExtractOptions): Promise<number>;
/**
* Get the archive contents as a `Blob`.
*
* Uses the compression settings specified when the Archive was created.
*
* @returns A promise that resolves with the archive data as a Blob
*
* @example
* **Get tarball as Blob:**
* ```ts
* const archive = new Bun.Archive(data);
* const blob = await archive.blob();
* ```
*
* @example
* **Get gzipped tarball as Blob:**
* ```ts
* const archive = new Bun.Archive(data, { compress: "gzip" });
* const gzippedBlob = await archive.blob();
* ```
*/
blob(): Promise<Blob>;
/**
* Get the archive contents as a `Uint8Array`.
*
* Uses the compression settings specified when the Archive was created.
*
* @returns A promise that resolves with the archive data as a Uint8Array
*
* @example
* **Get tarball bytes:**
* ```ts
* const archive = new Bun.Archive(data);
* const bytes = await archive.bytes();
* ```
*
* @example
* **Get gzipped tarball bytes:**
* ```ts
* const archive = new Bun.Archive(data, { compress: "gzip" });
* const gzippedBytes = await archive.bytes();
* ```
*/
bytes(): Promise<Uint8Array<ArrayBuffer>>;
/**
* Get the archive contents as a `Map` of `File` objects.
*
* Each file in the archive is returned as a `File` object with:
* - `name`: The file path within the archive
* - `lastModified`: The file's modification time from the archive
* - Standard Blob methods (`text()`, `arrayBuffer()`, `stream()`, etc.)
*
* Only regular files are included; directories are not returned.
* File contents are loaded into memory, so for large archives consider using `extract()` instead.
*
* @param glob - Optional glob pattern(s) to filter files. Supports the same syntax as {@link Bun.Glob},
* including negation patterns (prefixed with `!`). Patterns are matched against paths normalized
* to use forward slashes (`/`).
* @returns A promise that resolves with a Map where keys are file paths (always using forward slashes `/` as separators) and values are File objects
*
* @example
* **Get all files:**
* ```ts
* const entries = await archive.files();
* for (const [path, file] of entries) {
* console.log(`${path}: ${file.size} bytes`);
* }
* ```
*
* @example
* **Filter by glob pattern:**
* ```ts
* const tsFiles = await archive.files("**" + "/*.ts");
* const srcFiles = await archive.files(["src/**", "lib/**"]);
* ```
*
* @example
* **Exclude files with negative patterns:**
* ```ts
* // Get all source files except tests
* const srcFiles = await archive.files(["src/**", "!**" + "/*.test.ts"]);
* ```
*
* @example
* **Read file contents:**
* ```ts
* const entries = await archive.files();
* const readme = entries.get("README.md");
* if (readme) {
* console.log(await readme.text());
* }
* ```
*/
files(glob?: string | readonly string[]): Promise<Map<string, File>>;
}
/**
* Generate a UUIDv7, which is a sequential ID based on the current timestamp with a random component.
*

View File

@@ -11,9 +11,9 @@ declare module "bun" {
* If the file descriptor is not writable yet, the data is buffered.
*
* @param chunk The data to write
* @returns Number of bytes written
* @returns Number of bytes written or, if the write is pending, a Promise resolving to the number of bytes
*/
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number | Promise<number>;
/**
* Flush the internal buffer, committing the data to disk or the pipe.
*
@@ -78,9 +78,9 @@ declare module "bun" {
* If the network is not writable yet, the data is buffered.
*
* @param chunk The data to write
* @returns Number of bytes written
* @returns Number of bytes written or, if the write is pending, a Promise resolving to the number of bytes
*/
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number | Promise<number>;
/**
* Flush the internal buffer, committing the data to the network.
*
@@ -321,6 +321,30 @@ declare module "bun" {
| "SNOW"
| "STANDARD_IA";
/**
* When set to `true`, confirms that the requester knows they will be charged
* for the request and data transfer costs. Required for accessing objects
* in Requester Pays buckets.
*
* @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/RequesterPaysBuckets.html
*
* @example
* // Accessing a file in a Requester Pays bucket
* const file = s3.file("data.csv", {
* bucket: "requester-pays-bucket",
* requestPayer: true
* });
* const content = await file.text();
*
* @example
* // Uploading to a Requester Pays bucket
* await s3.write("output.json", data, {
* bucket: "requester-pays-bucket",
* requestPayer: true
* });
*/
requestPayer?: boolean;
/**
* @deprecated The size of the internal buffer in bytes. Defaults to 5 MiB. use `partSize` and `queueSize` instead.
*/
@@ -585,7 +609,17 @@ declare module "bun" {
* });
*/
write(
data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile | S3File | Blob,
data:
| string
| ArrayBufferView
| ArrayBuffer
| SharedArrayBuffer
| Request
| Response
| BunFile
| S3File
| Blob
| Archive,
options?: S3Options,
): Promise<number>;
@@ -896,7 +930,8 @@ declare module "bun" {
| BunFile
| S3File
| Blob
| File,
| File
| Archive,
options?: S3Options,
): Promise<number>;
@@ -946,7 +981,8 @@ declare module "bun" {
| BunFile
| S3File
| Blob
| File,
| File
| Archive,
options?: S3Options,
): Promise<number>;

View File

@@ -154,12 +154,6 @@ declare module "bun:sqlite" {
* | `bigint` | `INTEGER` |
* | `null` | `NULL` |
*
* @example
* ```ts
* db.run("CREATE TABLE foo (bar TEXT)");
* db.run("INSERT INTO foo VALUES (?)", ["baz"]);
* ```
*
* Useful for queries like:
* - `CREATE TABLE`
* - `INSERT INTO`
@@ -180,8 +174,14 @@ declare module "bun:sqlite" {
*
* @param sql The SQL query to run
* @param bindings Optional bindings for the query
* @returns A `Changes` object with `changes` and `lastInsertRowid` properties
*
* @returns `Database` instance
* @example
* ```ts
* db.run("CREATE TABLE foo (bar TEXT)");
* db.run("INSERT INTO foo VALUES (?)", ["baz"]);
* // => { changes: 1, lastInsertRowid: 1 }
* ```
*/
run<ParamsType extends SQLQueryBindings[]>(sql: string, ...bindings: ParamsType[]): Changes;
@@ -670,18 +670,19 @@ declare module "bun:sqlite" {
* Execute the prepared statement.
*
* @param params optional values to bind to the statement. If omitted, the statement is run with the last bound values or no parameters if there are none.
* @returns A `Changes` object with `changes` and `lastInsertRowid` properties
*
* @example
* ```ts
* const stmt = db.prepare("UPDATE foo SET bar = ?");
* stmt.run("baz");
* // => undefined
* const insert = db.prepare("INSERT INTO users (name) VALUES (?)");
* insert.run("Alice");
* // => { changes: 1, lastInsertRowid: 1 }
* insert.run("Bob");
* // => { changes: 1, lastInsertRowid: 2 }
*
* stmt.run();
* // => undefined
*
* stmt.run("foo");
* // => undefined
* const update = db.prepare("UPDATE users SET name = ? WHERE id = ?");
* update.run("Charlie", 1);
* // => { changes: 1, lastInsertRowid: 2 }
* ```
*
* The following types can be used when binding parameters:

View File

@@ -315,6 +315,15 @@ int us_internal_ssl_socket_is_closed(struct us_internal_ssl_socket_t *s) {
return us_socket_is_closed(0, &s->s);
}
int us_internal_ssl_socket_is_handshake_finished(struct us_internal_ssl_socket_t *s) {
if (!s || !s->ssl) return 0;
return SSL_is_init_finished(s->ssl);
}
int us_internal_ssl_socket_handshake_callback_has_fired(struct us_internal_ssl_socket_t *s) {
if (!s) return 0;
return s->handshake_state == HANDSHAKE_COMPLETED;
}
void us_internal_trigger_handshake_callback_econnreset(struct us_internal_ssl_socket_t *s) {
struct us_internal_ssl_socket_context_t *context =

View File

@@ -439,6 +439,8 @@ void *us_internal_ssl_socket_ext(us_internal_ssl_socket_r s);
void *us_internal_connecting_ssl_socket_ext(struct us_connecting_socket_t *c);
int us_internal_ssl_socket_is_shut_down(us_internal_ssl_socket_r s);
int us_internal_ssl_socket_is_closed(us_internal_ssl_socket_r s);
int us_internal_ssl_socket_is_handshake_finished(us_internal_ssl_socket_r s);
int us_internal_ssl_socket_handshake_callback_has_fired(us_internal_ssl_socket_r s);
void us_internal_ssl_socket_shutdown(us_internal_ssl_socket_r s);
struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_adopt_socket(

View File

@@ -457,6 +457,12 @@ int us_socket_is_shut_down(int ssl, us_socket_r s) nonnull_fn_decl;
/* Returns whether this socket has been closed. Only valid if memory has not yet been released. */
int us_socket_is_closed(int ssl, us_socket_r s) nonnull_fn_decl;
/* Returns 1 if the TLS handshake has completed, 0 otherwise. For non-SSL sockets, always returns 1. */
int us_socket_is_ssl_handshake_finished(int ssl, us_socket_r s) nonnull_fn_decl;
/* Returns 1 if the TLS handshake callback has been invoked, 0 otherwise. For non-SSL sockets, always returns 1. */
int us_socket_ssl_handshake_callback_has_fired(int ssl, us_socket_r s) nonnull_fn_decl;
/* Immediately closes the socket */
struct us_socket_t *us_socket_close(int ssl, us_socket_r s, int code, void *reason) __attribute__((nonnull(2)));

View File

@@ -128,6 +128,26 @@ int us_socket_is_closed(int ssl, struct us_socket_t *s) {
return s->flags.is_closed;
}
int us_socket_is_ssl_handshake_finished(int ssl, struct us_socket_t *s) {
#ifndef LIBUS_NO_SSL
if(ssl) {
return us_internal_ssl_socket_is_handshake_finished((struct us_internal_ssl_socket_t *) s);
}
#endif
// Non-SSL sockets are always "handshake finished"
return 1;
}
int us_socket_ssl_handshake_callback_has_fired(int ssl, struct us_socket_t *s) {
#ifndef LIBUS_NO_SSL
if(ssl) {
return us_internal_ssl_socket_handshake_callback_has_fired((struct us_internal_ssl_socket_t *) s);
}
#endif
// Non-SSL sockets are always "callback fired"
return 1;
}
int us_connecting_socket_is_closed(int ssl, struct us_connecting_socket_t *c) {
return c->closed;
}

View File

@@ -18,7 +18,7 @@
#pragma once
#ifndef UWS_HTTP_MAX_HEADERS_COUNT
#define UWS_HTTP_MAX_HEADERS_COUNT 100
#define UWS_HTTP_MAX_HEADERS_COUNT 200
#endif
// todo: HttpParser is in need of a few clean-ups and refactorings
@@ -30,6 +30,7 @@
#include <algorithm>
#include <climits>
#include <string_view>
#include <span>
#include <map>
#include "MoveOnlyFunction.h"
#include "ChunkedEncoding.h"
@@ -160,6 +161,13 @@ namespace uWS
std::map<std::string, unsigned short, std::less<>> *currentParameterOffsets = nullptr;
public:
/* Any data pipelined after the HTTP headers (before response).
* Used for Node.js compatibility: 'connect' and 'upgrade' events
* pass this as the 'head' Buffer parameter.
* WARNING: This points to data in the receive buffer and may be stack-allocated.
* Must be cloned before the request handler returns. */
std::span<const char> head;
bool isAncient()
{
return ancientHttp;
@@ -883,6 +891,8 @@ namespace uWS
/* If returned socket is not what we put in we need
* to break here as we either have upgraded to
* WebSockets or otherwise closed the socket. */
/* Store any remaining data as head for Node.js compat (connect/upgrade events) */
req->head = std::span<const char>(data, length);
void *returnedUser = requestHandler(user, req);
if (returnedUser != user) {
/* We are upgraded to WebSocket or otherwise broken */
@@ -928,9 +938,13 @@ namespace uWS
consumedTotal += emittable;
}
} else if(isConnectRequest) {
// This only server to mark that the connect request read all headers
// and can starting emitting data
// This only serves to mark that the connect request read all headers
// and can start emitting data. Don't try to parse remaining data as HTTP -
// it's pipelined data that we've already captured in req->head.
remainingStreamingBytes = STATE_IS_CHUNKED;
// Mark remaining data as consumed and break - it's not HTTP
consumedTotal += length;
break;
} else {
/* If we came here without a body; emit an empty data chunk to signal no data */
dataHandler(user, {}, true);

View File

@@ -313,7 +313,7 @@ function Install-Build-Essentials {
strawberryperl `
mingw
Install-Rust
Install-Sccache
Install-Ccache
# Needed to remap stack traces
Install-PdbAddr2line
Install-Llvm
@@ -376,8 +376,8 @@ function Install-Rust {
Add-To-Path "$rustPath\cargo\bin"
}
function Install-Sccache {
Install-Package sccache -Version "0.12.0"
function Install-Ccache {
Install-Package ccache
}
function Install-PdbAddr2line {

View File

@@ -1,5 +1,5 @@
#!/bin/sh
# Version: 24
# Version: 26
# A script that installs the dependencies needed to build and test Bun.
# This should work on macOS and Linux with a POSIX shell.
@@ -281,9 +281,6 @@ check_operating_system() {
Darwin)
os="darwin"
;;
FreeBSD)
os="freebsd"
;;
*)
error "Unsupported operating system: $os"
;;
@@ -346,11 +343,6 @@ check_operating_system() {
;;
esac
;;
freebsd)
. /etc/os-release
distro="$ID"
release="$VERSION_ID"
;;
esac
if [ -n "$distro" ]; then
@@ -434,9 +426,6 @@ check_package_manager() {
error "No package manager found. (apt, dnf, yum, apk)"
fi
;;
freebsd)
pm="pkg"
;;
esac
print "Package manager: $pm"
@@ -449,13 +438,6 @@ check_package_manager() {
apk)
package_manager update
;;
pkg)
# may need to switch betwen 'latest' and 'quarterly' depending on which repo www/chromium is in. check https://www.freshports.org/www/chromium/.
# mkdir -p /usr/local/etc/pkg/repos
# echo 'FreeBSD: { url: "pkg+http://pkg.FreeBSD.org/${ABI}/quarterly" }' > /usr/local/etc/pkg/repos/FreeBSD.conf
export ASSUME_ALWAYS_YES=yes
package_manager update -f
;;
esac
}
@@ -816,9 +798,6 @@ install_nodejs() {
linux)
nodejs_platform="linux"
;;
freebsd)
nodejs_platform="freebsd"
;;
*)
error "Unsupported OS for Node.js download: $os"
;;
@@ -837,12 +816,6 @@ install_nodejs() {
;;
esac
if [ "$os" = "freebsd" ]; then
# TODO: use nodejs_version_exact
install_packages "www/node$(nodejs_version)" "www/npm-node$(nodejs_version)"
return
fi
case "$abi" in
musl)
nodejs_mirror="https://bun-nodejs-release.s3.us-west-1.amazonaws.com"
@@ -932,10 +905,6 @@ install_nodejs() {
}
install_nodejs_headers() {
if [ "$os" = "freebsd" ]; then
return
fi
nodejs_version="$(nodejs_version_exact)"
nodejs_headers_tar="$(download_file "https://nodejs.org/download/release/v$nodejs_version/node-v$nodejs_version-headers.tar.gz")"
nodejs_headers_dir="$(dirname "$nodejs_headers_tar")"
@@ -950,10 +919,6 @@ install_nodejs_headers() {
}
setup_node_gyp_cache() {
if [ "$os" = "freebsd" ]; then
return
fi
nodejs_version="$1"
headers_source="$2"
@@ -992,11 +957,6 @@ bun_version_exact() {
}
install_bun() {
if [ "$os" = "freebsd" ]; then
# TODO: need to complete bun bootstrap for for this work
return
fi
install_packages unzip
case "$pm" in
@@ -1048,9 +1008,6 @@ install_cmake() {
--skip-license \
--prefix=/usr
;;
freebsd-pkg)
install_packages devel/cmake
;;
esac
}
@@ -1122,17 +1079,6 @@ install_build_essentials() {
ruby \
perl \
;;
freebsd)
install_packages \
devel/ninja \
devel/pkgconf \
lang/go \
devel/gmake \
lang/python3 \
devel/libtool \
lang/ruby33 \
perl5 \
;;
esac
install_cmake
@@ -1140,7 +1086,7 @@ install_build_essentials() {
install_osxcross
install_gcc
install_rust
install_sccache
install_ccache
install_docker
}
@@ -1162,7 +1108,8 @@ install_llvm() {
"clang-$(llvm_version)" \
"lld-$(llvm_version)" \
"llvm-$(llvm_version)-dev" \
"llvm-$(llvm_version)-tools"
"llvm-$(llvm_version)-tools" \
"libclang-rt-$(llvm_version)-dev"
else
bash="$(require bash)"
llvm_script="$(download_file "https://apt.llvm.org/llvm.sh")"
@@ -1184,13 +1131,6 @@ install_llvm() {
"llvm$(llvm_version)-dev" # Ensures llvm-symbolizer is installed
;;
esac
case "$os" in
freebsd)
# TODO: use llvm_version_exact
install_packages "devel/llvm$(llvm_version)"
;;
esac
}
install_gcc() {
@@ -1266,67 +1206,39 @@ install_gcc() {
execute_sudo ln -sf $(which llvm-symbolizer-$llvm_v) /usr/bin/llvm-symbolizer
}
install_sccache() {
case "$os" in
linux)
;;
freebsd)
cargo install sccache --locked --version 0.12.0
return
;;
*)
error "Unsupported platform: $os"
;;
install_ccache() {
case "$pm" in
apt)
install_packages ccache
;;
brew)
install_packages ccache
;;
apk)
install_packages ccache
;;
dnf|yum)
install_packages ccache
;;
zypper)
install_packages ccache
;;
esac
# Alright, look, this function is cobbled together but it's only as cobbled
# together as this whole script is.
#
# For some reason, move_to_bin doesn't work here due to permissions so I'm
# avoiding that function. It's also wrong with permissions and so on.
#
# Unfortunately, we cannot use install_packages since many package managers
# don't compile `sccache` with S3 support.
local opts=$-
set -ef
local sccache_http
sccache_http="https://github.com/mozilla/sccache/releases/download/v0.12.0/sccache-v0.12.0-$(uname -m)-unknown-linux-musl.tar.gz"
local file
file=$(download_file "$sccache_http")
local tmpdir
tmpdir=$(mktemp -d)
execute tar -xzf "$file" -C "$tmpdir"
execute_sudo install -m755 "$tmpdir/sccache-v0.12.0-$(uname -m)-unknown-linux-musl/sccache" "/usr/local/bin"
set +ef -"$opts"
}
install_rust() {
case "$distro" in
freebsd)
install_packages lang/rust
create_directory "$HOME/.cargo/bin"
append_to_path "$HOME/.cargo/bin"
;;
*)
rust_home="/opt/rust"
create_directory "$rust_home"
append_to_profile "export RUSTUP_HOME=$rust_home"
append_to_profile "export CARGO_HOME=$rust_home"
rust_home="/opt/rust"
create_directory "$rust_home"
append_to_profile "export RUSTUP_HOME=$rust_home"
append_to_profile "export CARGO_HOME=$rust_home"
sh="$(require sh)"
rustup_script=$(download_file "https://sh.rustup.rs")
execute "$sh" -lc "$rustup_script -y --no-modify-path"
append_to_path "$rust_home/bin"
sh="$(require sh)"
rustup_script=$(download_file "https://sh.rustup.rs")
execute "$sh" -lc "$rustup_script -y --no-modify-path"
append_to_path "$rust_home/bin"
# Ensure all rustup files are accessible (for CI builds where different users run builds)
grant_to_user "$rust_home"
;;
esac
# Ensure all rustup files are accessible (for CI builds where different users run builds)
grant_to_user "$rust_home"
case "$osxcross" in
1)
@@ -1443,9 +1355,6 @@ install_tailscale() {
execute_as_user go install tailscale.com/cmd/tailscale{,d}@latest
append_to_path "$home/go/bin"
;;
freebsd)
install_packages security/tailscale
;;
esac
}
@@ -1512,14 +1421,6 @@ create_buildkite_user() {
--home "$home" \
--disabled-password
;;
freebsd)
execute_sudo pw group add -n "$group"
execute_sudo pw user add \
-n "$user" \
-g "$group" \
-s "$(require sh)" \
-d "$home" \
;;
*)
execute_sudo useradd "$user" \
--system \
@@ -1545,9 +1446,7 @@ create_buildkite_user() {
done
# The following is necessary to configure buildkite to use a stable
# checkout directory. sccache hashes absolute paths into its cache keys,
# so if buildkite uses a different checkout path each time (which it does
# by default), sccache will be useless.
# checkout directory for ccache to be effective.
local opts=$-
set -ef
@@ -1702,17 +1601,6 @@ install_age() {
;;
esac
;;
freebsd)
case "$arch" in
x64)
age_arch="amd64"
age_hash="943a7510a9973a1e589b913a70228aa1361a63cde39e3ed581435a4d4802df29"
;;
*)
error "Unsupported platform: $os-$arch"
;;
esac
;;
*)
error "Unsupported platform: $os-$arch"
;;

View File

@@ -1,249 +0,0 @@
{
"lockfileVersion": 1,
"configVersion": 1,
"workspaces": {
"": {
"dependencies": {
"@aws-sdk/client-s3": "^3.928.0",
"@aws-sdk/property-provider": "^3.374.0",
},
},
},
"packages": {
"@aws-crypto/crc32": ["@aws-crypto/crc32@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg=="],
"@aws-crypto/crc32c": ["@aws-crypto/crc32c@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag=="],
"@aws-crypto/sha1-browser": ["@aws-crypto/sha1-browser@5.2.0", "", { "dependencies": { "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg=="],
"@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="],
"@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="],
"@aws-crypto/supports-web-crypto": ["@aws-crypto/supports-web-crypto@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg=="],
"@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
"@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.928.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.928.0", "@aws-sdk/credential-provider-node": "3.928.0", "@aws-sdk/middleware-bucket-endpoint": "3.922.0", "@aws-sdk/middleware-expect-continue": "3.922.0", "@aws-sdk/middleware-flexible-checksums": "3.928.0", "@aws-sdk/middleware-host-header": "3.922.0", "@aws-sdk/middleware-location-constraint": "3.922.0", "@aws-sdk/middleware-logger": "3.922.0", "@aws-sdk/middleware-recursion-detection": "3.922.0", "@aws-sdk/middleware-sdk-s3": "3.928.0", "@aws-sdk/middleware-ssec": "3.922.0", "@aws-sdk/middleware-user-agent": "3.928.0", "@aws-sdk/region-config-resolver": "3.925.0", "@aws-sdk/signature-v4-multi-region": "3.928.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@aws-sdk/util-user-agent-browser": "3.922.0", "@aws-sdk/util-user-agent-node": "3.928.0", "@aws-sdk/xml-builder": "3.921.0", "@smithy/config-resolver": "^4.4.2", "@smithy/core": "^3.17.2", "@smithy/eventstream-serde-browser": "^4.2.4", "@smithy/eventstream-serde-config-resolver": "^4.3.4", "@smithy/eventstream-serde-node": "^4.2.4", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/hash-blob-browser": "^4.2.5", "@smithy/hash-node": "^4.2.4", "@smithy/hash-stream-node": "^4.2.4", "@smithy/invalid-dependency": "^4.2.4", "@smithy/md5-js": "^4.2.4", "@smithy/middleware-content-length": "^4.2.4", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-retry": "^4.4.6", "@smithy/middleware-serde": "^4.2.4", "@smithy/middleware-stack": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/node-http-handler": "^4.4.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.5", "@smithy/util-defaults-mode-node": "^4.2.8", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/util-stream": "^4.5.5", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.4", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-lXhhmcBjYa+ea0kRs00aq3WUwiXggwJkLwcMzOOsbW3CVYQaNpT7hztkfn2S6Qna7ETzd8M5+XZP+BmQRVE0Sg=="],
"@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.928.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.928.0", "@aws-sdk/middleware-host-header": "3.922.0", "@aws-sdk/middleware-logger": "3.922.0", "@aws-sdk/middleware-recursion-detection": "3.922.0", "@aws-sdk/middleware-user-agent": "3.928.0", "@aws-sdk/region-config-resolver": "3.925.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@aws-sdk/util-user-agent-browser": "3.922.0", "@aws-sdk/util-user-agent-node": "3.928.0", "@smithy/config-resolver": "^4.4.2", "@smithy/core": "^3.17.2", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/hash-node": "^4.2.4", "@smithy/invalid-dependency": "^4.2.4", "@smithy/middleware-content-length": "^4.2.4", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-retry": "^4.4.6", "@smithy/middleware-serde": "^4.2.4", "@smithy/middleware-stack": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/node-http-handler": "^4.4.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.5", "@smithy/util-defaults-mode-node": "^4.2.8", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Efenb8zV2fJJDXmp2NE4xj8Ymhp4gVJCkQ6ixhdrpfQXgd2PODO7a20C2+BhFM6aGmN3m6XWYJ64ZyhXF4pAyQ=="],
"@aws-sdk/core": ["@aws-sdk/core@3.928.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@aws-sdk/xml-builder": "3.921.0", "@smithy/core": "^3.17.2", "@smithy/node-config-provider": "^4.3.4", "@smithy/property-provider": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/signature-v4": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-e28J2uKjy2uub4u41dNnmzAu0AN3FGB+LRcLN2Qnwl9Oq3kIcByl5sM8ZD+vWpNG+SFUrUasBCq8cMnHxwXZ4w=="],
"@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.928.0", "", { "dependencies": { "@aws-sdk/core": "3.928.0", "@aws-sdk/types": "3.922.0", "@smithy/property-provider": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-tB8F9Ti0/NFyFVQX8UQtgRik88evtHpyT6WfXOB4bAY6lEnEHA0ubJZmk9y+aUeoE+OsGLx70dC3JUsiiCPJkQ=="],
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.928.0", "", { "dependencies": { "@aws-sdk/core": "3.928.0", "@aws-sdk/types": "3.922.0", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/property-provider": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-67ynC/8UW9Y8Gn1ZZtC3OgcQDGWrJelHmkbgpmmxYUrzVhp+NINtz3wiTzrrBFhPH/8Uy6BxvhMfXhn0ptcMEQ=="],
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.928.0", "", { "dependencies": { "@aws-sdk/core": "3.928.0", "@aws-sdk/credential-provider-env": "3.928.0", "@aws-sdk/credential-provider-http": "3.928.0", "@aws-sdk/credential-provider-process": "3.928.0", "@aws-sdk/credential-provider-sso": "3.928.0", "@aws-sdk/credential-provider-web-identity": "3.928.0", "@aws-sdk/nested-clients": "3.928.0", "@aws-sdk/types": "3.922.0", "@smithy/credential-provider-imds": "^4.2.4", "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-WVWYyj+jox6mhKYp11mu8x1B6Xa2sLbXFHAv5K3Jg8CHvXYpePgTcYlCljq3d4XHC4Jl4nCcsdMtBahSpU9bAA=="],
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.928.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.928.0", "@aws-sdk/credential-provider-http": "3.928.0", "@aws-sdk/credential-provider-ini": "3.928.0", "@aws-sdk/credential-provider-process": "3.928.0", "@aws-sdk/credential-provider-sso": "3.928.0", "@aws-sdk/credential-provider-web-identity": "3.928.0", "@aws-sdk/types": "3.922.0", "@smithy/credential-provider-imds": "^4.2.4", "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-SdXVjxZOIXefIR/NJx+lyXOrn4m0ScTAU2JXpLsFCkW2Cafo6vTqHUghyO8vak/XQ8PpPqpLXVpGbAYFuIPW6Q=="],
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.928.0", "", { "dependencies": { "@aws-sdk/core": "3.928.0", "@aws-sdk/types": "3.922.0", "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-XL0juran8yhqwn0mreV+NJeHJOkcRBaExsvVn9fXWW37A4gLh4esSJxM2KbSNh0t+/Bk3ehBI5sL9xad+yRDuw=="],
"@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.928.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.928.0", "@aws-sdk/core": "3.928.0", "@aws-sdk/token-providers": "3.928.0", "@aws-sdk/types": "3.922.0", "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-md/y+ePDsO1zqPJrsOyPs4ciKmdpqLL7B0dln1NhqZPnKIS5IBfTqZJ5tJ9eTezqc7Tn4Dbg6HiuemcGvZTeFA=="],
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.928.0", "", { "dependencies": { "@aws-sdk/core": "3.928.0", "@aws-sdk/nested-clients": "3.928.0", "@aws-sdk/types": "3.922.0", "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-rd97nLY5e/nGOr73ZfsXD+H44iZ9wyGZTKt/2QkiBN3hot/idhgT9+XHsWhRi+o/dThQbpL8RkpAnpF+0ZGthw=="],
"@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/middleware-bucket-endpoint@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@aws-sdk/util-arn-parser": "3.893.0", "@smithy/node-config-provider": "^4.3.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-config-provider": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Dpr2YeOaLFqt3q1hocwBesynE3x8/dXZqXZRuzSX/9/VQcwYBFChHAm4mTAl4zuvArtDbLrwzWSxmOWYZGtq5w=="],
"@aws-sdk/middleware-expect-continue": ["@aws-sdk/middleware-expect-continue@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-xmnLWMtmHJHJBupSWMUEW1gyxuRIeQ1Ov2xa8Tqq77fPr4Ft2AluEwiDMaZIMHoAvpxWKEEt9Si59Li7GIA+bQ=="],
"@aws-sdk/middleware-flexible-checksums": ["@aws-sdk/middleware-flexible-checksums@3.928.0", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", "@aws-sdk/core": "3.928.0", "@aws-sdk/types": "3.922.0", "@smithy/is-array-buffer": "^4.2.0", "@smithy/node-config-provider": "^4.3.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-middleware": "^4.2.4", "@smithy/util-stream": "^4.5.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-9+aCRt7teItSIMbnGvOY+FhtJnW2ZBUbfD+ug29a/ZbobDfTwmtrmtgEIWdXryFaRbT03HHfaJ3a++lTw4osuw=="],
"@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-HPquFgBnq/KqKRVkiuCt97PmWbKtxQ5iUNLEc6FIviqOoZTmaYG3EDsIbuFBz9C4RHJU4FKLmHL2bL3FEId6AA=="],
"@aws-sdk/middleware-location-constraint": ["@aws-sdk/middleware-location-constraint@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-T4iqd7WQ2DDjCH/0s50mnhdoX+IJns83ZE+3zj9IDlpU0N2aq8R91IG890qTfYkUEdP9yRm0xir/CNed+v6Dew=="],
"@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-AkvYO6b80FBm5/kk2E636zNNcNgjztNNUxpqVx+huyGn9ZqGTzS4kLqW2hO6CBe5APzVtPCtiQsXL24nzuOlAg=="],
"@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@aws/lambda-invoke-store": "^0.1.1", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-TtSCEDonV/9R0VhVlCpxZbp/9sxQvTTRKzIf8LxW3uXpby6Wl8IxEciBJlxmSkoqxh542WRcko7NYODlvL/gDA=="],
"@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.928.0", "", { "dependencies": { "@aws-sdk/core": "3.928.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-arn-parser": "3.893.0", "@smithy/core": "^3.17.2", "@smithy/node-config-provider": "^4.3.4", "@smithy/protocol-http": "^5.3.4", "@smithy/signature-v4": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-stream": "^4.5.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-LTkjS6cpJ2PEtsottTKq7JxZV0oH+QJ12P/dGNPZL4URayjEMBVR/dp4zh835X/FPXzijga3sdotlIKzuFy9FA=="],
"@aws-sdk/middleware-ssec": ["@aws-sdk/middleware-ssec@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-eHvSJZTSRJO+/tjjGD6ocnPc8q9o3m26+qbwQTu/4V6yOJQ1q+xkDZNqwJQphL+CodYaQ7uljp8g1Ji/AN3D9w=="],
"@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.928.0", "", { "dependencies": { "@aws-sdk/core": "3.928.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@smithy/core": "^3.17.2", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-ESvcfLx5PtpdUM3ptCwb80toBTd3y5I4w5jaeOPHihiZr7jkRLE/nsaCKzlqscPs6UQ8xI0maav04JUiTskcHw=="],
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.928.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.928.0", "@aws-sdk/middleware-host-header": "3.922.0", "@aws-sdk/middleware-logger": "3.922.0", "@aws-sdk/middleware-recursion-detection": "3.922.0", "@aws-sdk/middleware-user-agent": "3.928.0", "@aws-sdk/region-config-resolver": "3.925.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@aws-sdk/util-user-agent-browser": "3.922.0", "@aws-sdk/util-user-agent-node": "3.928.0", "@smithy/config-resolver": "^4.4.2", "@smithy/core": "^3.17.2", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/hash-node": "^4.2.4", "@smithy/invalid-dependency": "^4.2.4", "@smithy/middleware-content-length": "^4.2.4", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-retry": "^4.4.6", "@smithy/middleware-serde": "^4.2.4", "@smithy/middleware-stack": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/node-http-handler": "^4.4.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.5", "@smithy/util-defaults-mode-node": "^4.2.8", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kXzfJkq2cD65KAHDe4hZCsnxcGGEWD5pjHqcZplwG4VFMa/iVn/mWrUY9QdadD2GBpXFNQbgOiKG3U2NkKu+4Q=="],
"@aws-sdk/property-provider": ["@aws-sdk/property-provider@3.374.0", "", { "dependencies": { "@smithy/property-provider": "^1.0.1", "tslib": "^2.5.0" } }, "sha512-UvsRnRtLD3g0tPMuiILJiGT9PIg4tgU3coRO4s+bAuT29mUiuViDAQB8DYPgaOtqA4qNFiWose+AMCpdxlOhRA=="],
"@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.925.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@smithy/config-resolver": "^4.4.2", "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-FOthcdF9oDb1pfQBRCfWPZhJZT5wqpvdAS5aJzB1WDZ+6EuaAhLzLH/fW1slDunIqq1PSQGG3uSnVglVVOvPHQ=="],
"@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.928.0", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "3.928.0", "@aws-sdk/types": "3.922.0", "@smithy/protocol-http": "^5.3.4", "@smithy/signature-v4": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-1+Ic8+MyqQy+OE6QDoQKVCIcSZO+ETmLLLpVS5yu0fihBU85B5HHU7iaKX1qX7lEaGPMpSN/mbHW0VpyQ0Xqaw=="],
"@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.928.0", "", { "dependencies": { "@aws-sdk/core": "3.928.0", "@aws-sdk/nested-clients": "3.928.0", "@aws-sdk/types": "3.922.0", "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-533NpTdUJNDi98zBwRp4ZpZoqULrAVfc0YgIy+8AZHzk0v7N+v59O0d2Du3YO6zN4VU8HU8766DgKiyEag6Dzg=="],
"@aws-sdk/types": ["@aws-sdk/types@3.922.0", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-eLA6XjVobAUAMivvM7DBL79mnHyrm+32TkXNWZua5mnxF+6kQCfblKKJvxMZLGosO53/Ex46ogim8IY5Nbqv2w=="],
"@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.893.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA=="],
"@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-endpoints": "^3.2.4", "tslib": "^2.6.2" } }, "sha512-4ZdQCSuNMY8HMlR1YN4MRDdXuKd+uQTeKIr5/pIM+g3TjInZoj8imvXudjcrFGA63UF3t92YVTkBq88mg58RXQ=="],
"@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.893.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg=="],
"@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@smithy/types": "^4.8.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-qOJAERZ3Plj1st7M4Q5henl5FRpE30uLm6L9edZqZXGR6c7ry9jzexWamWVpQ4H4xVAVmiO9dIEBAfbq4mduOA=="],
"@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.928.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.928.0", "@aws-sdk/types": "3.922.0", "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-s0jP67nQLLWVWfBtqTkZUkSWK5e6OI+rs+wFya2h9VLyWBFir17XSDI891s8HZKIVCEl8eBrup+hhywm4nsIAA=="],
"@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.921.0", "", { "dependencies": { "@smithy/types": "^4.8.1", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-LVHg0jgjyicKKvpNIEMXIMr1EBViESxcPkqfOlT+X1FkmUMTNZEEVF18tOJg4m4hV5vxtkWcqtr4IEeWa1C41Q=="],
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.1.1", "", {}, "sha512-RcLam17LdlbSOSp9VxmUu1eI6Mwxp+OwhD2QhiSNmNCzoDb0EeUXTD2n/WbcnrAYMGlmf05th6QYq23VqvJqpA=="],
"@smithy/abort-controller": ["@smithy/abort-controller@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA=="],
"@smithy/chunked-blob-reader": ["@smithy/chunked-blob-reader@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA=="],
"@smithy/chunked-blob-reader-native": ["@smithy/chunked-blob-reader-native@4.2.1", "", { "dependencies": { "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ=="],
"@smithy/config-resolver": ["@smithy/config-resolver@4.4.3", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw=="],
"@smithy/core": ["@smithy/core@3.18.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-vGSDXOJFZgOPTatSI1ly7Gwyy/d/R9zh2TO3y0JZ0uut5qQ88p9IaWaZYIWSSqtdekNM4CGok/JppxbAff4KcQ=="],
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ=="],
"@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.5", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.9.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Ogt4Zi9hEbIP17oQMd68qYOHUzmH47UkK7q7Gl55iIm9oKt27MUGrC5JfpMroeHjdkOliOA4Qt3NQ1xMq/nrlA=="],
"@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.5", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-HohfmCQZjppVnKX2PnXlf47CW3j92Ki6T/vkAT2DhBR47e89pen3s4fIa7otGTtrVxmj7q+IhH0RnC5kpR8wtw=="],
"@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-ibjQjM7wEXtECiT6my1xfiMH9IcEczMOS6xiCQXoUIYSj5b1CpBbJ3VYbdwDy8Vcg5JHN7eFpOCGk8nyZAltNQ=="],
"@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.5", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-+elOuaYx6F2H6x1/5BQP5ugv12nfJl66GhxON8+dWVUEDJ9jah/A0tayVdkLRP0AeSac0inYkDz5qBFKfVp2Gg=="],
"@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.5", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-G9WSqbST45bmIFaeNuP/EnC19Rhp54CcVdX9PDL1zyEB514WsDVXhlyihKlGXnRycmHNmVv88Bvvt4EYxWef/Q=="],
"@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.6", "", { "dependencies": { "@smithy/protocol-http": "^5.3.5", "@smithy/querystring-builder": "^4.2.5", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg=="],
"@smithy/hash-blob-browser": ["@smithy/hash-blob-browser@4.2.6", "", { "dependencies": { "@smithy/chunked-blob-reader": "^5.2.0", "@smithy/chunked-blob-reader-native": "^4.2.1", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8P//tA8DVPk+3XURk2rwcKgYwFvwGwmJH/wJqQiSKwXZtf/LiZK+hbUZmPj/9KzM+OVSwe4o85KTp5x9DUZTjw=="],
"@smithy/hash-node": ["@smithy/hash-node@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA=="],
"@smithy/hash-stream-node": ["@smithy/hash-stream-node@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6+do24VnEyvWcGdHXomlpd0m8bfZePpUKBy7m311n+JuRwug8J4dCanJdTymx//8mi0nlkflZBvJe+dEO/O12Q=="],
"@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A=="],
"@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
"@smithy/md5-js": ["@smithy/md5-js@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Bt6jpSTMWfjCtC0s79gZ/WZ1w90grfmopVOWqkI2ovhjpD5Q2XRXuecIPB9689L2+cCySMbaXDhBPU56FKNDNg=="],
"@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A=="],
"@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.7", "", { "dependencies": { "@smithy/core": "^3.18.0", "@smithy/middleware-serde": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-middleware": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-i8Mi8OuY6Yi82Foe3iu7/yhBj1HBRoOQwBSsUNYglJTNSFaWYTNM2NauBBs/7pq2sqkLRqeUXA3Ogi2utzpUlQ=="],
"@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/protocol-http": "^5.3.5", "@smithy/service-error-classification": "^4.2.5", "@smithy/smithy-client": "^4.9.3", "@smithy/types": "^4.9.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-E7Vc6WHCHlzDRTx1W0jZ6J1L6ziEV0PIWcUdmfL4y+c8r7WYr6I+LkQudaD8Nfb7C5c4P3SQ972OmXHtv6m/OA=="],
"@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-La1ldWTJTZ5NqQyPqnCNeH9B+zjFhrNoQIL1jTh4zuqXRlmXhxYHhMtI1/92OlnoAtp6JoN7kzuwhWoXrBwPqg=="],
"@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ=="],
"@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.5", "", { "dependencies": { "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg=="],
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.5", "", { "dependencies": { "@smithy/abort-controller": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/querystring-builder": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw=="],
"@smithy/property-provider": ["@smithy/property-provider@1.2.0", "", { "dependencies": { "@smithy/types": "^1.2.0", "tslib": "^2.5.0" } }, "sha512-qlJd9gT751i4T0t/hJAyNGfESfi08Fek8QiLcysoKPgR05qHhG0OYhlaCJHhpXy4ECW0lHyjvFM1smrCLIXVfw=="],
"@smithy/protocol-http": ["@smithy/protocol-http@5.3.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ=="],
"@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg=="],
"@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ=="],
"@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0" } }, "sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ=="],
"@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA=="],
"@smithy/signature-v4": ["@smithy/signature-v4@5.3.5", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w=="],
"@smithy/smithy-client": ["@smithy/smithy-client@4.9.3", "", { "dependencies": { "@smithy/core": "^3.18.0", "@smithy/middleware-endpoint": "^4.3.7", "@smithy/middleware-stack": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-8tlueuTgV5n7inQCkhyptrB3jo2AO80uGrps/XTYZivv5MFQKKBj3CIWIGMI2fRY5LEduIiazOhAWdFknY1O9w=="],
"@smithy/types": ["@smithy/types@4.9.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA=="],
"@smithy/url-parser": ["@smithy/url-parser@4.2.5", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ=="],
"@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
"@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="],
"@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.2.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA=="],
"@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
"@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="],
"@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.6", "", { "dependencies": { "@smithy/property-provider": "^4.2.5", "@smithy/smithy-client": "^4.9.3", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-kbpuXbEf2YQ9zEE6eeVnUCQWO0e1BjMnKrXL8rfXgiWA0m8/E0leU4oSNzxP04WfCmW8vjEqaDeXWxwE4tpOjQ=="],
"@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.9", "", { "dependencies": { "@smithy/config-resolver": "^4.4.3", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/smithy-client": "^4.9.3", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-dgyribrVWN5qE5usYJ0m5M93mVM3L3TyBPZWe1Xl6uZlH2gzfQx3dz+ZCdW93lWqdedJRkOecnvbnoEEXRZ5VQ=="],
"@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A=="],
"@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
"@smithy/util-middleware": ["@smithy/util-middleware@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA=="],
"@smithy/util-retry": ["@smithy/util-retry@4.2.5", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg=="],
"@smithy/util-stream": ["@smithy/util-stream@4.5.6", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ=="],
"@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
"@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
"@smithy/util-waiter": ["@smithy/util-waiter@4.2.5", "", { "dependencies": { "@smithy/abort-controller": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Dbun99A3InifQdIrsXZ+QLcC0PGBPAdrl4cj1mTgJvyc9N2zf7QSxg8TBkzsCmGJdE3TLbO9ycwpY0EkWahQ/g=="],
"@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
"bowser": ["bowser@2.12.1", "", {}, "sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw=="],
"fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
"strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-crypto/sha1-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
"@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
"@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
"@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="],
"@aws-sdk/credential-provider-env/@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="],
"@aws-sdk/credential-provider-http/@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="],
"@aws-sdk/credential-provider-ini/@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="],
"@aws-sdk/credential-provider-node/@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="],
"@aws-sdk/credential-provider-process/@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="],
"@aws-sdk/credential-provider-sso/@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="],
"@aws-sdk/credential-provider-web-identity/@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="],
"@aws-sdk/token-providers/@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="],
"@smithy/credential-provider-imds/@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="],
"@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="],
"@smithy/property-provider/@smithy/types": ["@smithy/types@1.2.0", "", { "dependencies": { "tslib": "^2.5.0" } }, "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA=="],
"@smithy/util-defaults-mode-browser/@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="],
"@smithy/util-defaults-mode-node/@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="],
"@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
"@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
"@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
"@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
"@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
"@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
}
}

View File

@@ -1,82 +0,0 @@
/**
* Test whether the current user has access to the Bun build-cache.
*
* Exits with code 0 if access is available, or 1 otherwise.
*/
import { HeadBucketCommand, S3Client } from "@aws-sdk/client-s3";
import { CredentialsProviderError } from "@aws-sdk/property-provider";
class CacheConfig {
#bucket: string;
#region: string;
get bucket(): string {
return this.#bucket;
}
get region(): string {
return this.#region;
}
static fromArgv(): CacheConfig {
const cacheConfig = new CacheConfig();
const exitWithHelp = (reason: string) => {
console.error(`Error: ${reason}\n`);
console.error("Usage: have-access --bucket <bucket-name> --region <region>");
process.exit(1);
};
for (let i = 2; i < process.argv.length; i++) {
switch (process.argv[i]) {
case "-b":
case "--bucket":
cacheConfig.#bucket = process.argv[++i];
break;
case "-r":
case "--region":
cacheConfig.#region = process.argv[++i];
break;
default:
exitWithHelp(`Unknown argument: ${process.argv[i]}`);
}
}
if (!cacheConfig.#bucket) {
exitWithHelp("Missing required argument: --bucket");
}
if (!cacheConfig.#region) {
exitWithHelp("Missing required argument: --region");
}
return cacheConfig;
}
}
/**
* Test whether the current user has access to the Bun build-cache.
*/
async function currentUserHasAccess(cacheConfig: CacheConfig): Promise<boolean> {
const s3Client = new S3Client({ region: cacheConfig.region });
try {
await s3Client.send(new HeadBucketCommand({ Bucket: cacheConfig.bucket }));
return true;
} catch (error) {
if (
error.name === "NotFound" ||
error.$metadata?.httpStatusCode === 404 ||
error.name === "Forbidden" ||
error.$metadata?.httpStatusCode === 403 ||
error instanceof CredentialsProviderError
) {
return false;
}
throw error;
}
}
const ok = await currentUserHasAccess(CacheConfig.fromArgv());
process.exit(ok ? 0 : 1);

View File

@@ -1,6 +0,0 @@
{
"dependencies": {
"@aws-sdk/client-s3": "^3.928.0",
"@aws-sdk/property-provider": "^3.374.0"
}
}

View File

@@ -108,8 +108,8 @@ async function build(args) {
await startGroup("CMake Build", () => spawn("cmake", buildArgs, { env }));
if (ciCppBuild) {
await startGroup("sccache stats", () => {
spawn("sccache", ["--show-stats"], { env });
await startGroup("ccache stats", () => {
spawn("ccache", ["--show-stats"], { env });
});
}

View File

@@ -49,9 +49,42 @@ const colors = {
// Parse command line arguments
const args = process.argv.slice(2);
// Show help
if (args.includes("--help") || args.includes("-h")) {
console.log(`Usage: bun run scripts/buildkite-failures.ts [options] [build-id|branch|pr-url|buildkite-url]
Shows detailed error information from BuildKite build failures.
Full logs are saved to /tmp/bun-build-{number}-{platform}-{step}.log
Arguments:
build-id BuildKite build number (e.g., 35051)
branch Git branch name (e.g., main, claude/fix-bug)
pr-url GitHub PR URL (e.g., https://github.com/oven-sh/bun/pull/26173)
buildkite-url BuildKite build URL
#number GitHub PR number (e.g., #26173)
(none) Uses current git branch
Options:
--flaky, -f Include flaky test annotations
--warnings, -w Include warning annotations
--wait Poll continuously until build completes or fails
--help, -h Show this help message
Examples:
bun run scripts/buildkite-failures.ts # Current branch
bun run scripts/buildkite-failures.ts main # Main branch
bun run scripts/buildkite-failures.ts 35051 # Build #35051
bun run scripts/buildkite-failures.ts #26173 # PR #26173
bun run scripts/buildkite-failures.ts --wait # Wait for current branch build to complete
`);
process.exit(0);
}
const showWarnings = args.includes("--warnings") || args.includes("-w");
const showFlaky = args.includes("--flaky") || args.includes("-f");
const inputArg = args.find(arg => !arg.startsWith("-"));
const waitMode = args.includes("--wait");
const inputArg = args.find(arg => !arg.startsWith("-") && !arg.startsWith("--"));
// Determine what type of input we have
let buildNumber = null;
@@ -114,38 +147,138 @@ if (!buildNumber) {
buildNumber = match[1];
}
// Fetch build JSON
const buildResponse = await fetch(`https://buildkite.com/bun/bun/builds/${buildNumber}.json`);
const build = await buildResponse.json();
// Helper to format time ago
function formatTimeAgo(dateStr: string | null): string {
if (!dateStr) return "not started";
const date = new Date(dateStr);
const now = new Date();
const diffMs = now.getTime() - date.getTime();
const diffSecs = Math.floor(diffMs / 1000);
const diffMins = Math.floor(diffSecs / 60);
const diffHours = Math.floor(diffMins / 60);
const diffDays = Math.floor(diffHours / 24);
// Calculate time ago
const buildTime = new Date(build.started_at);
const now = new Date();
const diffMs = now.getTime() - buildTime.getTime();
const diffSecs = Math.floor(diffMs / 1000);
const diffMins = Math.floor(diffSecs / 60);
const diffHours = Math.floor(diffMins / 60);
const diffDays = Math.floor(diffHours / 24);
let timeAgo;
if (diffDays > 0) {
timeAgo = `${diffDays} day${diffDays !== 1 ? "s" : ""} ago`;
} else if (diffHours > 0) {
timeAgo = `${diffHours} hour${diffHours !== 1 ? "s" : ""} ago`;
} else if (diffMins > 0) {
timeAgo = `${diffMins} minute${diffMins !== 1 ? "s" : ""} ago`;
} else {
timeAgo = `${diffSecs} second${diffSecs !== 1 ? "s" : ""} ago`;
if (diffDays > 0) return `${diffDays} day${diffDays !== 1 ? "s" : ""} ago`;
if (diffHours > 0) return `${diffHours} hour${diffHours !== 1 ? "s" : ""} ago`;
if (diffMins > 0) return `${diffMins} minute${diffMins !== 1 ? "s" : ""} ago`;
return `${diffSecs} second${diffSecs !== 1 ? "s" : ""} ago`;
}
// Helper to clear line for updates
const clearLine = isTTY ? "\x1b[2K\r" : "";
// Poll for build status
let build: any;
let pollCount = 0;
const pollInterval = 10000; // 10 seconds
while (true) {
// Fetch build JSON
const buildResponse = await fetch(`https://buildkite.com/bun/bun/builds/${buildNumber}.json`);
build = await buildResponse.json();
// Check for failed jobs first (even if build is still running)
const failedJobsEarly =
build.jobs?.filter(
(job: any) => job.exit_status && job.exit_status > 0 && !job.soft_failed && job.type === "script",
) || [];
// In wait mode with failures, stop polling and show failures
if (waitMode && failedJobsEarly.length > 0) {
if (pollCount > 0) {
process.stdout.write(clearLine);
}
break;
}
// Calculate time ago (use created_at as fallback for scheduled/pending builds)
const timeAgo = formatTimeAgo(build.started_at || build.created_at);
// Check if build passed
if (build.state === "passed") {
if (pollCount > 0) {
process.stdout.write(clearLine);
}
console.log(`${timeAgo} - build #${buildNumber} https://buildkite.com/bun/bun/builds/${buildNumber}\n`);
console.log(`${colors.green}✅ Passed!${colors.reset}`);
process.exit(0);
}
// Check if build was canceled
if (build.state === "canceled" || build.state === "canceling") {
if (pollCount > 0) {
process.stdout.write(clearLine);
}
console.log(`${timeAgo} - build #${buildNumber} https://buildkite.com/bun/bun/builds/${buildNumber}\n`);
console.log(`${colors.dim}🚫 Build was canceled${colors.reset}`);
process.exit(0);
}
// Check if build is pending/running/scheduled
if (
build.state === "scheduled" ||
build.state === "running" ||
build.state === "creating" ||
build.state === "started"
) {
const runningJobs = build.jobs?.filter((job: any) => job.state === "running") || [];
const pendingJobs = build.jobs?.filter((job: any) => job.state === "scheduled" || job.state === "waiting") || [];
const passedJobs = build.jobs?.filter((job: any) => job.state === "passed") || [];
const totalJobs = build.jobs?.filter((job: any) => job.type === "script")?.length || 0;
if (waitMode) {
// In wait mode, show a single updating line
let statusMsg = "";
if (build.state === "scheduled" || build.state === "creating") {
statusMsg = `⏳ Waiting... (scheduled ${formatTimeAgo(build.created_at)})`;
} else {
statusMsg = `🔄 Running... ${passedJobs.length}/${totalJobs} passed, ${runningJobs.length} running`;
}
process.stdout.write(`${clearLine}${colors.dim}${statusMsg}${colors.reset}`);
pollCount++;
await Bun.sleep(pollInterval);
continue;
} else {
// Not in wait mode, show full status and exit
console.log(`${timeAgo} - build #${buildNumber} https://buildkite.com/bun/bun/builds/${buildNumber}\n`);
if (build.state === "scheduled" || build.state === "creating") {
console.log(`${colors.dim}⏳ Build is scheduled/pending${colors.reset}`);
if (build.created_at) {
console.log(`${colors.dim} Created: ${formatTimeAgo(build.created_at)}${colors.reset}`);
}
} else {
console.log(`${colors.dim}🔄 Build is running${colors.reset}`);
if (build.started_at) {
console.log(`${colors.dim} Started: ${formatTimeAgo(build.started_at)}${colors.reset}`);
}
console.log(
`${colors.dim} Progress: ${passedJobs.length}/${totalJobs} jobs passed, ${runningJobs.length} running, ${pendingJobs.length} pending${colors.reset}`,
);
if (runningJobs.length > 0) {
console.log(`\n${colors.dim}Running jobs:${colors.reset}`);
for (const job of runningJobs.slice(0, 5)) {
const name = job.name || job.label || "Unknown";
console.log(` ${colors.dim}${name}${colors.reset}`);
}
if (runningJobs.length > 5) {
console.log(` ${colors.dim}... and ${runningJobs.length - 5} more${colors.reset}`);
}
}
}
process.exit(0);
}
}
// Build is in a terminal state (failed, etc.) - break out of loop
break;
}
// Print header for failed build
const timeAgo = formatTimeAgo(build.started_at || build.created_at);
console.log(`${timeAgo} - build #${buildNumber} https://buildkite.com/bun/bun/builds/${buildNumber}\n`);
// Check if build passed
if (build.state === "passed") {
console.log(`${colors.green}✅ Passed!${colors.reset}`);
process.exit(0);
}
// Get failed jobs
const failedJobs =
build.jobs?.filter(job => job.exit_status && job.exit_status > 0 && !job.soft_failed && job.type === "script") || [];
@@ -734,7 +867,212 @@ if (registerRequestIndex !== -1) {
console.log(` https://buildkite.com/bun/bun/builds/${buildNumber}#annotations`);
}
} else {
console.log(`\n${colors.red}${colors.bold}${failedJobs.length} job failures${colors.reset}\n`);
console.log("View detailed results at:");
console.log(` https://buildkite.com/bun/bun/builds/${buildNumber}#annotations`);
// No annotations found - show detailed job failure information
if (failedJobs.length > 0) {
console.log(`\n${colors.red}${colors.bold}${failedJobs.length} job failures${colors.reset}\n`);
// Show annotation counts if available
const annotationCounts = build.annotation_counts_by_style;
if (annotationCounts) {
const errors = annotationCounts.error || 0;
const warnings = annotationCounts.warning || 0;
if (errors > 0 || warnings > 0) {
const parts = [];
if (errors > 0) parts.push(`${errors} error${errors !== 1 ? "s" : ""}`);
if (warnings > 0) parts.push(`${warnings} warning${warnings !== 1 ? "s" : ""}`);
console.log(
`${colors.dim}Annotations: ${parts.join(", ")} - view at https://buildkite.com/bun/bun/builds/${buildNumber}#annotations${colors.reset}\n`,
);
}
}
// Group jobs by type
const buildJobs = failedJobs.filter(job => (job.name || job.label || "").includes("build-"));
const testJobs = failedJobs.filter(job => (job.name || job.label || "").includes("test"));
const otherJobs = failedJobs.filter(
job => !(job.name || job.label || "").includes("build-") && !(job.name || job.label || "").includes("test"),
);
// Display build failures
if (buildJobs.length > 0) {
console.log(
`${colors.bgRed}${colors.white}${colors.bold} Build Failures (${buildJobs.length}) ${colors.reset}\n`,
);
for (const job of buildJobs) {
const name = (job.name || job.label || "Unknown").replace(/^:([^:]+):/, (_, emoji) => {
const platform = emoji.toLowerCase();
return platformMap[platform] || `:${emoji}:`;
});
const duration =
job.started_at && job.finished_at
? `${((new Date(job.finished_at).getTime() - new Date(job.started_at).getTime()) / 1000).toFixed(0)}s`
: "N/A";
console.log(` ${colors.red}${colors.reset} ${name}`);
console.log(` ${colors.dim}Duration: ${duration} | Exit: ${job.exit_status}${colors.reset}`);
console.log(` ${colors.dim}https://buildkite.com${job.path}${colors.reset}`);
console.log();
}
}
// Display test failures
if (testJobs.length > 0) {
console.log(`${colors.bgBlue}${colors.white}${colors.bold} Test Failures (${testJobs.length}) ${colors.reset}\n`);
for (const job of testJobs) {
const name = (job.name || job.label || "Unknown").replace(/^:([^:]+):/, (_, emoji) => {
const platform = emoji.toLowerCase();
return platformMap[platform] || `:${emoji}:`;
});
const duration =
job.started_at && job.finished_at
? `${((new Date(job.finished_at).getTime() - new Date(job.started_at).getTime()) / 1000).toFixed(0)}s`
: "N/A";
console.log(` ${colors.red}${colors.reset} ${name}`);
console.log(` ${colors.dim}Duration: ${duration} | Exit: ${job.exit_status}${colors.reset}`);
console.log(` ${colors.dim}https://buildkite.com${job.path}${colors.reset}`);
console.log();
}
}
// Display other failures
if (otherJobs.length > 0) {
console.log(
`${colors.bgBlue}${colors.white}${colors.bold} Other Failures (${otherJobs.length}) ${colors.reset}\n`,
);
for (const job of otherJobs) {
const name = (job.name || job.label || "Unknown").replace(/^:([^:]+):/, (_, emoji) => {
const platform = emoji.toLowerCase();
return platformMap[platform] || `:${emoji}:`;
});
const duration =
job.started_at && job.finished_at
? `${((new Date(job.finished_at).getTime() - new Date(job.started_at).getTime()) / 1000).toFixed(0)}s`
: "N/A";
console.log(` ${colors.red}${colors.reset} ${name}`);
console.log(` ${colors.dim}Duration: ${duration} | Exit: ${job.exit_status}${colors.reset}`);
console.log(` ${colors.dim}https://buildkite.com${job.path}${colors.reset}`);
console.log();
}
}
// Fetch and display logs for all failed jobs
// Use the public BuildKite log endpoint
console.log(`${colors.dim}Fetching logs for ${failedJobs.length} failed jobs...${colors.reset}\n`);
for (const job of failedJobs) {
const name = (job.name || job.label || "Unknown").replace(/^:([^:]+):/, (_, emoji) => {
const platform = emoji.toLowerCase();
return platformMap[platform] || `:${emoji}:`;
});
// Create a sanitized filename from the job name
// e.g., ":darwin: aarch64 - build-cpp" -> "darwin-aarch64-build-cpp"
const sanitizedName = (job.name || job.label || "unknown")
.replace(/^:([^:]+):\s*/, "$1-") // :darwin: -> darwin-
.replace(/\s+-\s+/g, "-") // " - " -> "-"
.replace(/[^a-zA-Z0-9-]/g, "-") // Replace other chars with -
.replace(/-+/g, "-") // Collapse multiple -
.replace(/^-|-$/g, "") // Remove leading/trailing -
.toLowerCase();
const logFilePath = `/tmp/bun-build-${buildNumber}-${sanitizedName}.log`;
try {
const logResponse = await fetch(
`https://buildkite.com/organizations/bun/pipelines/bun/builds/${buildNumber}/jobs/${job.id}/log`,
);
if (logResponse.ok) {
const logData = await logResponse.json();
let output = logData.output || "";
// Convert HTML to readable text (without ANSI codes for file output)
const plainOutput = output
// Remove timestamp tags
.replace(/<time[^>]*>[^<]*<\/time>/g, "")
// Remove all span tags
.replace(/<span[^>]*>([^<]*)<\/span>/g, "$1")
// Remove remaining HTML tags
.replace(/<[^>]+>/g, "")
// Decode HTML entities
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.replace(/&#47;/g, "/")
.replace(/&nbsp;/g, " ");
// Write the full log to a file
await Bun.write(logFilePath, plainOutput);
// Extract unique error messages for display
const lines = plainOutput.split("\n");
const uniqueErrors = new Set<string>();
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// Look for actual error messages
const isError =
(line.includes("error:") && !line.includes('error: script "') && !line.includes("error: exit")) ||
line.includes("fatal error:") ||
line.includes("panic:") ||
line.includes("undefined reference");
if (isError) {
// Extract just the error message part (remove path prefixes and timestamps)
const errorMsg = line
.replace(/^.*?\d{4}-\d{2}-\d{2}T[\d:.]+Z/, "") // Remove timestamps
.replace(/^.*?\/[^\s]*:\d+:\d+:\s*/, "") // Remove file paths
.trim();
if (errorMsg && !uniqueErrors.has(errorMsg)) {
uniqueErrors.add(errorMsg);
}
}
}
// Display job info with log file path
console.log(`${colors.bgBlue}${colors.white}${colors.bold} ${name} ${colors.reset}`);
console.log(` ${colors.dim}Log: ${logFilePath}${colors.reset}`);
if (uniqueErrors.size > 0) {
console.log(` ${colors.red}Errors (${uniqueErrors.size}):${colors.reset}`);
let count = 0;
for (const err of uniqueErrors) {
if (count >= 5) {
console.log(` ${colors.dim}... and ${uniqueErrors.size - 5} more${colors.reset}`);
break;
}
console.log(` ${colors.red}${colors.reset} ${err.slice(0, 120)}${err.length > 120 ? "..." : ""}`);
count++;
}
} else {
// Show last few lines as a preview
const lastLines = lines.slice(-5).filter(l => l.trim());
if (lastLines.length > 0) {
console.log(` ${colors.dim}Last output:${colors.reset}`);
for (const line of lastLines) {
console.log(` ${colors.dim}${line.slice(0, 100)}${line.length > 100 ? "..." : ""}${colors.reset}`);
}
}
}
if (logData.truncated) {
console.log(` ${colors.dim}(Log was truncated by BuildKite)${colors.reset}`);
}
} else {
console.log(`${colors.bgBlue}${colors.white}${colors.bold} ${name} ${colors.reset}`);
console.log(` ${colors.dim}Failed to fetch log: ${logResponse.status}${colors.reset}`);
}
} catch (e) {
console.log(`${colors.bgBlue}${colors.white}${colors.bold} ${name} ${colors.reset}`);
console.log(` ${colors.dim}Error fetching log: ${e.message}${colors.reset}`);
}
console.log();
}
} else {
console.log("View detailed results at:");
console.log(` https://buildkite.com/bun/bun/builds/${buildNumber}#annotations`);
}
}

View File

@@ -28,6 +28,7 @@ console.log("Reading linker files...");
const linkerLds = await Bun.file(join(repoRoot, "src", "linker.lds")).text();
const symbolsDyn = await Bun.file(join(repoRoot, "src", "symbols.dyn")).text();
const symbolsTxt = await Bun.file(join(repoRoot, "src", "symbols.txt")).text();
const symbolsDef = await Bun.file(join(repoRoot, "src", "symbols.def")).text();
// Create metadata JSON with link command included
const metadata = {
@@ -41,6 +42,7 @@ const metadata = {
linker_lds: linkerLds,
symbols_dyn: symbolsDyn,
symbols_txt: symbolsTxt,
symbols_def: symbolsDef,
};
const metadataPath = join(buildPath, "link-metadata.json");

View File

@@ -387,9 +387,6 @@ const aws = {
owner = "amazon";
name = `Windows_Server-${release || "*"}-English-Full-Base-*`;
}
} else if (os === "freebsd") {
owner = "782442783595"; // upstream member of FreeBSD team, likely Colin Percival
name = `FreeBSD ${release}-STABLE-${{ "aarch64": "arm64", "x64": "amd64" }[arch] ?? "amd64"}-* UEFI-PREFERRED cloud-init UFS`;
}
if (!name) {
@@ -676,10 +673,6 @@ function getCloudInit(cloudInit) {
case "windows":
// handled above
break;
case "freebsd":
sftpPath = "/usr/libexec/openssh/sftp-server";
shell = "/bin/csh";
break;
default:
throw new Error(`Unsupported os: ${cloudInit["os"]}`);
}
@@ -1071,7 +1064,7 @@ function getCloud(name) {
}
/**
* @typedef {"linux" | "darwin" | "windows" | "freebsd"} Os
* @typedef {"linux" | "darwin" | "windows"} Os
* @typedef {"aarch64" | "x64"} Arch
* @typedef {"macos" | "windowsserver" | "debian" | "ubuntu" | "alpine" | "amazonlinux"} Distro
*/

View File

@@ -1128,6 +1128,7 @@ async function spawnBun(execPath, { args, cwd, timeout, env, stdout, stderr }) {
...process.env,
PATH: path,
TMPDIR: tmpdirPath,
BUN_TMPDIR: tmpdirPath,
USER: username,
HOME: homedir,
SHELL: shellPath,
@@ -1584,6 +1585,9 @@ function isNodeTest(path) {
if (isCI && isMacOS && isX64) {
return false;
}
if (!isJavaScript(path)) {
return false;
}
const unixPath = path.replaceAll(sep, "/");
return (
unixPath.includes("js/node/test/parallel/") ||

View File

@@ -1538,7 +1538,7 @@ export function parseNumber(value) {
/**
* @param {string} string
* @returns {"darwin" | "linux" | "windows" | "freebsd"}
* @returns {"darwin" | "linux" | "windows"}
*/
export function parseOs(string) {
if (/darwin|apple|mac/i.test(string)) {
@@ -1550,9 +1550,6 @@ export function parseOs(string) {
if (/win/i.test(string)) {
return "windows";
}
if (/freebsd/i.test(string)) {
return "freebsd";
}
throw new Error(`Unsupported operating system: ${string}`);
}
@@ -1915,10 +1912,6 @@ export function getUsernameForDistro(distro) {
if (/amazon|amzn|al\d+|rhel/i.test(distro)) {
return "ec2-user";
}
if (/freebsd/i.test(distro)) {
return "root";
}
throw new Error(`Unsupported distro: ${distro}`);
}
@@ -2986,7 +2979,6 @@ const emojiMap = {
gear: ["⚙️", "gear"],
clipboard: ["📋", "clipboard"],
rocket: ["🚀", "rocket"],
freebsd: ["😈", "freebsd"],
openbsd: ["🐡", "openbsd"],
netbsd: ["🚩", "netbsd"],
};

View File

@@ -17,7 +17,7 @@ pkgs.mkShell rec {
cargo
go
python3
sccache
ccache
pkg-config
gnumake
libtool

View File

@@ -8,4 +8,5 @@ Syntax reminders:
Conventions:
- Prefer `@import` at the **bottom** of the file, but the auto formatter will move them so you don't need to worry about it.
- **Never** use `@import()` inline inside of functions. **Always** put them at the bottom of the file or containing struct. Imports in Zig are free of side-effects, so there's no such thing as a "dynamic" import.
- You must be patient with the build.

View File

@@ -74,7 +74,7 @@ pub const FileOperation = struct {
pub fn getPathname(file: *const FileOperation) string {
if (file.is_tmpdir) {
return resolve_path.joinAbs(@TypeOf(Fs.FileSystem.instance.fs).tmpdir_path, .auto, file.pathname);
return resolve_path.joinAbs(Fs.FileSystem.RealFS.tmpdirPath(), .auto, file.pathname);
} else {
return file.pathname;
}

View File

@@ -670,7 +670,7 @@ pub const StandaloneModuleGraph = struct {
if (!tried_changing_abs_dir) {
tried_changing_abs_dir = true;
const zname_z = bun.strings.concat(bun.default_allocator, &.{
bun.fs.FileSystem.instance.fs.tmpdirPath(),
bun.fs.FileSystem.RealFS.tmpdirPath(),
std.fs.path.sep_str,
zname,
&.{0},
@@ -1159,7 +1159,9 @@ pub const StandaloneModuleGraph = struct {
return .success;
}
pub fn fromExecutable(allocator: std.mem.Allocator) !?StandaloneModuleGraph {
/// Loads the standalone module graph from the executable, allocates it on the heap,
/// sets it globally, and returns the pointer.
pub fn fromExecutable(allocator: std.mem.Allocator) !?*StandaloneModuleGraph {
if (comptime Environment.isMac) {
const macho_bytes = Macho.getData() orelse return null;
if (macho_bytes.len < @sizeOf(Offsets) + trailer.len) {
@@ -1173,7 +1175,7 @@ pub const StandaloneModuleGraph = struct {
return null;
}
const offsets = std.mem.bytesAsValue(Offsets, macho_bytes_slice).*;
return try StandaloneModuleGraph.fromBytes(allocator, @constCast(macho_bytes), offsets);
return try fromBytesAlloc(allocator, @constCast(macho_bytes), offsets);
}
if (comptime Environment.isWindows) {
@@ -1189,7 +1191,7 @@ pub const StandaloneModuleGraph = struct {
return null;
}
const offsets = std.mem.bytesAsValue(Offsets, pe_bytes_slice).*;
return try StandaloneModuleGraph.fromBytes(allocator, @constCast(pe_bytes), offsets);
return try fromBytesAlloc(allocator, @constCast(pe_bytes), offsets);
}
// Do not invoke libuv here.
@@ -1213,7 +1215,7 @@ pub const StandaloneModuleGraph = struct {
}
}
if (read_amount < trailer.len + @sizeOf(usize) + 32)
if (read_amount < trailer.len + @sizeOf(usize) + @sizeOf(Offsets))
// definitely missing data
return null;
@@ -1284,7 +1286,15 @@ pub const StandaloneModuleGraph = struct {
}
}
return try StandaloneModuleGraph.fromBytes(allocator, to_read, offsets);
return try fromBytesAlloc(allocator, to_read, offsets);
}
/// Allocates a StandaloneModuleGraph on the heap, populates it from bytes, sets it globally, and returns the pointer.
fn fromBytesAlloc(allocator: std.mem.Allocator, raw_bytes: []u8, offsets: Offsets) !*StandaloneModuleGraph {
const graph_ptr = try allocator.create(StandaloneModuleGraph);
graph_ptr.* = try StandaloneModuleGraph.fromBytes(allocator, raw_bytes, offsets);
graph_ptr.set();
return graph_ptr;
}
/// heuristic: `bun build --compile` won't be supported if the name is "bun", "bunx", or "node".

View File

@@ -166,12 +166,7 @@ pub const WatchEvent = struct {
pub fn merge(this: *WatchEvent, other: WatchEvent) void {
this.name_len += other.name_len;
this.op = Op{
.delete = this.op.delete or other.op.delete,
.metadata = this.op.metadata or other.op.metadata,
.rename = this.op.rename or other.op.rename,
.write = this.op.write or other.op.write,
};
this.op = Op.merge(this.op, other.op);
}
pub const Op = packed struct(u8) {
@@ -180,7 +175,8 @@ pub const WatchEvent = struct {
rename: bool = false,
write: bool = false,
move_to: bool = false,
_padding: u3 = 0,
create: bool = false,
_padding: u2 = 0,
pub fn merge(before: Op, after: Op) Op {
return .{
@@ -189,6 +185,7 @@ pub const WatchEvent = struct {
.metadata = before.metadata or after.metadata,
.rename = before.rename or after.rename,
.move_to = before.move_to or after.move_to,
.create = before.create or after.create,
};
}

View File

@@ -132,7 +132,7 @@ pub fn create(bytes: []const u8) bun.sys.Maybe(bun.webcore.Blob.Store.Bytes) {
const label = std.fmt.bufPrintZ(&label_buf, "memfd-num-{d}", .{memfd_counter.fetchAdd(1, .monotonic)}) catch "";
// Using huge pages was slower.
const fd = switch (bun.sys.memfd_create(label, std.os.linux.MFD.CLOEXEC)) {
const fd = switch (bun.sys.memfd_create(label, .non_executable)) {
.err => |err| return .{ .err = bun.sys.Error.fromCode(err.getErrno(), .open) },
.result => |fd| fd,
};

View File

@@ -296,7 +296,7 @@ fn deduplicatedImport(
// Disable this one since an older record is getting used. It isn't
// practical to delete this import record entry since an import or
// require expression can exist.
ir.is_unused = true;
ir.flags.is_unused = true;
const stmt = ctx.stmts.items[gop.value_ptr.stmt_index].data.s_import;
if (items.len > 0) {

View File

@@ -617,7 +617,7 @@ pub fn joinAllWithCommaCallback(all: []Expr, comptime Context: type, ctx: Contex
return callback(ctx, all[0]);
},
2 => {
return Expr.joinWithComma(
const result = Expr.joinWithComma(
callback(ctx, all[0]) orelse Expr{
.data = .{ .e_missing = .{} },
.loc = all[0].loc,
@@ -628,6 +628,10 @@ pub fn joinAllWithCommaCallback(all: []Expr, comptime Context: type, ctx: Contex
},
allocator,
);
if (result.isMissing()) {
return null;
}
return result;
},
else => {
var i: usize = 1;
@@ -643,6 +647,9 @@ pub fn joinAllWithCommaCallback(all: []Expr, comptime Context: type, ctx: Contex
}, allocator);
}
if (expr.isMissing()) {
return null;
}
return expr;
},
}

View File

@@ -25,7 +25,7 @@ pub fn scan(
const record: *ImportRecord = &p.import_records.items[st.import_record_index];
if (record.path.isMacro()) {
record.is_unused = true;
record.flags.is_unused = true;
record.path.is_disabled = true;
continue;
}
@@ -190,8 +190,8 @@ pub fn scan(
{
// internal imports are presumed to be always used
// require statements cannot be stripped
if (!record.is_internal and !record.was_originally_require) {
record.is_unused = true;
if (!record.flags.is_internal and !record.flags.was_originally_require) {
record.flags.is_unused = true;
continue;
}
}
@@ -204,7 +204,7 @@ pub fn scan(
st.star_name_loc = null;
}
record.contains_default_alias = record.contains_default_alias or st.default_name != null;
record.flags.contains_default_alias = record.flags.contains_default_alias or st.default_name != null;
const existing_items: ImportItemForNamespaceMap = p.import_items_for_namespace.get(namespace_ref) orelse
ImportItemForNamespaceMap.init(allocator);
@@ -265,7 +265,7 @@ pub fn scan(
) catch |err| bun.handleOom(err);
if (st.star_name_loc) |loc| {
record.contains_import_star = true;
record.flags.contains_import_star = true;
p.named_imports.putAssumeCapacity(
namespace_ref,
js_ast.NamedImport{
@@ -279,7 +279,7 @@ pub fn scan(
}
if (st.default_name) |default| {
record.contains_default_alias = true;
record.flags.contains_default_alias = true;
p.named_imports.putAssumeCapacity(
default.ref.?,
.{
@@ -313,7 +313,7 @@ pub fn scan(
// We do not know at this stage whether or not the import statement is bundled
// This keeps track of the `namespace_alias` incase, at printing time, we determine that we should print it with the namespace
for (st.items) |item| {
record.contains_default_alias = record.contains_default_alias or strings.eqlComptime(item.alias, "default");
record.flags.contains_default_alias = record.flags.contains_default_alias or strings.eqlComptime(item.alias, "default");
const name: LocRef = item.name;
const name_ref = name.ref.?;
@@ -327,7 +327,7 @@ pub fn scan(
// Make sure the printer prints this as a property access
var symbol: *Symbol = &p.symbols.items[name_ref.innerIndex()];
if (record.contains_import_star or st.star_name_loc != null)
if (record.flags.contains_import_star or st.star_name_loc != null)
symbol.namespace_alias = G.NamespaceAlias{
.namespace_ref = namespace_ref,
.alias = item.alias,
@@ -336,7 +336,7 @@ pub fn scan(
};
}
if (record.was_originally_require) {
if (record.flags.was_originally_require) {
var symbol = &p.symbols.items[namespace_ref.innerIndex()];
symbol.namespace_alias = G.NamespaceAlias{
.namespace_ref = namespace_ref,
@@ -349,12 +349,12 @@ pub fn scan(
try p.import_records_for_current_part.append(allocator, st.import_record_index);
record.contains_import_star = record.contains_import_star or st.star_name_loc != null;
record.contains_default_alias = record.contains_default_alias or st.default_name != null;
record.flags.contains_import_star = record.flags.contains_import_star or st.star_name_loc != null;
record.flags.contains_default_alias = record.flags.contains_default_alias or st.default_name != null;
for (st.items) |*item| {
record.contains_default_alias = record.contains_default_alias or strings.eqlComptime(item.alias, "default");
record.contains_es_module_alias = record.contains_es_module_alias or strings.eqlComptime(item.alias, "__esModule");
record.flags.contains_default_alias = record.flags.contains_default_alias or strings.eqlComptime(item.alias, "default");
record.flags.contains_es_module_alias = record.flags.contains_es_module_alias or strings.eqlComptime(item.alias, "__esModule");
}
},
@@ -456,7 +456,7 @@ pub fn scan(
});
try p.recordExport(alias.loc, alias.original_name, st.namespace_ref);
var record = &p.import_records.items[st.import_record_index];
record.contains_import_star = true;
record.flags.contains_import_star = true;
} else {
// "export * from 'path'"
try p.export_star_import_records.append(allocator, st.import_record_index);
@@ -482,9 +482,9 @@ pub fn scan(
var record = &p.import_records.items[st.import_record_index];
if (strings.eqlComptime(item.original_name, "default")) {
record.contains_default_alias = true;
record.flags.contains_default_alias = true;
} else if (strings.eqlComptime(item.original_name, "__esModule")) {
record.contains_es_module_alias = true;
record.flags.contains_es_module_alias = true;
}
}
},

View File

@@ -480,7 +480,7 @@ pub const Runner = struct {
this.macro.vm.waitForPromise(promise);
const promise_result = promise.result(this.macro.vm.jsc_vm);
const rejected = promise.status(this.macro.vm.jsc_vm) == .rejected;
const rejected = promise.status() == .rejected;
if (promise_result.isUndefined() and this.is_top_level) {
this.is_top_level = false;

View File

@@ -518,7 +518,7 @@ pub fn NewParser_(
p.import_records.items[import_record_index].tag = tag;
}
p.import_records.items[import_record_index].handles_import_errors = (state.is_await_target and p.fn_or_arrow_data_visit.try_body_count != 0) or state.is_then_catch_target;
p.import_records.items[import_record_index].flags.handles_import_errors = (state.is_await_target and p.fn_or_arrow_data_visit.try_body_count != 0) or state.is_then_catch_target;
p.import_records_for_current_part.append(p.allocator, import_record_index) catch unreachable;
return p.newExpr(E.Import{
@@ -573,7 +573,7 @@ pub fn NewParser_(
}
const import_record_index = p.addImportRecord(.require_resolve, arg.loc, arg.data.e_string.string(p.allocator) catch unreachable);
p.import_records.items[import_record_index].handles_import_errors = p.fn_or_arrow_data_visit.try_body_count != 0;
p.import_records.items[import_record_index].flags.handles_import_errors = p.fn_or_arrow_data_visit.try_body_count != 0;
p.import_records_for_current_part.append(p.allocator, import_record_index) catch unreachable;
return p.newExpr(
@@ -625,7 +625,7 @@ pub fn NewParser_(
if (should_unwrap_require) {
const import_record_index = p.addImportRecordByRangeAndPath(.stmt, p.source.rangeOfString(arg.loc), path);
p.import_records.items[import_record_index].handles_import_errors = handles_import_errors;
p.import_records.items[import_record_index].flags.handles_import_errors = handles_import_errors;
// Note that this symbol may be completely removed later.
var path_name = fs.PathName.init(path.text);
@@ -658,7 +658,7 @@ pub fn NewParser_(
}
const import_record_index = p.addImportRecordByRangeAndPath(.require, p.source.rangeOfString(arg.loc), path);
p.import_records.items[import_record_index].handles_import_errors = handles_import_errors;
p.import_records.items[import_record_index].flags.handles_import_errors = handles_import_errors;
p.import_records_for_current_part.append(p.allocator, import_record_index) catch unreachable;
return p.newExpr(E.RequireString{ .import_record_index = import_record_index }, arg.loc);
@@ -1332,7 +1332,7 @@ pub fn NewParser_(
var import_record: *ImportRecord = &p.import_records.items[import_record_i];
if (comptime is_internal)
import_record.path.namespace = "runtime";
import_record.is_internal = is_internal;
import_record.flags.is_internal = is_internal;
const import_path_identifier = try import_record.path.name.nonUniqueNameString(allocator);
var namespace_identifier = try allocator.alloc(u8, import_path_identifier.len + prefix.len);
const clause_items = try allocator.alloc(js_ast.ClauseItem, imports.len);
@@ -2628,7 +2628,7 @@ pub fn NewParser_(
if (is_macro) {
const id = p.addImportRecord(.stmt, path.loc, path.text);
p.import_records.items[id].path.namespace = js_ast.Macro.namespace;
p.import_records.items[id].is_unused = true;
p.import_records.items[id].flags.is_unused = true;
if (stmt.default_name) |name_loc| {
const name = p.loadNameFromRef(name_loc.ref.?);
@@ -2694,7 +2694,7 @@ pub fn NewParser_(
null;
stmt.import_record_index = p.addImportRecord(.stmt, path.loc, path.text);
p.import_records.items[stmt.import_record_index].was_originally_bare_import = was_originally_bare_import;
p.import_records.items[stmt.import_record_index].flags.was_originally_bare_import = was_originally_bare_import;
if (stmt.star_name_loc) |star| {
const name = p.loadNameFromRef(stmt.namespace_ref);
@@ -2755,9 +2755,9 @@ pub fn NewParser_(
});
p.import_records.items[new_import_id].path.namespace = js_ast.Macro.namespace;
p.import_records.items[new_import_id].is_unused = true;
p.import_records.items[new_import_id].flags.is_unused = true;
if (comptime only_scan_imports_and_do_not_visit) {
p.import_records.items[new_import_id].is_internal = true;
p.import_records.items[new_import_id].flags.is_internal = true;
p.import_records.items[new_import_id].path.is_disabled = true;
}
stmt.default_name = null;
@@ -2816,9 +2816,9 @@ pub fn NewParser_(
});
p.import_records.items[new_import_id].path.namespace = js_ast.Macro.namespace;
p.import_records.items[new_import_id].is_unused = true;
p.import_records.items[new_import_id].flags.is_unused = true;
if (comptime only_scan_imports_and_do_not_visit) {
p.import_records.items[new_import_id].is_internal = true;
p.import_records.items[new_import_id].flags.is_internal = true;
p.import_records.items[new_import_id].path.is_disabled = true;
}
remap_count += 1;
@@ -2844,11 +2844,11 @@ pub fn NewParser_(
if (remap_count > 0 and stmt.items.len == 0 and stmt.default_name == null) {
p.import_records.items[stmt.import_record_index].path.namespace = js_ast.Macro.namespace;
p.import_records.items[stmt.import_record_index].is_unused = true;
p.import_records.items[stmt.import_record_index].flags.is_unused = true;
if (comptime only_scan_imports_and_do_not_visit) {
p.import_records.items[stmt.import_record_index].path.is_disabled = true;
p.import_records.items[stmt.import_record_index].is_internal = true;
p.import_records.items[stmt.import_record_index].flags.is_internal = true;
}
return p.s(S.Empty{}, loc);

View File

@@ -134,17 +134,17 @@ pub const Parser = struct {
// - import 'foo';
// - import("foo")
// - require("foo")
import_record.is_unused = import_record.is_unused or
import_record.flags.is_unused = import_record.flags.is_unused or
(import_record.kind == .stmt and
!import_record.was_originally_bare_import and
!import_record.calls_runtime_re_export_fn);
!import_record.flags.was_originally_bare_import and
!import_record.flags.calls_runtime_re_export_fn);
}
var iter = scan_pass.used_symbols.iterator();
while (iter.next()) |entry| {
const val = entry.value_ptr;
if (val.used) {
scan_pass.import_records.items[val.import_record_index].is_unused = false;
scan_pass.import_records.items[val.import_record_index].flags.is_unused = false;
}
}
}
@@ -783,9 +783,39 @@ pub const Parser = struct {
// else
// module.exports = require('./foo.dev.js')
//
if (parts.items.len == 1 and parts.items[0].stmts.len == 1) {
var part = &parts.items[0];
const stmt: Stmt = part.stmts[0];
// Find the part containing the actual module.exports = require() statement,
// skipping over parts that only contain comments, directives, and empty statements.
// This handles files like:
//
// /*!
// * express
// * MIT Licensed
// */
// 'use strict';
// module.exports = require('./lib/express');
//
// When tree-shaking is enabled, each statement becomes its own part, so we need
// to look across all parts to find the single meaningful statement.
const StmtAndPart = struct { stmt: Stmt, part_idx: usize };
const stmt_and_part: ?StmtAndPart = brk: {
var found: ?StmtAndPart = null;
for (parts.items, 0..) |part, part_idx| {
for (part.stmts) |s| {
switch (s.data) {
.s_comment, .s_directive, .s_empty => continue,
else => {
// If we already found a non-trivial statement, there's more than one
if (found != null) break :brk null;
found = .{ .stmt = s, .part_idx = part_idx };
},
}
}
}
break :brk found;
};
if (stmt_and_part) |found| {
const stmt = found.stmt;
var part = &parts.items[found.part_idx];
if (p.symbols.items[p.module_ref.innerIndex()].use_count_estimate == 1) {
if (stmt.data == .s_expr) {
const value: Expr = stmt.data.s_expr.value;
@@ -800,13 +830,13 @@ pub const Parser = struct {
left.data.e_dot.target.data == .e_identifier and
left.data.e_dot.target.data.e_identifier.ref.eql(p.module_ref))
{
const redirect_import_record_index: ?u32 = brk: {
const redirect_import_record_index: ?u32 = inner_brk: {
// general case:
//
// module.exports = require("foo");
//
if (right.data == .e_require_string) {
break :brk right.data.e_require_string.import_record_index;
break :inner_brk right.data.e_require_string.import_record_index;
}
// special case: a module for us to unwrap
@@ -825,10 +855,10 @@ pub const Parser = struct {
{
// We know it's 0 because there is only one import in the whole file
// so that one import must be the one we're looking for
break :brk 0;
break :inner_brk 0;
}
break :brk null;
break :inner_brk null;
};
if (redirect_import_record_index) |id| {
part.symbol_uses = .{};
@@ -1023,7 +1053,7 @@ pub const Parser = struct {
const import_record: ?*const ImportRecord = brk: {
for (p.import_records.items) |*import_record| {
if (import_record.is_internal or import_record.is_unused) continue;
if (import_record.flags.is_internal or import_record.flags.is_unused) continue;
if (import_record.kind == .stmt) break :brk import_record;
}
@@ -1094,7 +1124,7 @@ pub const Parser = struct {
// If they use an import statement, we say it's ESM because that's not allowed in CommonJS files.
const uses_any_import_statements = brk: {
for (p.import_records.items) |*import_record| {
if (import_record.is_internal or import_record.is_unused) continue;
if (import_record.flags.is_internal or import_record.flags.is_unused) continue;
if (import_record.kind == .stmt) break :brk true;
}
@@ -1494,7 +1524,7 @@ pub const Parser = struct {
var state: PragmaState = .{};
while (cursor < self.lexer.end) : (cursor += 1) {
while (cursor < end) : (cursor += 1) {
switch (contents[cursor]) {
'\n' => break,
'@' => {

View File

@@ -315,6 +315,10 @@ pub const SideEffects = enum(u1) {
);
}
if (result.isMissing()) {
return null;
}
return result;
},
.e_array => {

View File

@@ -327,7 +327,7 @@ pub fn ParseStmt(
if (comptime track_symbol_usage_during_parse_pass) {
// In the scan pass, we need _some_ way of knowing *not* to mark as unused
p.import_records.items[import_record_index].calls_runtime_re_export_fn = true;
p.import_records.items[import_record_index].flags.calls_runtime_re_export_fn = true;
}
try p.lexer.expectOrInsertSemicolon();
@@ -381,7 +381,7 @@ pub fn ParseStmt(
if (comptime track_symbol_usage_during_parse_pass) {
// In the scan pass, we need _some_ way of knowing *not* to mark as unused
p.import_records.items[import_record_index].calls_runtime_re_export_fn = true;
p.import_records.items[import_record_index].flags.calls_runtime_re_export_fn = true;
}
p.current_scope.is_after_const_local_prefix = true;
p.has_es_module_syntax = true;

View File

@@ -36,7 +36,7 @@ bakeModuleLoaderImportModule(JSC::JSGlobalObject* global,
if (!keyString) {
auto promise = JSC::JSInternalPromise::create(vm, global->internalPromiseStructure());
promise->reject(global, JSC::createError(global, "import() requires a string"_s));
promise->reject(vm, global, JSC::createError(global, "import() requires a string"_s));
return promise;
}
@@ -94,8 +94,7 @@ static JSC::JSInternalPromise* rejectedInternalPromise(JSC::JSGlobalObject* glob
{
auto& vm = JSC::getVM(globalObject);
JSC::JSInternalPromise* promise = JSC::JSInternalPromise::create(vm, globalObject->internalPromiseStructure());
promise->internalField(JSC::JSPromise::Field::ReactionsOrResult).set(vm, promise, value);
promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, JSC::jsNumber(promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32AsAnyInt() | JSC::JSPromise::isFirstResolvingFunctionCalledFlag | static_cast<unsigned>(JSC::JSPromise::Status::Rejected)));
promise->rejectAsHandled(vm, globalObject, value);
return promise;
}
@@ -103,8 +102,7 @@ static JSC::JSInternalPromise* resolvedInternalPromise(JSC::JSGlobalObject* glob
{
auto& vm = JSC::getVM(globalObject);
JSC::JSInternalPromise* promise = JSC::JSInternalPromise::create(vm, globalObject->internalPromiseStructure());
promise->internalField(JSC::JSPromise::Field::ReactionsOrResult).set(vm, promise, value);
promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, JSC::jsNumber(promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32AsAnyInt() | JSC::JSPromise::isFirstResolvingFunctionCalledFlag | static_cast<unsigned>(JSC::JSPromise::Status::Fulfilled)));
promise->fulfill(vm, globalObject, value);
return promise;
}
@@ -141,10 +139,10 @@ JSC::JSInternalPromise* bakeModuleLoaderFetch(JSC::JSGlobalObject* globalObject,
globalObject,
source.toWTFString(),
origin,
WTFMove(moduleKey),
WTF::move(moduleKey),
WTF::TextPosition(),
JSC::SourceProviderSourceType::Module));
return resolvedInternalPromise(globalObject, JSC::JSSourceCode::create(vm, WTFMove(sourceCode)));
return resolvedInternalPromise(globalObject, JSC::JSSourceCode::create(vm, WTF::move(sourceCode)));
}
// We unconditionally prefix the key with "bake:" inside

View File

@@ -33,7 +33,7 @@ extern "C" JSC::EncodedJSValue BakeLoadInitialServerCode(JSC::JSGlobalObject* gl
global,
source.toWTFString(),
origin,
WTFMove(string),
WTF::move(string),
WTF::TextPosition(),
JSC::SourceProviderSourceType::Program
));
@@ -67,7 +67,7 @@ extern "C" JSC::EncodedJSValue BakeLoadServerHmrPatch(GlobalObject* global, BunS
global,
source.toWTFString(),
origin,
WTFMove(string),
WTF::move(string),
WTF::TextPosition(),
JSC::SourceProviderSourceType::Program
));
@@ -93,7 +93,7 @@ extern "C" JSC::EncodedJSValue BakeLoadServerHmrPatchWithSourceMap(GlobalObject*
sourceMapJSONPtr,
sourceMapJSONLength,
origin,
WTFMove(string),
WTF::move(string),
WTF::TextPosition(),
JSC::SourceProviderSourceType::Program
);
@@ -159,7 +159,7 @@ extern "C" JSC::EncodedJSValue BakeRegisterProductionChunk(JSC::JSGlobalObject*
global,
source.toWTFString(),
origin,
WTFMove(string),
WTF::move(string),
WTF::TextPosition(),
JSC::SourceProviderSourceType::Module
));

View File

@@ -20,7 +20,7 @@ public:
const TextPosition& startPosition,
JSC::SourceProviderSourceType sourceType)
{
auto provider = adoptRef(*new SourceProvider(source, sourceOrigin, WTFMove(sourceURL), startPosition, sourceType));
auto provider = adoptRef(*new SourceProvider(source, sourceOrigin, WTF::move(sourceURL), startPosition, sourceType));
auto* zigGlobalObject = jsCast<Zig::GlobalObject*>(globalObject);
auto specifier = Bun::toString(provider->sourceURL());
Bun__addBakeSourceProviderSourceMap(zigGlobalObject->bunVM(), provider.ptr(), &specifier);
@@ -38,7 +38,7 @@ private:
source,
sourceOrigin,
JSC::SourceTaintedOrigin::Untainted,
WTFMove(sourceURL),
WTF::move(sourceURL),
startPosition,
sourceType)
{

View File

@@ -2845,7 +2845,7 @@ pub fn finalizeBundle(
if (dev.bundling_failures.count() == 0) {
if (current_bundle.had_reload_event) {
const clear_terminal = !debug.isVisible();
const clear_terminal = !debug.isVisible() and !dev.vm.transpiler.env.hasSetNoClearTerminalOnReload(false);
if (clear_terminal) {
Output.disableBuffering();
Output.resetTerminalAll();

View File

@@ -925,7 +925,7 @@ pub fn IncrementalGraph(comptime side: bake.Side) type {
// When an import record is duplicated, it gets marked unused.
// This happens in `ConvertESMExportsForHmr.deduplicatedImport`
// There is still a case where deduplication must happen.
if (import_record.is_unused) return .stop;
if (import_record.flags.is_unused) return .stop;
if (import_record.source_index.isRuntime()) return .stop;
const key = import_record.path.keyForIncrementalGraph();
@@ -1044,7 +1044,7 @@ pub fn IncrementalGraph(comptime side: bake.Side) type {
// When an import record is duplicated, it gets marked unused.
// This happens in `ConvertESMExportsForHmr.deduplicatedImport`
// There is still a case where deduplication must happen.
if (import_record.is_unused) continue;
if (import_record.flags.is_unused) continue;
if (!import_record.source_index.isRuntime()) try_index_record: {
// TODO: move this block into a function

View File

@@ -132,8 +132,6 @@ pub fn watcherReleaseAndSubmitEvent(self: *Self, ev: *HotReloadEvent) void {
self.dbg_server_event = ev;
}
ev.concurrent_task = .{
.auto_delete = false,
.next = null,
.task = jsc.Task.init(ev),
};
ev.owner.vm.event_loop.enqueueTaskConcurrent(&ev.concurrent_task);

View File

@@ -25,7 +25,7 @@ public:
const TextPosition& startPosition,
JSC::SourceProviderSourceType sourceType)
{
auto provider = adoptRef(*new DevServerSourceProvider(source, sourceMapJSONPtr, sourceMapJSONLength, sourceOrigin, WTFMove(sourceURL), startPosition, sourceType));
auto provider = adoptRef(*new DevServerSourceProvider(source, sourceMapJSONPtr, sourceMapJSONLength, sourceOrigin, WTF::move(sourceURL), startPosition, sourceType));
auto* zigGlobalObject = jsCast<::Zig::GlobalObject*>(globalObject);
auto specifier = Bun::toString(provider->sourceURL());
provider->m_globalObject = zigGlobalObject;
@@ -52,7 +52,7 @@ private:
source,
sourceOrigin,
JSC::SourceTaintedOrigin::Untainted,
WTFMove(sourceURL),
WTF::move(sourceURL),
startPosition,
sourceType)
, m_sourceMapJSON(sourceMapJSONPtr, sourceMapJSONLength)

View File

@@ -697,7 +697,7 @@ pub fn buildWithVm(ctx: bun.cli.Command.Context, cwd: []const u8, vm: *VirtualMa
route_param_info,
route_style_references,
);
render_promise.setHandled(vm.jsc_vm);
render_promise.setHandled();
vm.waitForPromise(.{ .normal = render_promise });
switch (render_promise.unwrap(vm.jsc_vm, .mark_handled)) {
.pending => unreachable,

Some files were not shown because too many files have changed in this diff Show More