Bun 1.2 — npm Compatibility and Built-in Cloud APIs

Bun 1.2 shifts Node.js compatibility from reactive bug-fixing to running the official test suite, while adding native S3, Postgres, and a text-based lockfile.

Bun 1.2 — npm Compatibility and Built-in Cloud APIs

Ninety percent compatibility feels close to done. It is not. The remaining ten percent is where production workloads live — the packages that use undocumented Node.js internals, the UDP telemetry libraries, the C++ addons that bypass N-API entirely. Chasing those bugs one GitHub issue at a time is a losing strategy.

Source

Bun 1.2

By: Ashcon Partovi
Published: Jan 22 2025
Type: Release notes

Saved because the shift from reactive patching to running the Node.js test suite is an architectural decision about how compatibility is maintained, not just a status update on which modules pass.

From Bug Reports to Running the Official Test Suite

The core change in how Bun approaches Node.js compatibility is methodological. Previously, bugs were fixed when users reported them from real packages. That produced real fixes but discouraged the large refactors needed for systematic coverage. Bun 1.2 changed this by porting thousands of files from the Node.js test/parallel directory and running them against every commit.

The porting work is non-trivial. Many Node.js tests rely on internal bindings — internalBinding("cares_wrap"), internalBinding("uv") — that have no public equivalent. The team replaced them with stubs or adapted tests to use public APIs. Error message matching presented another category: Node.js tests often assert exact error strings. Bun sometimes provides more context ("It must be >= 0 and <= 5" instead of "It must be >= 0") which technically fails the assertion. The solution was shifting assertions to check name and code instead of message — which is also the recommended Node.js practice.

This approach produced coverage across modules that were previously partial. node:http2 server support arrived and runs 2x faster than Node.js 22. node:zlib was rewritten from JavaScript to native code, fixing missing Brotli support and doubling inflate performance. node:dgram enables UDP socket bindings used by packages like DataDog’s dd-trace. node:cluster supports the multi-process HTTP server pattern common in Node.js production deployments. Express serves requests 3x faster than Node.js as a direct result of node:http compatibility improvements stacking with Bun’s HTTP server.

The V8 C++ API work is the most architecturally unusual piece. Some npm packages use V8’s internal C++ API directly — not N-API, but V8 itself. Since Bun uses JavaScriptCore, those packages crashed at runtime. The fix was implementing V8’s public C++ API as a shim on top of JavaScriptCore — an approach the team documented in a three-part blog series. Packages like cpu-features that previously produced dyld: missing symbol called and SIGABRT now work. Coverage is not complete; node-canvas@v2 and node-sqlite3 still have gaps. But the architecture is in place for continued improvement.

The lockfile change from bun.lockb to bun.lock is more consequential than it appears. Binary lockfiles are fast but opaque: pull request diffs are unreadable, and merge conflicts are effectively unresolvable without deleting the file and re-running install. bun.lock is JSONC — JSON with comments and trailing commas — which makes diffs reviewable and conflicts less likely. Existing projects keep bun.lockb with no forced migration. New projects get bun.lock by default. Install speed did not regress; bun install is 30% faster in 1.2 despite the format change.

Bun.sql and Bun.s3 follow the same design principle: native implementations of things that were previously JavaScript packages with avoidable overhead. Bun.sql adds a Postgres client with automatic prepared statements, query pipelining, binary wire protocol, and connection pooling — 50% faster than popular JavaScript clients for row reads. The API mirrors postgres.js closely enough that migration is nearly a mechanical import swap. Bun.s3 adds S3-compatible object storage that is 5x faster than @aws-sdk/client-s3 for downloads and integrates with Bun.file() so that s3:// paths work alongside file:// paths in the same code. When a Response wraps an S3 file, Bun redirects to a presigned URL rather than proxying the bytes through the server — saving both memory and bandwidth at no extra cost.

Two behavioral changes matter for existing codebases. Bun.build() now rejects on failure instead of resolving with errors in the logs array — which is the correct behavior but breaks any build pipeline that checked result.logs rather than catching the rejection. bun run now sets the working directory to the package.json’s parent rather than the shell’s current directory, matching npm and yarn. Both changes fix surprising behavior, and both will break assumptions that were already fragile.

What I want to remember

Remember: Switching from reactive bug-fixing to running the official test suite is the difference between maintaining a compatibility claim and being able to verify it — the lockfile format, native cloud APIs, and V8 shim all follow from the same engineering culture of owning the surface area completely.