CodeVetter
← All unpacks
CodeVetter · Repo Unpack evidence

oven-sh/bun

Scanned commit bf80d21c69 dated 2026-09-22 — last upstream commit. Generated by codevetter 1.12.0 on 2026-09-22.

github.com/oven-sh/bun ↗ · 3996 files · 37.9 MB · scan capped

Bun Documentation   •   Discord   •   Issues   •   Roadmap Read the docs → What is Bun? Bun is an all-in-one toolkit for JavaScript and TypeScript apps.

Deep analysis by claude-opus-5 via the claude CLI on 2026-09-22 (449s) — every claim below links to its cited source files.

Overview

Bun is an all-in-one JavaScript/TypeScript runtime, bundler, transpiler, test runner, and npm-compatible package manager built on WebKit's JavaScriptCore and shipped as a single native binary. The codebase at this commit is a ~200-crate Rust Cargo workspace (`src/`) linked against a large C++ JSC bindings layer (`src/jsc/bindings/`, 407 files), ~15 vendored native dependencies, and a TypeScript layer (`src/js/`) that is bundled at build time into the binary as built-in modules like `node:fs` and `bun:ffi`. Everything routes through one process entry (`src/runtime/bin_entry/mod.rs::main`) into a single subcommand dispatch table (`src/runtime/cli/mod.rs`) covering 34 command tags — `run`, `install`, `test`, `build`, `pm`, `x`, `publish`, `audit`, and more. What's distinctive is the obsessive cold-start engineering (`#[cold]` on every non-`bun run` subcommand body, symbol ordering, mimalloc wired into ICU/libuv/BoringSSL before anything allocates) and a review culture encoded as machine-checked policy: `clippy.toml` bans `std::fs`, `mordant.toml` ratchets Rust lints against a baseline, and `REVIEW.md` distills ~2,500 merged PRs into merge-blocking rules.

System map

One native executable with a Rust core, a C++ JSC binding layer, and a build-time-bundled TypeScript module layer. `src/runtime/bin_entry/mod.rs` is the C-ABI `main`; it initializes the crash handler, allocator, stdio, and stack limits, then hands off to `cli::Cli::start()` which decodes a `Tag` and dispatches to one of 34 subcommand bodies. Storage is not a database — it's the lockfile (`bun.lock` text / `bun.lockb` binary under `src/install/lockfile/`), the global package cache, and the standalone-executable graph appended to the binary itself. Builds go through `scripts/build.ts` → CMake/ninja + `cargo` per-crate rlibs; CI is a generated Buildkite pipeline (`.buildkite/ci.ts`) plus 25 GitHub Actions workflows. Deployment shape is a downloadable binary plus npm packages, Docker images (`dockerhub/`), and a VS Code extension (`packages/bun-vscode`).

All 14 system map claims →

Feature catalog

User-facing surface splits four ways: the CLI subcommand table, the `Bun` global namespace, the Node.js compatibility layer, and Web APIs. The CLI covers running scripts, package management (install/add/update/remove/link/patch/publish/audit/why/dedupe/prune/outdated), bundling (`bun build`, including `--compile` standalone executables), testing (`bun test`), a REPL, `bunx`, `bun init`/`bun create`, and self-upgrade. The `Bun` global is defined by a JSC hash table in `BunObject.cpp` exposing ~100 properties — `Bun.serve`, `Bun.file`, `Bun.write`, `Bun.spawn`, `Bun.build`, `Bun.sql`, `Bun.redis`, `Bun.s3`, `Bun.secrets`, `Bun.CSRF`, `Bun.$` shell, FFI, and hashing/compression helpers. Node compatibility is implemented per-module across `src/js/node/*.ts` and `src/runtime/node/*.rs`. Bake (`src/runtime/bake/`) adds a hot-reloading dev server and server-components framework toolkit on top of the bundler and `Bun.serve`.

All 15 feature catalog claims →

Data flow

Three main pipelines dominate. (1) Source → runtime: the JSC module loader asks `Bun__transpile*` hooks in `jsc_hooks.rs`, which resolve through `bun_resolver`, parse with `bun_js_parser`, print with `bun_js_printer`, and hand a `ResolvedSource` back to JSC; results are cached in a `RuntimeTranspilerStore` and an arena that is reset between graphs. (2) Package manifests → lockfile → disk: `install_with_manager` loads the lockfile, resolves dependencies via the registry client and task queue, runs a security scan, extracts tarballs, links binaries, then runs lifecycle scripts and saves the lockfile. (3) Bundling: `BundleV2` owns a `Graph` of parse tasks fanned out to worker threads, then `LinkerContext` performs tree-shaking, chunking, and sourcemap emission. Cross-thread movement is explicit: `ConcurrentTask`/`DeferredTaskQueue` marshal work onto the JS thread, and the HTTP client runs on its own `HTTPThread`.

All 14 data flow claims →

Behavior traces

