CtrlK
BlogDocsLog inGet started
Tessl Logo

pleaseai/bun

All-in-one JavaScript/TypeScript toolkit: fast runtime, package manager, test runner, and bundler. Version-aware skill backed by the ask CLI.

75

Quality

94%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Advisory

Suggest reviewing before use

Overview
Quality
Evals
Security
Files

node-compat.mdskills/use-bun/references/

Node.js Compatibility

Bun targets drop-in compatibility with Node.js. Most production Node apps run on Bun with no changes. This reference summarises what's supported, what's gappy, and how to verify before relying on a specific API.

Engine

Bun uses JavaScriptCore (Apple's engine, used by Safari), not V8. This means:

  • Faster startup in most cases (4× faster on Linux for hello-world).
  • Different perf characteristics for tight numeric loops, GC patterns, and JIT warmup. Benchmarks that work in V8 may behave differently.
  • No V8-specific globals: %OptimizeFunctionOnNextCall, %DebugPrint, --allow-natives-syntax flags — these do not exist.
  • No v8 module APIs that depend on V8 internals: v8.serialize/deserialize is implemented, but heap snapshot formats differ.

For most application code this is invisible. For libraries that use --allow-natives-syntax (rare), it matters.

Built-in module status

ModuleStatus
node:fs, node:fs/promisesNear-complete; some edge-case flags differ
node:pathComplete
node:osComplete
node:cryptoHigh coverage; some legacy ciphers differ
node:http, node:https, node:http2Implemented; prefer Bun.serve for new code
node:net, node:tls, node:dgramImplemented
node:stream, node:buffer, node:eventsComplete
node:url, node:querystringComplete
node:zlibComplete
node:child_processImplemented; Bun.spawn is more ergonomic
node:clusterPartial — IPC limited
node:worker_threadsImplemented
node:assertComplete
node:utilHigh coverage
node:vmPartial — most APIs work; some isolation edge cases differ
node:perf_hooksImplemented
node:async_hooksImplemented (AsyncLocalStorage works)
node:dnsImplemented
node:testImplemented (alongside bun:test)
node:wasiPartial
node:inspectorPartial

For the authoritative list, read at the installed version:

BUN_REF="bun-v$(${CLAUDE_SKILL_DIR}/scripts/resolve-bun-version.sh)"
BUN_SRC=$(ask src "github:oven-sh/bun@${BUN_REF}")
cat "${BUN_SRC}/docs/runtime/nodejs-compat.mdx"

Globals and process

APINotes
process.env, process.argv, process.platform, process.cwd()Work identically
process.versions.bunBun-specific; use to detect runtime
process.versions.nodeReturns a Node.js version string for compat (currently emulates a recent LTS)
process.binding(…)Limited — many internal bindings unavailable
process.dlopenWorks for N-API addons (.node files)
__dirname, __filenameWork in CJS; in ESM use import.meta.dir / import.meta.file (or fileURLToPath(import.meta.url))
requireWorks for CJS; also works inside ESM files (Bun-specific convenience)
BufferGlobal; full Node compat
globalAliased to globalThis

Detecting the runtime

if (typeof Bun !== "undefined") {
  // Bun-only code path
} else if (process.versions.node) {
  // Node-only code path
}

Or via process.versions.bun:

const isBun = !!(process as any).versions.bun;

ESM and CJS interop

Bun supports:

  • CJS importing ESM: via require() — works (unlike Node, which restricts this).
  • ESM importing CJS: via import — default import gets module.exports, named imports work for static analysis.
  • Top-level await in ESM only. CommonJS modules do not support top-level await (Bun matches Node here — top-level await is a language-level feature of ESM).
  • require() from ESM files — Bun-specific convenience; portable code should not rely on this.

TypeScript without compilation

Bun runs .ts and .tsx natively. tsconfig.json is read for path mapping and JSX configuration. tsc is still needed for type-checking and emitting .d.ts — Bun does not run the type checker at runtime.

Recommended tsconfig.json excerpt for Bun-only projects:

{
  "compilerOptions": {
    "lib": ["ESNext"],
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "types": ["bun"],
    "allowJs": true,
    "noEmit": true,
    "strict": true,
    "skipLibCheck": true
  }
}

Install Bun types: bun add -d @types/bun (provides Bun.*, bun:* ambient declarations).

Common compatibility issues

  • Native modules requiring node-gyp — Bun supports N-API; modules using V8 internals (rare) will not load. Most popular native modules (sharp, better-sqlite3, bcrypt) work.
  • process.binding consumers — internal-binding-based libraries (e.g. some ancient ones) may break. These are usually deprecated upstream too.
  • Worker differencesnode:worker_threads works, but transferList semantics for some types differ subtly. Verify with a smoke test.
  • fetch is global in both — but Bun uses its own implementation; behaviour for streaming bodies and HTTP/2 push may differ. Same is true for Node 18+.
  • Some node:crypto ciphers — algorithms that depend on OpenSSL provider configs may behave differently. Check the error message and verify with openssl directly.

Verifying a specific API

BUN_REF="bun-v$(${CLAUDE_SKILL_DIR}/scripts/resolve-bun-version.sh)"
BUN_SRC=$(ask src "github:oven-sh/bun@${BUN_REF}")

# Find Bun's implementation of a Node module
rg -l "function createServer" "${BUN_SRC}/src/js/node"

# Read the test cases — these prove what is supported
ls "${BUN_SRC}/test/js/node/"
rg "test\(" "${BUN_SRC}/test/js/node/http/" | head

If a Node test file exists for the area you care about, the API is at least intended to work and has regression coverage.

README.md

tile.json