Two files, one repository
oven-sh/bun ships 2 formats across 9 indexed files. The question worth asking is whether the second one says anything the first does not.
CompareCLAUDE.md ↔ AGENTS.md
| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 30 | 19 | 0% |
| Commands | 0 | 4 | 37 | 0% |
| Section tags | 4 | 4 | 3 | 36% |
What each file covers
Sections
0 shared · 30 only in A · 19 only in B- − V8 C++ API Implementation Guide
- − Architecture Overview
- − Directory Structure
- − Implementing New V8 APIs
- − 1. Create Header and Implementation Files
- − 2. Add Symbol Exports
- − Build your changes first
- − Extract symbols
- − Use the provided PowerShell script in the comments:
- − 3. Add Tests
- − 4. Handle Special Cases
- − Memory Management Guidelines
- − Handle Scopes
- − JSC Integration
- − Tagged Pointers
- − Testing Strategy
- − Comprehensive Testing
- − Test Categories
- − Adding New Tests
- − Debugging Tips
- − Build and Test
- − Build debug version (takes ~5 minutes)
- − Run V8 tests
- − Run specific test
- − Common Issues
- − Debug Logging
- − Advanced Topics
- − Inline Function Compatibility
- − Cross-Platform Considerations
- − Contributing
- + Building and Running Bun
- + Build Commands
- + Changes that don't require a build
- + Testing
- + Running Tests
- + Test Organization
- + Writing Tests
- + Code Architecture
- + Language Structure
- + Core Source Organization
- + JavaScript Class Implementation (C++)
- + Code Generation
- + JavaScript Modules (`src/js/`)
- + Landing PRs: What Bun Reviewers Catch
- + Important Development Notes
- + Debugging CI Failures
- + Reading PR Feedback
- + Machine-readable output for jq pipelines — one object per entry.
- + Resolved threads and bot noise (robobun CI status, CodeRabbit summaries) are filtered out.
Commands
0 shared · 4 only in A · 37 only in B- − bun bd --help
- − bun bd test test/v8/v8.test.ts
- − bun bd test test/v8/v8.test.ts -t "can create small integer"
- − bun bd test test/v8/v8.test.ts -t "your test name"
- + bun bd test foo.test.ts
- + bun run build test foo.test.ts
- + bun run build:release -p 'Bun.version'
- + bun run build:local run script.ts
- + bun test test/integration/bun-types/bun-types.test.ts
- + bun run ci:errors
- + bun run ci:errors '#26173'
- + bun run ci:status
- + bun run ci:logs
- + bun run ci:find
- + bun run ci:watch
- + bun run pr:comments
- + bun run pr:comments 28838
- + bun run pr:comments --include-resolved
- + bun run pr:comments --json | jq '.[] | select(.user == "Jarred-Sumner")'
- + bun bd
- + bun bd test <test-file>
- + bun test
- + bun bd <command>
- + bun run build
- + tsc
- + bun bd test test/js/bun/http/serve.test.ts
- + bun bd test http/serve.test.ts
- + bun bd test test/js/bun/http/serve.test.ts -t "should handle"
- + bun bd test <file>
- + bun/
- + bun.sys
- + node:crypto
- + npm.rs
- + node:fs
- + node:*
- + node:path
- + bun:ffi
- + bun:sqlite
- + bun <file>
- + bun bd test
- + bun run rust:check-all
Section tags
4 shared · 4 only in A · 3 only in B- − testing-strategy
- − api
- − performance
- − deployment
- + code-style
- + git-pr
- + do-not
- build
- test
- architecture
- docs
Line diff
oven-sh/bun · src/jsc/bindings/v8/AGENTS.md
@@ −1 @@
1# V8 C++ API Implementation Guide
2
3This directory contains Bun's implementation of the V8 C++ API on top of JavaScriptCore. This allows native Node.js modules that use V8 APIs to work with Bun.
4
5## Architecture Overview
6
7Bun implements V8 APIs by creating a compatibility layer that:
8
9- Maps V8's `Local<T>` handles to JSC's `JSValue` system
10- Uses handle scopes to manage memory lifetimes similar to V8
11- Provides V8-compatible object layouts that inline V8 functions can read
12- Manages tagged pointers for efficient value representation
13
14For detailed background, see the blog series:
15
16- [Part 1: Introduction and challenges](https://bun.com/blog/how-bun-supports-v8-apis-without-using-v8-part-1.md)
17- [Part 2: Memory layout and object representation](https://bun.com/blog/how-bun-supports-v8-apis-without-using-v8-part-2.md)
18- [Part 3: Garbage collection and primitives](https://bun.com/blog/how-bun-supports-v8-apis-without-using-v8-part-3.md)
19
20## Directory Structure
21
22```
23src/jsc/bindings/v8/
24├── v8.h # Main header with V8_UNIMPLEMENTED macro
25├── v8_*.h # V8 compatibility headers
26├── V8*.h # V8 class headers (Number, String, Object, etc.)
27├── V8*.cpp # V8 class implementations
28├── shim/ # Internal implementation details
29│ ├── Handle.h # Handle and ObjectLayout implementation
30│ ├── HandleScopeBuffer.h # Handle scope memory management
31│ ├── TaggedPointer.h # V8-style tagged pointer implementation
32│ ├── Map.h # V8 Map objects for inline function compatibility
33│ ├── GlobalInternals.h # V8 global state management
34│ ├── InternalFieldObject.h # Objects with internal fields
35│ └── Oddball.h # Primitive values (undefined, null, true, false)
36├── node.h # Node.js module registration compatibility
37└── real_v8.h # Includes real V8 headers when needed
38```
39
40## Implementing New V8 APIs
41
42### 1. Create Header and Implementation Files
43
44Create `V8NewClass.h`:
45
46```cpp
47#pragma once
48
49#include "v8.h"
50#include "V8Local.h"
51#include "V8Isolate.h"
52
53namespace v8 {
54
55class NewClass : public Data {
56public:
57 BUN_EXPORT static Local<NewClass> New(Isolate* isolate, /* parameters */);
58 BUN_EXPORT /* return_type */ SomeMethod() const;
59
60 // Add other methods as needed
61};
62
63} // namespace v8
64```
65
66Create `V8NewClass.cpp`:
67
68```cpp
69#include "V8NewClass.h"
70#include "V8HandleScope.h"
71#include "v8_compatibility_assertions.h"
72
73ASSERT_V8_TYPE_LAYOUT_MATCHES(v8::NewClass)
74
75namespace v8 {
76
77Local<NewClass> NewClass::New(Isolate* isolate, /* parameters */)
78{
79 // Implementation - typically:
80 // 1. Create JSC value
81 // 2. Get current handle scope
82 // 3. Create local handle
83 return isolate->currentHandleScope()->createLocal<NewClass>(isolate->vm(), /* JSC value */);
84}
85
86/* return_type */ NewClass::SomeMethod() const
87{
88 // Implementation - typically:
89 // 1. Convert this Local to JSValue via localToJSValue()
90 // 2. Perform JSC operations
91 // 3. Return converted result
92 auto jsValue = localToJSValue();
93 // ... JSC operations ...
94 return /* result */;
95}
96
97} // namespace v8
98```
99
100### 2. Add Symbol Exports
101
102For each new C++ method, you must add the mangled symbol names to multiple files:
103
104#### a. Add to `src/runtime/napi/napi_body.rs`
105
106Find the `v8_api` module and add entries to both the `#[cfg(not(windows))]` (Itanium) and `#[cfg(windows)]` (MSVC) `extern "C"` blocks:
107
108```rust
109#[cfg(not(windows))]
110mod v8_api {
111 use core::ffi::c_void;
112 unsafe extern "C" {
113 // ... existing functions ...
114 pub(super) fn _ZN2v88NewClass3NewEPNS_7IsolateE/* parameters */() -> *mut c_void;
115 pub(super) fn _ZNK2v88NewClass10SomeMethodEv() -> *mut c_void;
116 }
117}
118#[cfg(windows)]
119mod v8_api {
120 use core::ffi::c_void;
121 unsafe extern "C" {
122 // ... existing functions ...
123 #[link_name = "?New@NewClass@v8@@SA?AV?$Local@VNewClass@v8@@@2@PEAVIsolate@2@/* parameters */@Z"]
124 pub(super) fn NewClass_New() -> *mut c_void;
125 #[link_name = "?SomeMethod@NewClass@v8@@QEBA/* return_type */XZ"]
126 pub(super) fn NewClass_SomeMethod() -> *mut c_void;
127 }
128}
129```
130
131**To get the correct mangled names:**
132
133For **GCC/Clang** (Unix):
134
135```bash
136# Build your changes first
137bun bd --help # This compiles your code
138
139# Extract symbols
140nm build/CMakeFiles/bun-debug.dir/src/jsc/bindings/v8/V8NewClass.cpp.o | grep "T _ZN2v8"
141```
142
143For **MSVC** (Windows):
144
145```powershell
146# Use the provided PowerShell script in the comments:
147dumpbin .\build\CMakeFiles\bun-debug.dir\src\jsc\bindings\v8\V8NewClass.cpp.obj /symbols | where-object { $_.Contains(' v8::') } | foreach-object { (($_ -split "\|")[1] -split " ")[1] } | ForEach-Object { "#[link_name = `"${_}`"] pub(super) fn ___() -> *mut c_void;" }
148```
149
150#### b. Add to Symbol Files
151
152Add to `src/symbols.txt` (without leading underscore):
153
154```
155_ZN2v88NewClass3NewEPNS_7IsolateE...
156_ZNK2v88NewClass10SomeMethodEv
157```
158
159Add to `src/symbols.dyn` (with leading underscore and semicolons):
160
161```
162{
163 __ZN2v88NewClass3NewEPNS_7IsolateE...;
164 __ZNK2v88NewClass10SomeMethodEv;
165}
166```
167
168**Note:** `src/symbols.def` is Windows-only and typically doesn't contain V8 symbols.
169
170### 3. Add Tests
171
172Create tests in `test/v8/v8-module/main.cpp`:
173
174```cpp
175void test_new_class_feature(const FunctionCallbackInfo<Value> &info) {
176 Isolate* isolate = info.GetIsolate();
177
178 // Test your new V8 API
179 Local<NewClass> obj = NewClass::New(isolate, /* parameters */);
180 auto result = obj->SomeMethod();
181
182 // Print results for comparison with Node.js
183 std::cout << "Result: " << result << std::endl;
184
185 info.GetReturnValue().Set(Undefined(isolate));
186}
187```
188
189Add the test to the registration section:
190
191```cpp
192void Init(Local<Object> exports, Local<Value> module, Local<Context> context) {
193 // ... existing functions ...
194 NODE_SET_METHOD(exports, "test_new_class_feature", test_new_class_feature);
195}
196```
197
198Add test case to `test/v8/v8.test.ts`:
199
200```typescript
201describe("NewClass", () => {
202 it("can use new feature", async () => {
203 await checkSameOutput("test_new_class_feature", []);
204 });
205});
206```
207
208### 4. Handle Special Cases
209
210#### Objects with Internal Fields
211
212If implementing objects that need internal fields, extend `InternalFieldObject`:
213
214```cpp
215// In your .h file
216class MyObject : public InternalFieldObject {
217 // ... implementation
218};
219```
220
221#### Primitive Values
222
223For primitive values, ensure they work with the `Oddball` system in `shim/Oddball.h`.
224
225#### Template Classes
226
227For `ObjectTemplate` or `FunctionTemplate` implementations, see existing patterns in `V8ObjectTemplate.cpp` and `V8FunctionTemplate.cpp`.
228
229## Memory Management Guidelines
230
231### Handle Scopes
232
233- All V8 values must be created within an active handle scope
234- Use `isolate->currentHandleScope()->createLocal<T>()` to create handles
235- Handle scopes automatically clean up when destroyed
236
237### JSC Integration
238
239- Use `localToJSValue()` to convert V8 handles to JSC values
240- Use `JSC::WriteBarrier` for heap-allocated references
241- Implement `visitChildren()` for custom heap objects
242
243### Tagged Pointers
244
245- Small integers (±2^31) are stored directly as Smis
246- Objects use pointer tagging with map pointers
247- Doubles are stored in object layouts with special maps
248
249## Testing Strategy
250
251### Comprehensive Testing
252
253The V8 test suite compares output between Node.js and Bun for the same C++ code:
254
2551. **Install Phase**: Sets up identical module builds for Node.js and Bun
2562. **Build Phase**: Compiles native modules using node-gyp
2573. **Test Phase**: Runs identical C++ functions and compares output
258
259### Test Categories
260
261- **Primitives**: undefined, null, booleans, numbers, strings
262- **Objects**: creation, property access, internal fields
263- **Arrays**: creation, length, iteration, element access
264- **Functions**: callbacks, templates, argument handling
265- **Memory**: handle scopes, garbage collection, external data
266- **Advanced**: templates, inheritance, error handling
267
268### Adding New Tests
269
2701. Add C++ test function to `test/v8/v8-module/main.cpp`
2712. Register function in the module exports
2723. Add test case to `test/v8/v8.test.ts` using `checkSameOutput()`
2734. Run with: `bun bd test test/v8/v8.test.ts -t "your test name"`
274
275## Debugging Tips
276
277### Build and Test
278
279```bash
280# Build debug version (takes ~5 minutes)
281bun bd --help
282
283# Run V8 tests
284bun bd test test/v8/v8.test.ts
285
286# Run specific test
287bun bd test test/v8/v8.test.ts -t "can create small integer"
288```
289
290### Common Issues
291
292**Symbol Not Found**: Ensure mangled names are correctly added to `napi_body.rs` and symbol files.
293
294**Segmentation Fault**: Usually indicates inline V8 functions are reading incorrect memory layouts. Check `Map` setup and `ObjectLayout` structure.
295
296**GC Issues**: Objects being freed prematurely. Ensure proper `WriteBarrier` usage and `visitChildren()` implementation.
297
298**Type Mismatches**: Use `v8_compatibility_assertions.h` macros to verify type layouts match V8 expectations.
299
300### Debug Logging
301
302Use `V8_UNIMPLEMENTED()` macro for functions not yet implemented:
303
304```cpp
305void MyClass::NotYetImplemented() {
306 V8_UNIMPLEMENTED();
307}
308```
309
310## Advanced Topics
311
312### Inline Function Compatibility
313
314Many V8 functions are inline and compiled into native modules. The memory layout must exactly match what these functions expect:
315
316- Objects start with tagged pointer to `Map`
317- Maps have instance type at offset 12
318- Handle scopes store tagged pointers
319- Primitive values at fixed global offsets
320
321### Cross-Platform Considerations
322
323- Symbol mangling differs between GCC/Clang and MSVC
324- Handle calling conventions (JSC uses System V on Unix)
325- Ensure `BUN_EXPORT` visibility on all public functions
326- Test on all target platforms via CI
327
328## Contributing
329
330When contributing V8 API implementations:
331
3321. **Follow existing patterns** in similar classes
3332. **Add comprehensive tests** that compare with Node.js
3343. **Update all symbol files** with correct mangled names
3354. **Document any special behavior** or limitations
336
337For questions about V8 API implementation, refer to the blog series linked above or examine existing implementations in this directory.
338
oven-sh/bun · CLAUDE.md
@@ +1 @@
1This is the Bun repository - an all-in-one JavaScript runtime & toolkit designed for speed, with a bundler, test runner, and Node.js-compatible package manager. It's written primarily in Rust with C++ for JavaScriptCore integration, powered by WebKit's JavaScriptCore engine.
2
3## Building and Running Bun
4
5### Build Commands
6
7- **Build Bun**: `bun bd`
8 - Creates a debug build at `./build/debug/bun-debug`
9 - **CRITICAL**: do not set a timeout when running `bun bd`
10- **Run tests with your debug build**: `bun bd test <test-file>`
11 - **CRITICAL**: Never use `bun test` directly - it won't include your changes
12- **Run any command with debug build**: `bun bd <command>`
13- **Run with JavaScript exception scope verification**: `BUN_JSC_validateExceptionChecks=1
14BUN_JSC_dumpSimulatedThrows=1 bun bd <command>`
15
16Tip: Bun is already installed and in $PATH. The `bd` subcommand is a package.json script.
17
18**All build scripts support build-then-exec.** Any `bun run build*` command (and `bun bd`) accepts trailing args which are passed to the built executable after building — you never invoke `./build/debug/bun-debug` directly.
19
20```sh
21bun bd test foo.test.ts # debug build + quiet debug logs
22bun run build test foo.test.ts # debug build
23bun run build:release -p 'Bun.version' # release build
24bun run build:local run script.ts # debug build with local WebKit
25```
26
27When exec args are present, build output is suppressed unless the build fails — you see only the binary's output. Build flags (e.g. `--asan=off`) go before the exec args; see `scripts/build.ts` header for the full arg routing rules.
28
29### Changes that don't require a build
30
31Edits to **TypeScript type declarations** (`packages/bun-types/**/*.d.ts`) do not touch any compiled code, so `bun bd` is unnecessary. The types test just packs the `.d.ts` files and runs `tsc` against fixtures — it never executes your build. Run it directly with the system Bun (an explicit exception to the "never use `bun test` directly" rule):
32
33```sh
34bun test test/integration/bun-types/bun-types.test.ts
35```
36
37This is an explicit exception to the "never use `bun test` directly" rule. There are no native changes for a debug build to pick up, so don't wait on one.
38
39## Testing
40
41### Running Tests
42
43- **Single test file**: `bun bd test test/js/bun/http/serve.test.ts`
44- **Fuzzy match test file**: `bun bd test http/serve.test.ts`
45- **With filter**: `bun bd test test/js/bun/http/serve.test.ts -t "should handle"`
46
47### Test Organization
48
49**Default: add your test to the existing test file for the code you're changing.** Do not create a new file. A fetch bug goes in `test/js/web/fetch/fetch.test.ts`, a `Bun.serve` bug goes in `test/js/bun/http/serve.test.ts`, and so on. Keeping tests next to related coverage is what makes them discoverable and prevents duplicated setup.
50
51- `test/js/bun/` - Bun-specific API tests (http, crypto, ffi, shell, etc.)
52- `test/js/node/` - Node.js compatibility tests
53- `test/js/web/` - Web API tests (fetch, WebSocket, streams, etc.)
54- `test/cli/` - CLI command tests (install, run, test, etc.)
55- `test/bundler/` - Bundler and transpiler tests. Use `itBundled` helper.
56- `test/integration/` - End-to-end integration tests
57- `test/napi/` - N-API compatibility tests
58- `test/v8/` - V8 C++ API compatibility tests
59
60**Exception:** `test/regression/issue/${issueNumber}.test.ts` is reserved for bugs with a GitHub issue number **and** that are true regressions (worked in a previous release, then broke). If the behavior was never correct, it's not a regression — the test belongs in the existing file for that module. The issue number must be **REAL**, not a placeholder.
61
62### Writing Tests
63
64Tests use Bun's Jest-compatible test runner. For **single-file tests**, prefer spawning with `-e`; for **multi-file tests**, prefer `tempDir` and `Bun.spawn`:
65
66```typescript
67import { test, expect } from "bun:test";
68import { bunEnv, bunExe, normalizeBunSnapshot, tempDir } from "harness";
69
70 const [stdout, stderr, exitCode] = await Promise.all([
71test("(multi-file test) my feature", async () => {
72 using dir = tempDir("test-prefix", {
73 "index.js": `import { foo } from "./foo.ts"; foo();`,
74 "foo.ts": `export function foo() { console.log("foo"); }`,
75 });
76 // For a single-file test, use: cmd: [bunExe(), "-e", `console.log("foo")`] and omit cwd.
77 await using proc = Bun.spawn({
78 cmd: [bunExe(), "index.js"],
79 env: bunEnv,
80 cwd: String(dir),
81 stderr: "pipe",
82 });
83
84 const [stdout, stderr, exitCode] = await Promise.all([
85 proc.stdout.text(),
86 proc.stderr.text(),
87 proc.exited,
88 ]);
89
90 // Prefer snapshot tests over expect(stdout).toBe("hello\n");
91 expect(normalizeBunSnapshot(stdout, dir)).toMatchInlineSnapshot(`"foo"`);
92
93 // Assert the exit code last. This gives you a more useful error message on test failure.
94 expect(exitCode).toBe(0);
95});
96```
97
98- Always use `port: 0`. Do not hardcode ports. Do not use your own random port number function.
99- Use `normalizeBunSnapshot` to normalize snapshot output of the test.
100- NEVER write tests that check for no "panic" or "uncaught exception" or similar in the test output. These tests will never fail in CI.
101- Use `tempDir` from `"harness"` to create a temporary directory. **Do not** use `tmpdirSync` or `fs.mkdtempSync` to create temporary directories.
102- When spawning processes, tests should expect(stdout).toBe(...) BEFORE expect(exitCode).toBe(0). This gives you a more useful error message on test failure.
103- Keep tests fast: budget roughly 1s per test and 10s per file. Debug+ASAN builds run 10-100x slower than release, so a 1s local test can take a minute in CI. Use `test.concurrent` for independent subprocess-spawning tests.
104- Never contact the public internet (registry.npmjs.org, github.com, CDNs). Use `VerdaccioRegistry` from `"harness"` for package installs and a local `Bun.serve({ port: 0 })` for HTTP.
105- `setDefaultTimeout` is a ceiling, not a target. Leave the default and pass a per-test timeout only for the rare outlier; a 5-minute file default multiplies across retries when one test hangs.
106- Leak tests branch their RSS threshold on `isASAN`/`isDebug` and keep the bound well below what the unfixed leak produces. An un-branched absolute delta flakes under ASAN quarantine and GC jitter.
107- **CRITICAL**: Do not write flaky tests. Do not use `setTimeout` or `await sleep(N)` to wait for a condition; poll with a deadline or `await` the event itself. You are not testing the TIME PASSING, you are testing the CONDITION.
108- **CRITICAL**: Verify your test fails with `USE_SYSTEM_BUN=1 bun test <file>` and passes with `bun bd test <file>`. Your test is NOT VALID if it passes with `USE_SYSTEM_BUN=1`.
109
110## Code Architecture
111
112### Language Structure
113
114- **Rust code** (`src/**/*.rs`): Core runtime, JavaScript bindings, bundler, package manager. This is what compiles and ships.
115- **C++ code** (`src/jsc/bindings/*.cpp`): JavaScriptCore bindings, Web APIs
116- **TypeScript** (`src/js/`): Built-in JavaScript modules with special syntax (see JavaScript Modules section)
117- **Generated code**: Many `.rs` and `.cpp` files are auto-generated from `.classes.ts` and other sources. The build regenerates them automatically when their inputs change.
118
119### Core Source Organization
120
121The Rust side is a Cargo workspace of ~200 crates rooted at `Cargo.toml`. The key ones:
122
123- `src/bun_core/` - The `bun.*`-namespace foundation: strings/`String` (`string/`), formatting (`fmt.rs`), logging (`output.rs`), feature flags, env vars, allocator helpers
124- `src/sys/` - Cross-platform syscall wrappers (`file.rs`, `dir.rs`, `fd.rs`, `Error.rs`, `tmp.rs`) — the `bun.sys` equivalent
125- `src/collections/`, `src/threading/`, `src/paths/`, `src/semver/`, `src/sourcemap/` - shared utilities
126- `src/bun_bin/` - Cargo entrypoint; produces `libbun_rust.a`, linked into the final binary
127- `src/runtime/cli/` - CLI argument parsing and command dispatch
128- `src/js_parser/`, `src/js_printer/` - JavaScript/TypeScript parsing and printing (each is its own crate; the lexer is `src/js_parser/lexer.rs`)
129- `src/transpiler/` - Wrapper around the parser/printer with sourcemap support
130- `src/resolver/` - Module resolution system
131- `src/ast/` - AST node types and arena allocation
132- `src/jsc/bindings/` - C++ JavaScriptCore bindings (generated classes from `.classes.ts` + manual bindings)
133- `src/jsc/` - Rust-side JSC glue (`VirtualMachine.rs`, `web_worker.rs`, `event_loop.rs`, FFI imports)
134- `src/runtime/api/` - Bun-specific JS-visible APIs (`BunObject.rs`, `JSBundler.rs`, `Glob`, `Archive`, …)
135- `src/runtime/server/` - `Bun.serve` HTTP/WebSocket server
136- `src/runtime/node/` - Node.js compatibility layer (fs, path, process, Buffer, …)
137- `src/runtime/crypto/` - WebCrypto + `node:crypto` (`EVP.rs`, `HMAC.rs`, `CryptoHasher.rs`, …)
138- `src/runtime/webcore/` - Web API implementations (`fetch.rs`, `streams.rs`, `Blob.rs`, `Response.rs`, `Request.rs`, …)
139- `src/event_loop/` - Event loop and task management
140- `src/bundler/` - JavaScript bundler (tree-shaking, CSS processing, HTML handling)
141- `src/install/` - Package manager (`lockfile/`, `npm.rs` registry client, `lifecycle_script_runner.rs`)
142- `src/shell/` - Cross-platform shell implementation
143- `src/css/` - CSS parser and processor
144- `src/http/` - HTTP client + `websocket_client/` (WebSocket, deflate)
145- `src/sql/` - SQL database integrations (Postgres, MySQL, SQLite)
146- `src/bake/` - Server-side rendering / dev server framework
147
148#### Vendored Dependencies (`vendor/`)
149
150Third-party C/C++ libraries are vendored locally and can be read from disk (not git submodules): boringssl (TLS/crypto), brotli, cares (async DNS), hdrhistogram, highway (SIMD), libarchive (tar/zip), libdeflate, libuv (Windows event loop), lolhtml (HTML rewriter), lshpack (HTTP/2 HPACK), lsqpack + lsquic (HTTP/3), mimalloc (allocator), nodejs (headers), picohttpparser, tinycc (FFI JIT, fork: oven-sh/tinycc), WebKit (JavaScriptCore), zlib (zlib-ng), zstd. Build configuration for these is in `scripts/build/deps/*.ts`.
151
152### JavaScript Class Implementation (C++)
153
154When implementing JavaScript classes in C++:
155
1561. Create three classes if there's a public constructor:
157 - `class Foo : public JSC::JSDestructibleObject` (if has C++ fields)
158 - `class FooPrototype : public JSC::JSNonFinalObject`
159 - `class FooConstructor : public JSC::InternalFunction`
1602. Define properties using HashTableValue arrays
1613. Add iso subspaces for classes with C++ fields
1624. Cache structures in `ZigGlobalObject`
163
164### Code Generation
165
166Code generation happens automatically as part of the build process. The main scripts are:
167
168- `src/codegen/generate-classes.ts` - Generates Rust & C++ bindings from `*.classes.ts` files
169- `src/codegen/generate-jssink.ts` - Generates stream-related classes
170- `src/codegen/bundle-modules.ts` - Bundles built-in modules like `node:fs`
171- `src/codegen/bundle-functions.ts` - Bundles global functions like `ReadableStream`
172
173In development, bundled JS modules can be reloaded without rebuilding native code by running `bun run build`.
174
175## JavaScript Modules (`src/js/`)
176
177Built-in JavaScript modules use special syntax and are organized as:
178
179- `node/` - Node.js compatibility modules (`node:fs`, `node:path`, etc.)
180- `bun/` - Bun-specific modules (`bun:ffi`, `bun:sqlite`, etc.)
181- `thirdparty/` - NPM modules we replace (like `ws`)
182- `internal/` - Internal modules not exposed to users
183- `builtins/` - Core JavaScript builtins (streams, console, etc.)
184
185## Landing PRs: What Bun Reviewers Catch
186
187The code review rules — what blocks merges, distilled from ~2,500 merged PRs — live in `REVIEW.md`. Read it before writing code that makes a non-obvious choice.
188
189Several situational sections live in `.claude/docs/landing-prs.md` — read the relevant one before the work it covers: **Node/Web compat** (touching `node:*` modules, Web APIs, or `src/runtime/node/`), **API design** (adding or changing user-facing API surface), **Performance** (optimizing, touching hot paths, or making perf claims), **Cross-platform** (platform-gated code, FFI/ABI, or platform-sensitive tests), **Dependencies & vendoring** (bumping deps or touching `vendor/`), **Docs, types, and comments** (docs, `.d.ts`, JSDoc), and **PR process** (opening or responding to a PR).
190
191## Important Development Notes
192
1931. **Never use `bun test` or `bun <file>` directly** - always use `bun bd test` or `bun bd <command>`. `bun bd` compiles & runs the debug build.
1942. **All changes must be tested** - if you're not testing your changes, you're not done.
1953. **Get your tests to pass**. If you didn't run the tests, your code does not work.
1964. **Follow existing code style** - check neighboring files for patterns
1975. **Create tests in the right folder** in `test/` and the test must end in `.test.ts` or `.test.tsx`
1986. **Use absolute paths** - Always use absolute paths in file operations
1997. **Avoid shell commands** - Don't use `find` or `grep` in tests; use Bun's Glob and built-in tools
2008. **Memory management** - Prefer RAII (`Drop`) over manual cleanup. Arena edge case: values allocated in an arena (`Arena<T>`/`bumpalo`) do **not** run `Drop` on arena reset — types owning a heap allocation or refcount must be freed/deref'd explicitly first, mirroring the original Zig `deinit()` order.
2019. **Cross-platform** - Run `bun run rust:check-all` to compile across all targets (linux/macos/windows × x64/aarch64) when making platform-specific changes. `#[cfg(...)]`-gated code is not type-checked unless the matching target is built.
20210. **Debug builds** - Use `BUN_DEBUG_QUIET_LOGS=1` to disable debug logging, or `BUN_DEBUG_<SCOPE>=1` to enable a specific `bun_core::output` scoped logger
20311. **Be humble & honest** - NEVER overstate what you got done or what actually works in commits, PRs or in messages to the user.
20412. **Branch names must start with `claude/`** - This is a requirement for the CI to work.
20513. **If you need a paragraph-long comment to justify why the workaround is OK, the code is wrong — fix the code.**.
20614. After every code comment you write, ask yourself, "Is this information the next Claude would spend multiple tool calls trying to understand?". If the answer isn't clearly yes, the code comment is noise - delete it.
207
208**ONLY** push up changes after running `bun bd test <file>` and ensuring your tests pass.
209
210## Debugging CI Failures
211
212Requires the BuildKite CLI (`brew install buildkite/buildkite/bk`) and a read-scoped token in `BUILDKITE_API_TOKEN`. The repo's `.bk.yaml` sets the org/pipeline so `-p bun` is not needed.
213
214```bash
215bun run ci:errors # rendered test-failure output for this branch's latest build, [new] vs [also on main]
216bun run ci:errors '#26173' # or a PR number / URL / branch / build number
217bun run ci:status # one-screen progress summary (job counts, failed jobs, failing tests so far)
218bun run ci:logs # save full logs for every failed job to ./tmp/ci-<build>/
219bun run ci:find # just the build number, e.g. bk job log <job-uuid> -b $(bun run ci:find)
220bun run ci:watch # watch the current branch's build until it finishes
221```
222
223For anything else, use `bk` directly — `bk build list`, `bk api`, `bk artifacts`, etc.
224
225If output from these commands looks wrong (mis-parsed annotation HTML, a field BuildKite changed shape on), fix `scripts/find-build.ts` directly rather than working around it — it's a thin presenter over `bk`.
226
227## Reading PR Feedback
228
229`gh pr view --comments` silently omits review summaries and line-level review comments. For the complete picture — especially when responding to a review — use `bun run pr:comments`, which fetches issue comments, reviews, and line comments in one chronological, labelled listing.
230
231```bash
232bun run pr:comments # current branch's PR — resolved threads hidden
233bun run pr:comments 28838 # by PR number; '#28838' and full URLs also work
234bun run pr:comments --include-resolved # also show threads already marked resolved
235
236# Machine-readable output for jq pipelines — one object per entry.
237# Resolved threads and bot noise (robobun CI status, CodeRabbit summaries) are filtered out.
238bun run pr:comments --json | jq '.[] | select(.user == "Jarred-Sumner")'
239```
240
@@ −1 +1 @@
1−# V8 C++ API Implementation Guide
1+This is the Bun repository - an all-in-one JavaScript runtime & toolkit designed for speed, with a bundler, test runner, and Node.js-compatible package manager. It's written primarily in Rust with C++ for JavaScriptCore integration, powered by WebKit's JavaScriptCore engine.
22
3−This directory contains Bun's implementation of the V8 C++ API on top of JavaScriptCore. This allows native Node.js modules that use V8 APIs to work with Bun.
3+## Building and Running Bun
44
5−## Architecture Overview
5+### Build Commands
66
7−Bun implements V8 APIs by creating a compatibility layer that:
7+- **Build Bun**: `bun bd`
8+ - Creates a debug build at `./build/debug/bun-debug`
9+ - **CRITICAL**: do not set a timeout when running `bun bd`
10+- **Run tests with your debug build**: `bun bd test <test-file>`
11+ - **CRITICAL**: Never use `bun test` directly - it won't include your changes
12+- **Run any command with debug build**: `bun bd <command>`
13+- **Run with JavaScript exception scope verification**: `BUN_JSC_validateExceptionChecks=1
14+BUN_JSC_dumpSimulatedThrows=1 bun bd <command>`
815
9−- Maps V8's `Local<T>` handles to JSC's `JSValue` system
10−- Uses handle scopes to manage memory lifetimes similar to V8
11−- Provides V8-compatible object layouts that inline V8 functions can read
12−- Manages tagged pointers for efficient value representation
16+Tip: Bun is already installed and in $PATH. The `bd` subcommand is a package.json script.
1317
14−For detailed background, see the blog series:
18+**All build scripts support build-then-exec.** Any `bun run build*` command (and `bun bd`) accepts trailing args which are passed to the built executable after building — you never invoke `./build/debug/bun-debug` directly.
1519
16−- [Part 1: Introduction and challenges](https://bun.com/blog/how-bun-supports-v8-apis-without-using-v8-part-1.md)
17−- [Part 2: Memory layout and object representation](https://bun.com/blog/how-bun-supports-v8-apis-without-using-v8-part-2.md)
18−- [Part 3: Garbage collection and primitives](https://bun.com/blog/how-bun-supports-v8-apis-without-using-v8-part-3.md)
19−
20−## Directory Structure
21−
20+```sh
21+bun bd test foo.test.ts # debug build + quiet debug logs
22+bun run build test foo.test.ts # debug build
23+bun run build:release -p 'Bun.version' # release build
24+bun run build:local run script.ts # debug build with local WebKit
2225 ```
23−src/jsc/bindings/v8/
24−├── v8.h # Main header with V8_UNIMPLEMENTED macro
25−├── v8_*.h # V8 compatibility headers
26−├── V8*.h # V8 class headers (Number, String, Object, etc.)
27−├── V8*.cpp # V8 class implementations
28−├── shim/ # Internal implementation details
29−│ ├── Handle.h # Handle and ObjectLayout implementation
30−│ ├── HandleScopeBuffer.h # Handle scope memory management
31−│ ├── TaggedPointer.h # V8-style tagged pointer implementation
32−│ ├── Map.h # V8 Map objects for inline function compatibility
33−│ ├── GlobalInternals.h # V8 global state management
34−│ ├── InternalFieldObject.h # Objects with internal fields
35−│ └── Oddball.h # Primitive values (undefined, null, true, false)
36−├── node.h # Node.js module registration compatibility
37−└── real_v8.h # Includes real V8 headers when needed
38−```
3926
40−## Implementing New V8 APIs
27+When exec args are present, build output is suppressed unless the build fails — you see only the binary's output. Build flags (e.g. `--asan=off`) go before the exec args; see `scripts/build.ts` header for the full arg routing rules.
4128
42−### 1. Create Header and Implementation Files
29+### Changes that don't require a build
4330
44−Create `V8NewClass.h`:
31+Edits to **TypeScript type declarations** (`packages/bun-types/**/*.d.ts`) do not touch any compiled code, so `bun bd` is unnecessary. The types test just packs the `.d.ts` files and runs `tsc` against fixtures — it never executes your build. Run it directly with the system Bun (an explicit exception to the "never use `bun test` directly" rule):
4532
46−```cpp
47−#pragma once
48−
49−#include "v8.h"
50−#include "V8Local.h"
51−#include "V8Isolate.h"
52−
53−namespace v8 {
54−
55−class NewClass : public Data {
56−public:
57− BUN_EXPORT static Local<NewClass> New(Isolate* isolate, /* parameters */);
58− BUN_EXPORT /* return_type */ SomeMethod() const;
59−
60− // Add other methods as needed
61−};
62−
63−} // namespace v8
33+```sh
34+bun test test/integration/bun-types/bun-types.test.ts
6435 ```
6536
66−Create `V8NewClass.cpp`:
37+This is an explicit exception to the "never use `bun test` directly" rule. There are no native changes for a debug build to pick up, so don't wait on one.
6738
68−```cpp
69−#include "V8NewClass.h"
70−#include "V8HandleScope.h"
71−#include "v8_compatibility_assertions.h"
39+## Testing
7240
73−ASSERT_V8_TYPE_LAYOUT_MATCHES(v8::NewClass)
41+### Running Tests
7442
75−namespace v8 {
43+- **Single test file**: `bun bd test test/js/bun/http/serve.test.ts`
44+- **Fuzzy match test file**: `bun bd test http/serve.test.ts`
45+- **With filter**: `bun bd test test/js/bun/http/serve.test.ts -t "should handle"`
7646
77−Local<NewClass> NewClass::New(Isolate* isolate, /* parameters */)
78−{
79− // Implementation - typically:
80− // 1. Create JSC value
81− // 2. Get current handle scope
82− // 3. Create local handle
83− return isolate->currentHandleScope()->createLocal<NewClass>(isolate->vm(), /* JSC value */);
84−}
47+### Test Organization
8548
86−/* return_type */ NewClass::SomeMethod() const
87−{
88− // Implementation - typically:
89− // 1. Convert this Local to JSValue via localToJSValue()
90− // 2. Perform JSC operations
91− // 3. Return converted result
92− auto jsValue = localToJSValue();
93− // ... JSC operations ...
94− return /* result */;
95−}
49+**Default: add your test to the existing test file for the code you're changing.** Do not create a new file. A fetch bug goes in `test/js/web/fetch/fetch.test.ts`, a `Bun.serve` bug goes in `test/js/bun/http/serve.test.ts`, and so on. Keeping tests next to related coverage is what makes them discoverable and prevents duplicated setup.
9650
97−} // namespace v8
98−```
51+- `test/js/bun/` - Bun-specific API tests (http, crypto, ffi, shell, etc.)
52+- `test/js/node/` - Node.js compatibility tests
53+- `test/js/web/` - Web API tests (fetch, WebSocket, streams, etc.)
54+- `test/cli/` - CLI command tests (install, run, test, etc.)
55+- `test/bundler/` - Bundler and transpiler tests. Use `itBundled` helper.
56+- `test/integration/` - End-to-end integration tests
57+- `test/napi/` - N-API compatibility tests
58+- `test/v8/` - V8 C++ API compatibility tests
9959
100−### 2. Add Symbol Exports
60+**Exception:** `test/regression/issue/${issueNumber}.test.ts` is reserved for bugs with a GitHub issue number **and** that are true regressions (worked in a previous release, then broke). If the behavior was never correct, it's not a regression — the test belongs in the existing file for that module. The issue number must be **REAL**, not a placeholder.
10161
102−For each new C++ method, you must add the mangled symbol names to multiple files:
62+### Writing Tests
10363
104−#### a. Add to `src/runtime/napi/napi_body.rs`
64+Tests use Bun's Jest-compatible test runner. For **single-file tests**, prefer spawning with `-e`; for **multi-file tests**, prefer `tempDir` and `Bun.spawn`:
10565
106−Find the `v8_api` module and add entries to both the `#[cfg(not(windows))]` (Itanium) and `#[cfg(windows)]` (MSVC) `extern "C"` blocks:
66+```typescript
67+import { test, expect } from "bun:test";
68+import { bunEnv, bunExe, normalizeBunSnapshot, tempDir } from "harness";
10769
108−```rust
109−#[cfg(not(windows))]
110−mod v8_api {
111− use core::ffi::c_void;
112− unsafe extern "C" {
113− // ... existing functions ...
114− pub(super) fn _ZN2v88NewClass3NewEPNS_7IsolateE/* parameters */() -> *mut c_void;
115− pub(super) fn _ZNK2v88NewClass10SomeMethodEv() -> *mut c_void;
116− }
117−}
118−#[cfg(windows)]
119−mod v8_api {
120− use core::ffi::c_void;
121− unsafe extern "C" {
122− // ... existing functions ...
123− #[link_name = "?New@NewClass@v8@@SA?AV?$Local@VNewClass@v8@@@2@PEAVIsolate@2@/* parameters */@Z"]
124− pub(super) fn NewClass_New() -> *mut c_void;
125− #[link_name = "?SomeMethod@NewClass@v8@@QEBA/* return_type */XZ"]
126− pub(super) fn NewClass_SomeMethod() -> *mut c_void;
127− }
128−}
129−```
70+ const [stdout, stderr, exitCode] = await Promise.all([
71+test("(multi-file test) my feature", async () => {
72+ using dir = tempDir("test-prefix", {
73+ "index.js": `import { foo } from "./foo.ts"; foo();`,
74+ "foo.ts": `export function foo() { console.log("foo"); }`,
75+ });
76+ // For a single-file test, use: cmd: [bunExe(), "-e", `console.log("foo")`] and omit cwd.
77+ await using proc = Bun.spawn({
78+ cmd: [bunExe(), "index.js"],
79+ env: bunEnv,
80+ cwd: String(dir),
81+ stderr: "pipe",
82+ });
13083
131−**To get the correct mangled names:**
84+ const [stdout, stderr, exitCode] = await Promise.all([
85+ proc.stdout.text(),
86+ proc.stderr.text(),
87+ proc.exited,
88+ ]);
13289
133−For **GCC/Clang** (Unix):
90+ // Prefer snapshot tests over expect(stdout).toBe("hello\n");
91+ expect(normalizeBunSnapshot(stdout, dir)).toMatchInlineSnapshot(`"foo"`);
13492
135−```bash
136−# Build your changes first
137−bun bd --help # This compiles your code
138−
139−# Extract symbols
140−nm build/CMakeFiles/bun-debug.dir/src/jsc/bindings/v8/V8NewClass.cpp.o | grep "T _ZN2v8"
141−```
142−
143−For **MSVC** (Windows):
144−
145−```powershell
146−# Use the provided PowerShell script in the comments:
147−dumpbin .\build\CMakeFiles\bun-debug.dir\src\jsc\bindings\v8\V8NewClass.cpp.obj /symbols | where-object { $_.Contains(' v8::') } | foreach-object { (($_ -split "\|")[1] -split " ")[1] } | ForEach-Object { "#[link_name = `"${_}`"] pub(super) fn ___() -> *mut c_void;" }
148−```
149−
150−#### b. Add to Symbol Files
151−
152−Add to `src/symbols.txt` (without leading underscore):
153−
154−```
155−_ZN2v88NewClass3NewEPNS_7IsolateE...
156−_ZNK2v88NewClass10SomeMethodEv
157−```
158−
159−Add to `src/symbols.dyn` (with leading underscore and semicolons):
160−
161−```
162−{
163− __ZN2v88NewClass3NewEPNS_7IsolateE...;
164− __ZNK2v88NewClass10SomeMethodEv;
165−}
166−```
167−
168−**Note:** `src/symbols.def` is Windows-only and typically doesn't contain V8 symbols.
169−
170−### 3. Add Tests
171−
172−Create tests in `test/v8/v8-module/main.cpp`:
173−
174−```cpp
175−void test_new_class_feature(const FunctionCallbackInfo<Value> &info) {
176− Isolate* isolate = info.GetIsolate();
177−
178− // Test your new V8 API
179− Local<NewClass> obj = NewClass::New(isolate, /* parameters */);
180− auto result = obj->SomeMethod();
181−
182− // Print results for comparison with Node.js
183− std::cout << "Result: " << result << std::endl;
184−
185− info.GetReturnValue().Set(Undefined(isolate));
186−}
187−```
188−
189−Add the test to the registration section:
190−
191−```cpp
192−void Init(Local<Object> exports, Local<Value> module, Local<Context> context) {
193− // ... existing functions ...
194− NODE_SET_METHOD(exports, "test_new_class_feature", test_new_class_feature);
195−}
196−```
197−
198−Add test case to `test/v8/v8.test.ts`:
199−
200−```typescript
201−describe("NewClass", () => {
202− it("can use new feature", async () => {
203− await checkSameOutput("test_new_class_feature", []);
204− });
93+ // Assert the exit code last. This gives you a more useful error message on test failure.
94+ expect(exitCode).toBe(0);
20595 });
20696 ```
20797
208−### 4. Handle Special Cases
98+- Always use `port: 0`. Do not hardcode ports. Do not use your own random port number function.
99+- Use `normalizeBunSnapshot` to normalize snapshot output of the test.
100+- NEVER write tests that check for no "panic" or "uncaught exception" or similar in the test output. These tests will never fail in CI.
101+- Use `tempDir` from `"harness"` to create a temporary directory. **Do not** use `tmpdirSync` or `fs.mkdtempSync` to create temporary directories.
102+- When spawning processes, tests should expect(stdout).toBe(...) BEFORE expect(exitCode).toBe(0). This gives you a more useful error message on test failure.
103+- Keep tests fast: budget roughly 1s per test and 10s per file. Debug+ASAN builds run 10-100x slower than release, so a 1s local test can take a minute in CI. Use `test.concurrent` for independent subprocess-spawning tests.
104+- Never contact the public internet (registry.npmjs.org, github.com, CDNs). Use `VerdaccioRegistry` from `"harness"` for package installs and a local `Bun.serve({ port: 0 })` for HTTP.
105+- `setDefaultTimeout` is a ceiling, not a target. Leave the default and pass a per-test timeout only for the rare outlier; a 5-minute file default multiplies across retries when one test hangs.
106+- Leak tests branch their RSS threshold on `isASAN`/`isDebug` and keep the bound well below what the unfixed leak produces. An un-branched absolute delta flakes under ASAN quarantine and GC jitter.
107+- **CRITICAL**: Do not write flaky tests. Do not use `setTimeout` or `await sleep(N)` to wait for a condition; poll with a deadline or `await` the event itself. You are not testing the TIME PASSING, you are testing the CONDITION.
108+- **CRITICAL**: Verify your test fails with `USE_SYSTEM_BUN=1 bun test <file>` and passes with `bun bd test <file>`. Your test is NOT VALID if it passes with `USE_SYSTEM_BUN=1`.
209109
210−#### Objects with Internal Fields
110+## Code Architecture
211111
212−If implementing objects that need internal fields, extend `InternalFieldObject`:
112+### Language Structure
213113
214−```cpp
215−// In your .h file
216−class MyObject : public InternalFieldObject {
217− // ... implementation
218−};
219−```
114+- **Rust code** (`src/**/*.rs`): Core runtime, JavaScript bindings, bundler, package manager. This is what compiles and ships.
115+- **C++ code** (`src/jsc/bindings/*.cpp`): JavaScriptCore bindings, Web APIs
116+- **TypeScript** (`src/js/`): Built-in JavaScript modules with special syntax (see JavaScript Modules section)
117+- **Generated code**: Many `.rs` and `.cpp` files are auto-generated from `.classes.ts` and other sources. The build regenerates them automatically when their inputs change.
220118
221−#### Primitive Values
119+### Core Source Organization
222120
223−For primitive values, ensure they work with the `Oddball` system in `shim/Oddball.h`.
121+The Rust side is a Cargo workspace of ~200 crates rooted at `Cargo.toml`. The key ones:
224122
225−#### Template Classes
123+- `src/bun_core/` - The `bun.*`-namespace foundation: strings/`String` (`string/`), formatting (`fmt.rs`), logging (`output.rs`), feature flags, env vars, allocator helpers
124+- `src/sys/` - Cross-platform syscall wrappers (`file.rs`, `dir.rs`, `fd.rs`, `Error.rs`, `tmp.rs`) — the `bun.sys` equivalent
125+- `src/collections/`, `src/threading/`, `src/paths/`, `src/semver/`, `src/sourcemap/` - shared utilities
126+- `src/bun_bin/` - Cargo entrypoint; produces `libbun_rust.a`, linked into the final binary
127+- `src/runtime/cli/` - CLI argument parsing and command dispatch
128+- `src/js_parser/`, `src/js_printer/` - JavaScript/TypeScript parsing and printing (each is its own crate; the lexer is `src/js_parser/lexer.rs`)
129+- `src/transpiler/` - Wrapper around the parser/printer with sourcemap support
130+- `src/resolver/` - Module resolution system
131+- `src/ast/` - AST node types and arena allocation
132+- `src/jsc/bindings/` - C++ JavaScriptCore bindings (generated classes from `.classes.ts` + manual bindings)
133+- `src/jsc/` - Rust-side JSC glue (`VirtualMachine.rs`, `web_worker.rs`, `event_loop.rs`, FFI imports)
134+- `src/runtime/api/` - Bun-specific JS-visible APIs (`BunObject.rs`, `JSBundler.rs`, `Glob`, `Archive`, …)
135+- `src/runtime/server/` - `Bun.serve` HTTP/WebSocket server
136+- `src/runtime/node/` - Node.js compatibility layer (fs, path, process, Buffer, …)
137+- `src/runtime/crypto/` - WebCrypto + `node:crypto` (`EVP.rs`, `HMAC.rs`, `CryptoHasher.rs`, …)
138+- `src/runtime/webcore/` - Web API implementations (`fetch.rs`, `streams.rs`, `Blob.rs`, `Response.rs`, `Request.rs`, …)
139+- `src/event_loop/` - Event loop and task management
140+- `src/bundler/` - JavaScript bundler (tree-shaking, CSS processing, HTML handling)
141+- `src/install/` - Package manager (`lockfile/`, `npm.rs` registry client, `lifecycle_script_runner.rs`)
142+- `src/shell/` - Cross-platform shell implementation
143+- `src/css/` - CSS parser and processor
144+- `src/http/` - HTTP client + `websocket_client/` (WebSocket, deflate)
145+- `src/sql/` - SQL database integrations (Postgres, MySQL, SQLite)
146+- `src/bake/` - Server-side rendering / dev server framework
226147
227−For `ObjectTemplate` or `FunctionTemplate` implementations, see existing patterns in `V8ObjectTemplate.cpp` and `V8FunctionTemplate.cpp`.
148+#### Vendored Dependencies (`vendor/`)
228149
229−## Memory Management Guidelines
150+Third-party C/C++ libraries are vendored locally and can be read from disk (not git submodules): boringssl (TLS/crypto), brotli, cares (async DNS), hdrhistogram, highway (SIMD), libarchive (tar/zip), libdeflate, libuv (Windows event loop), lolhtml (HTML rewriter), lshpack (HTTP/2 HPACK), lsqpack + lsquic (HTTP/3), mimalloc (allocator), nodejs (headers), picohttpparser, tinycc (FFI JIT, fork: oven-sh/tinycc), WebKit (JavaScriptCore), zlib (zlib-ng), zstd. Build configuration for these is in `scripts/build/deps/*.ts`.
230151
231−### Handle Scopes
152+### JavaScript Class Implementation (C++)
232153
233−- All V8 values must be created within an active handle scope
234−- Use `isolate->currentHandleScope()->createLocal<T>()` to create handles
235−- Handle scopes automatically clean up when destroyed
154+When implementing JavaScript classes in C++:
236155
237−### JSC Integration
156+1. Create three classes if there's a public constructor:
157+ - `class Foo : public JSC::JSDestructibleObject` (if has C++ fields)
158+ - `class FooPrototype : public JSC::JSNonFinalObject`
159+ - `class FooConstructor : public JSC::InternalFunction`
160+2. Define properties using HashTableValue arrays
161+3. Add iso subspaces for classes with C++ fields
162+4. Cache structures in `ZigGlobalObject`
238163
239−- Use `localToJSValue()` to convert V8 handles to JSC values
240−- Use `JSC::WriteBarrier` for heap-allocated references
241−- Implement `visitChildren()` for custom heap objects
164+### Code Generation
242165
243−### Tagged Pointers
166+Code generation happens automatically as part of the build process. The main scripts are:
244167
245−- Small integers (±2^31) are stored directly as Smis
246−- Objects use pointer tagging with map pointers
247−- Doubles are stored in object layouts with special maps
168+- `src/codegen/generate-classes.ts` - Generates Rust & C++ bindings from `*.classes.ts` files
169+- `src/codegen/generate-jssink.ts` - Generates stream-related classes
170+- `src/codegen/bundle-modules.ts` - Bundles built-in modules like `node:fs`
171+- `src/codegen/bundle-functions.ts` - Bundles global functions like `ReadableStream`
248172
249−## Testing Strategy
173+In development, bundled JS modules can be reloaded without rebuilding native code by running `bun run build`.
250174
251−### Comprehensive Testing
175+## JavaScript Modules (`src/js/`)
252176
253−The V8 test suite compares output between Node.js and Bun for the same C++ code:
177+Built-in JavaScript modules use special syntax and are organized as:
254178
255−1. **Install Phase**: Sets up identical module builds for Node.js and Bun
256−2. **Build Phase**: Compiles native modules using node-gyp
257−3. **Test Phase**: Runs identical C++ functions and compares output
179+- `node/` - Node.js compatibility modules (`node:fs`, `node:path`, etc.)
180+- `bun/` - Bun-specific modules (`bun:ffi`, `bun:sqlite`, etc.)
181+- `thirdparty/` - NPM modules we replace (like `ws`)
182+- `internal/` - Internal modules not exposed to users
183+- `builtins/` - Core JavaScript builtins (streams, console, etc.)
258184
259−### Test Categories
185+## Landing PRs: What Bun Reviewers Catch
260186
261−- **Primitives**: undefined, null, booleans, numbers, strings
262−- **Objects**: creation, property access, internal fields
263−- **Arrays**: creation, length, iteration, element access
264−- **Functions**: callbacks, templates, argument handling
265−- **Memory**: handle scopes, garbage collection, external data
266−- **Advanced**: templates, inheritance, error handling
187+The code review rules — what blocks merges, distilled from ~2,500 merged PRs — live in `REVIEW.md`. Read it before writing code that makes a non-obvious choice.
267188
268−### Adding New Tests
189+Several situational sections live in `.claude/docs/landing-prs.md` — read the relevant one before the work it covers: **Node/Web compat** (touching `node:*` modules, Web APIs, or `src/runtime/node/`), **API design** (adding or changing user-facing API surface), **Performance** (optimizing, touching hot paths, or making perf claims), **Cross-platform** (platform-gated code, FFI/ABI, or platform-sensitive tests), **Dependencies & vendoring** (bumping deps or touching `vendor/`), **Docs, types, and comments** (docs, `.d.ts`, JSDoc), and **PR process** (opening or responding to a PR).
269190
270−1. Add C++ test function to `test/v8/v8-module/main.cpp`
271−2. Register function in the module exports
272−3. Add test case to `test/v8/v8.test.ts` using `checkSameOutput()`
273−4. Run with: `bun bd test test/v8/v8.test.ts -t "your test name"`
191+## Important Development Notes
274192
275−## Debugging Tips
193+1. **Never use `bun test` or `bun <file>` directly** - always use `bun bd test` or `bun bd <command>`. `bun bd` compiles & runs the debug build.
194+2. **All changes must be tested** - if you're not testing your changes, you're not done.
195+3. **Get your tests to pass**. If you didn't run the tests, your code does not work.
196+4. **Follow existing code style** - check neighboring files for patterns
197+5. **Create tests in the right folder** in `test/` and the test must end in `.test.ts` or `.test.tsx`
198+6. **Use absolute paths** - Always use absolute paths in file operations
199+7. **Avoid shell commands** - Don't use `find` or `grep` in tests; use Bun's Glob and built-in tools
200+8. **Memory management** - Prefer RAII (`Drop`) over manual cleanup. Arena edge case: values allocated in an arena (`Arena<T>`/`bumpalo`) do **not** run `Drop` on arena reset — types owning a heap allocation or refcount must be freed/deref'd explicitly first, mirroring the original Zig `deinit()` order.
201+9. **Cross-platform** - Run `bun run rust:check-all` to compile across all targets (linux/macos/windows × x64/aarch64) when making platform-specific changes. `#[cfg(...)]`-gated code is not type-checked unless the matching target is built.
202+10. **Debug builds** - Use `BUN_DEBUG_QUIET_LOGS=1` to disable debug logging, or `BUN_DEBUG_<SCOPE>=1` to enable a specific `bun_core::output` scoped logger
203+11. **Be humble & honest** - NEVER overstate what you got done or what actually works in commits, PRs or in messages to the user.
204+12. **Branch names must start with `claude/`** - This is a requirement for the CI to work.
205+13. **If you need a paragraph-long comment to justify why the workaround is OK, the code is wrong — fix the code.**.
206+14. After every code comment you write, ask yourself, "Is this information the next Claude would spend multiple tool calls trying to understand?". If the answer isn't clearly yes, the code comment is noise - delete it.
276207
277−### Build and Test
208+**ONLY** push up changes after running `bun bd test <file>` and ensuring your tests pass.
278209
279−```bash
280−# Build debug version (takes ~5 minutes)
281−bun bd --help
210+## Debugging CI Failures
282211
283−# Run V8 tests
284−bun bd test test/v8/v8.test.ts
212+Requires the BuildKite CLI (`brew install buildkite/buildkite/bk`) and a read-scoped token in `BUILDKITE_API_TOKEN`. The repo's `.bk.yaml` sets the org/pipeline so `-p bun` is not needed.
285213
286−# Run specific test
287−bun bd test test/v8/v8.test.ts -t "can create small integer"
214+```bash
215+bun run ci:errors # rendered test-failure output for this branch's latest build, [new] vs [also on main]
216+bun run ci:errors '#26173' # or a PR number / URL / branch / build number
217+bun run ci:status # one-screen progress summary (job counts, failed jobs, failing tests so far)
218+bun run ci:logs # save full logs for every failed job to ./tmp/ci-<build>/
219+bun run ci:find # just the build number, e.g. bk job log <job-uuid> -b $(bun run ci:find)
220+bun run ci:watch # watch the current branch's build until it finishes
288221 ```
289222
290−### Common Issues
223+For anything else, use `bk` directly — `bk build list`, `bk api`, `bk artifacts`, etc.
291224
292−**Symbol Not Found**: Ensure mangled names are correctly added to `napi_body.rs` and symbol files.
225+If output from these commands looks wrong (mis-parsed annotation HTML, a field BuildKite changed shape on), fix `scripts/find-build.ts` directly rather than working around it — it's a thin presenter over `bk`.
293226
294−**Segmentation Fault**: Usually indicates inline V8 functions are reading incorrect memory layouts. Check `Map` setup and `ObjectLayout` structure.
227+## Reading PR Feedback
295228
296−**GC Issues**: Objects being freed prematurely. Ensure proper `WriteBarrier` usage and `visitChildren()` implementation.
229+`gh pr view --comments` silently omits review summaries and line-level review comments. For the complete picture — especially when responding to a review — use `bun run pr:comments`, which fetches issue comments, reviews, and line comments in one chronological, labelled listing.
297230
298−**Type Mismatches**: Use `v8_compatibility_assertions.h` macros to verify type layouts match V8 expectations.
231+```bash
232+bun run pr:comments # current branch's PR — resolved threads hidden
233+bun run pr:comments 28838 # by PR number; '#28838' and full URLs also work
234+bun run pr:comments --include-resolved # also show threads already marked resolved
299235
300−### Debug Logging
301−
302−Use `V8_UNIMPLEMENTED()` macro for functions not yet implemented:
303−
304−```cpp
305−void MyClass::NotYetImplemented() {
306− V8_UNIMPLEMENTED();
307−}
236+# Machine-readable output for jq pipelines — one object per entry.
237+# Resolved threads and bot noise (robobun CI status, CodeRabbit summaries) are filtered out.
238+bun run pr:comments --json | jq '.[] | select(.user == "Jarred-Sumner")'
308239 ```
309−
310−## Advanced Topics
311−
312−### Inline Function Compatibility
313−
314−Many V8 functions are inline and compiled into native modules. The memory layout must exactly match what these functions expect:
315−
316−- Objects start with tagged pointer to `Map`
317−- Maps have instance type at offset 12
318−- Handle scopes store tagged pointers
319−- Primitive values at fixed global offsets
320−
321−### Cross-Platform Considerations
322−
323−- Symbol mangling differs between GCC/Clang and MSVC
324−- Handle calling conventions (JSC uses System V on Unix)
325−- Ensure `BUN_EXPORT` visibility on all public functions
326−- Test on all target platforms via CI
327−
328−## Contributing
329−
330−When contributing V8 API implementations:
331−
332−1. **Follow existing patterns** in similar classes
333−2. **Add comprehensive tests** that compare with Node.js
334−3. **Update all symbol files** with correct mangled names
335−4. **Document any special behavior** or limitations
336−
337−For questions about V8 API implementation, refer to the blog series linked above or examine existing implementations in this directory.
338240