Three flows walked end to end. Startup (`bun foo.ts`): C runtime → `main` → allocator/crash/stdio init → `Cli::start` → `Command::start` → fast-path detection of a run entrypoint → `init` → `RunCommand::exec_with_cfg` → `boot` → `VirtualMachine::init_with_module_graph` → `Run::start` → `load_entry_point` → event-loop drain → `on_before_exit`. Install: `Tag::InstallCommand` → `InstallCommand::exec` → `PackageManager::init` → `install_with_manager` → lockfile load → resolution → task run → scanner → installer → lifecycle scripts → `save_lockfile`. Serve/dev: `Bun.serve` constructs a monomorphized `NewServer`, uWS delivers a request into `RequestContext`, and under Bake the DevServer checks the Host header, resolves a route through `FrameworkRouter`, incrementally bundles, and pushes HMR events over a WebSocket.

All 14 behavior traces claims →

Testing signals

Testing is dominated by 2,149 JS/TS test files run through Bun's own Jest-compatible runner, orchestrated by a Node.js script (`scripts/runner.node.ts`, 3,504 lines) that deliberately avoids Bun APIs and imports nothing, spawning each file in a separate process so crashes are attributable. Coverage is heaviest in `test/js` (1,307 files) and `test/regression` (426 issue-numbered files); `test/bundler` uses a dedicated `itBundled` harness, `test/bake` covers HMR, and `test/internal` tests the build scripts themselves. Rust has thin unit coverage — 76 files contain `#[test]` out of ~700 Rust source files. CI shards the suite 20 ways on Linux with ASAN options set, and non-binary lanes (types, source lints) are excluded from Buildkite and run in GitHub Actions instead.

All 15 testing signals claims →

Risk map

The dominant risk class is memory safety across a Rust/C++/JSC boundary where `unsafe` is routine, raw pointers are stored across event-loop turns, and GC interaction is manual — `REVIEW.md` names this the most-blocked review category. Security-sensitive paths include registry credential handling, lifecycle scripts and the out-of-process security scanner, runtime C compilation via TinyCC, `Bun.secrets` keychain access, S3 SigV4 signing, CSRF token HMACs, the dev server's Host-header check, and the self-upgrade download path. Fragile coupling shows up where a value must stay in sync with an out-of-repo consumer (crash-report tag chars and `bun.report`) or with generated code. Blast-radius hotspots are the very large single files: `bundle_v2.rs` (8,079 lines), `VirtualMachine.rs` (7,700), `DevServer.rs` (6,792), `RequestContext.rs` (4,794), `server/mod.rs` (4,372).

All 16 risk map claims →

Extension points

Extension happens at five layers. New CLI subcommands add a `Tag` variant plus a dispatch arm and an out-lined `#[cold]` body. New JS classes are declared in `*.classes.ts` and generated into paired Rust/C++ bindings, or hand-written as the three-class JSC pattern. The `Bun` global is a static hash table in `BunObject.cpp` — adding a property is a table row plus a callback. Userland extends the bundler and runtime through `Bun.plugin` (regex-filter groups of onResolve/onLoad callbacks) and native plugins via `packages/bun-native-plugin-rs`/`-api`. Frameworks extend Bake through `FrameworkRouter` and the `UserOptions` framework description. The install path is extensible through a user-supplied security scanner module and bunfig-declared registries and scopes.

All 14 extension points claims →

Contributor handoff

Read `CLAUDE.md`, `REVIEW.md`, `src/AGENTS.md`, and `src/js/AGENTS.md` before writing anything — they encode rules that are enforced, not suggestions. The single most important operational fact: never run `bun test` or `bun <file>` directly; use `bun bd` (which builds the debug binary and execs it with trailing args) so your native changes are actually exercised. Prefer in-tree helpers over `std` — `clippy.toml` mechanically bans `std::fs::*`, `std::env::var`, `std::thread::spawn`, and `std::mem::zeroed`. Add tests to the existing file for the module you changed, not a new file, and reserve `test/regression/issue/<n>.test.ts` for real regressions with a real issue number. Branch names must start with `claude/`. The one documented exception to the build requirement is `packages/bun-types/**/*.d.ts`, which can be tested with the system Bun.

All 15 contributor handoff claims →

Languages

LanguageFilesShare of source
JavaScript787054%
TypeScript348824%
Markdown2307docs/config
Rust153610%
C9006%
JSON868docs/config
C++6074%
TOML1441%

Start reading here

Probable source entrypoint: Cargo.toml — inferred from the directory tree and manifest, not the upstream docs.

Source roots

src/ (1353 files) packages/ (312 files)

Repository structure

Top-level pathFiles
src/1353
test/1352
bench/374
docs/358
packages/312
scripts/80
.github/45
patches/30

Docs found: AGENTS.mdCLAUDE.mdCONTRIBUTING.mdLICENSE.mdREADME.mddocs/README.md

Package

Cargo.toml — v0.0.0, 0 declared dependencies.

CI & config entrypoints

Structural health signals

Average score 5.3/10 across 96 analyzed source files; 89 hotspots surfaced.

These are deterministic review leads — size, churn, and structural signals — not proof of a bug in this project.

Recent history