This release fixes 74 bugs (addressing 36 π). node:crypto gets faster & more compatible. timeout
option in Bun.spawn. Support for module.children
in node:module
. Connect to PostgreSQL via unix sockets with Bun.SQL
. Dev Server stability improvements. vm.compileFunction. Initial support for node:test. Faster Express & Fastify.
To install Bun
curl -fsSL https://bun.sh/install | bash
npm install -g bun
powershell -c "irm bun.sh/install.ps1|iex"
scoop install bun
brew tap oven-sh/bun
brew install bun
docker pull oven/bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun
To upgrade Bun
bun upgrade
Faster Express & Fastify
node:http compatibility improvements led to performance improvements for Express and Fastify.
In the next version of Bun
β Bun (@bunjavascript) March 25, 2025
node:http compatibility improvements make Express 9% faster and Fastify 5.4% faster pic.twitter.com/ML8DKqmjaL
Faster, more compatible node:crypto
In the previous release we rewrote the implementation of crypto.Sign
, crypto.Verify
, crypto.Hash
, and crypto.Hmac
from JavaScript to native code using BoringSSL.
In Bun v1.2.6, we've continued this work by rewriting Cipheriv
, Decipheriv
, DiffieHellman
, DiffieHellmanGroup
, ECDH
, randomFill(Sync)
, and randomBytes
with their tests from Node.js passing. Most notable performance improvements are seen in DiffieHellman
, Cipheriv/Decipheriv
, and scrypt
.
bun crypto-benchmark.mjs
cpu: Apple M1 Max
runtime: bun 1.2.6 (arm64-darwin)
benchmark time (avg) (min β¦ max) p75 p99 p995
----------------------------------------------------------------------------- -----------------------------
createDiffieHellman - 512 150.61 ms/iter (23.13 ms β¦ 339.16 ms) 201.08 ms 339.16 ms 339.16 ms
Cipheriv and Decipheriv - aes-256-gcm 3.54 Β΅s/iter (3.43 Β΅s β¦ 3.98 Β΅s) 3.62 Β΅s 3.98 Β΅s 3.98 Β΅s
scrypt - N=16384, p=1, r=1 47.37 ms/iter (46.69 ms β¦ 48.12 ms) 47.59 ms 48.12 ms 48.12 ms
bun-1.2.5 crypto-benchmark.mjs
cpu: Apple M1 Max
runtime: bun 1.2.5 (arm64-darwin)
benchmark time (avg) (min β¦ max) p75 p99 p995
----------------------------------------------------------------------------- -----------------------------
createDiffieHellman - 512 105.15 s/iter (23.29 s β¦ 319.92 s) 128.47 s 319.92 s 319.92 s
Cipheriv and Decipheriv - aes-256-gcm 1.15 ms/iter (1.04 ms β¦ 3.72 ms) 1.14 ms 3.3 ms 3.65 ms
scrypt - N=16384, p=1, r=1 365.4 ms/iter (362.31 ms β¦ 368.73 ms) 366.56 ms 368.73 ms 368.73 ms
crypto-benchmark.mjs
Thanks to @dylan-conway!
hkdf
support
Bun v1.2.6 now implements hkdf
(HMAC-based Extract-and-Expand Key Derivation Function) and hkdfSync
from node:crypto
. These functions allow you to derive keys of a specific length from an algorithm, input key, salt, and optional info.
import crypto from "node:crypto";
const derivedKey = crypto.hkdfSync(
"sha256",
"secret-key",
"salt",
"info", // optional info
32, // length of output key
);
crypto.hkdf("sha256", "secret-key", "salt", "info", 32, (err, derivedKey) => {
// console.log(derivedKey);
});
Thanks to @dylan-conway for implementing this!
prime functions support
Bun now implements the generatePrime
, generatePrimeSync
, checkPrime
, and checkPrimeSync
functions from the node:crypto
module, allowing you to generate and verify prime numbers.
import crypto from "node:crypto";
// Generate a 512-bit prime number synchronously
const prime = crypto.generatePrimeSync(512);
const isPrime = crypto.checkPrimeSync(prime); // true
crypto.generatePrime(2048, (err, prime) => {
// `prime` is a 2048-bit prime number
});
crypto.checkPrime(prime, (err, result) => {
// `result` is true
});
Thanks to @dylan-conway!
Fixed: ED25519 crypto key generation from private keys
Fixed a memory error in the Crypto API when generating ED25519 public keys from private keys. The previous implementation incorrectly read out of bounds from the private key, which could lead to unpredictable behavior or crashes.
// Creating an ED25519 key pair and exporting the public key now works correctly
import { generateKeyPair } from "crypto";
const { publicKey, privateKey } = await generateKeyPair("ed25519");
const publicKeyObject = privateKey.export({ type: "spki", format: "pem" });
// This operation is now safer and more reliable
Thanks to @DonIsaac for finding this issue!
Initial support for node:test
Bun now includes initial support for the node:test
module, leveraging bun:test
under the hood to provide a unified testing experience. This implementation allows you to run Node.js tests with the same performance benefits of Bun's native test runner.
// Use the Node.js test API in your Bun projects
import test from "node:test";
test("basic functionality", (t) => {
// Your test code here
});
While most basic tests should work, some advanced features are not yet implemented:
- Tests within tests (subtests)
- Mocks
- Snapshots
- Timers
- Custom reporters
- Programmatic API
Thanks to @Electroid for the contribution!
Dev Server stability improvements
This release fixes several bugs in Bun's dev server, including:
- Issues with Hot Module Replacement
- Windows path handling
- Filesystem watcher
Thanks to @paperclover for the contribution!
timeout
in Bun.spawn
The timeout
option in Bun.spawn
is now supported.
const result = Bun.spawnSync({
cmd: ["sleep", "1000"],
timeout: 1000, // Will terminate after 1 second
});
console.log(result.exitedDueToTimeout); // true
You can still pass an AbortSignal
to the spawn
function, but people more frequently guess the timeout
option instead of using AbortSignal.timeout
.
Thanks to @pfgithub!
Connect to PostgreSQL via unix sockets with Bun.SQL
Bun's SQL API now supports connecting to PostgreSQL via Unix sockets, offering improved performance for local database connections.
import { SQL } from "bun";
await using sql = new SQL({
// Connect via unix socket
path: "/tmp/.s.PGSQL.5432", // Full path to socket
// or just specify the directory
// path: "/tmp", // Bun will append /.s.PGSQL.{port}
user: "postgres",
password: "postgres",
database: "mydb"
});
const result = await sql`SELECT * FROM users`;
Thanks to @cirospaciari for implementing this!
Support for module.children
in node:module
The module.children
array tracks child modules that are loaded by a parent module. This is now supported in Bun.
// The children will be tracked automatically
const childModule = require("./child-module");
console.log(module.children); // [Module { ... }]
Previously, the array was always empty.
Thanks to @paperclover for implementing this!
Bun.SQL query parameter options
Fixed an issue with PostgreSQL connection options, allowing you to properly set runtime configurations like search_path
either through the connection URL or via the connection
object.
// Use via connection string query parameters
await using db = new SQL(
"postgres://user:pass@localhost:5432/mydb?search_path=information_schema",
{ max: 1 }
);
// Or use via connection options object
await using db = new SQL("postgres://user:pass@localhost:5432/mydb", {
connection: {
search_path: "information_schema"
},
max: 1
});
// Query tables from the specified schema
const count = await db`SELECT COUNT(*)::INT FROM columns LIMIT 1`.value();
Thanks to @cirospaciari for the contribution!
Improved: Database.deserialize
in bun:sqlite
bun:sqlite
now allows passing configuration options to Database.deserialize()
, including support for strict mode and other database settings. This enhancement provides more control when working with serialized SQLite databases.
// Before: Limited configuration
const db = Database.deserialize(serializedData);
// Now: Configure database settings during deserialization
const db = Database.deserialize(serializedData, {
readonly: true,
strict: true,
safeIntegers: true,
});
Thanks to @ubirdio for the contribution!
Improved: TLS certificate handling
Improved compatibility with TLS certificate handling, adding support for the translatePeerCertificate
function which converts certificate data into a more idiomatic JavaScript representation.
// Now you can use translatePeerCertificate from the _tls_common module
const { translatePeerCertificate } = require("_tls_common");
// Convert raw certificate data to a more developer-friendly format
const formattedCert = translatePeerCertificate(rawCertificate);
This change also adds proper error handling for TLS protocol version issues with the new error types ERR_TLS_INVALID_PROTOCOL_VERSION
and ERR_TLS_PROTOCOL_VERSION_CONFLICT
.
Thanks to @nektro!
Improved: ReferenceError message
The error message for ReferenceError has been updated in Bun's implementation of the node:vm module to match Node.js and V8 behavior. Reference errors now use "X is not defined" instead of "Can't find variable: X".
foo.bar = 5;
// ReferenceError: Can't find variable: foo
// ReferenceError: foo is not defined
Thanks to @zackradisic!
HTTP2 Node.js Compatibility Improvements
Significant improvements to Bun's node:http2
implementation. This update fixes several issues including proper stream management, response handling, and window size configuration.
Thanks to @cirospaciari for the contribution!
Implemented vm.compileFunction
from node:vm
Bun now supports Node.js vm.compileFunction
API, allowing developers to compile JavaScript code into a function with specific arguments and context.
// Compile a function in a specific context
const vm = require("node:vm");
const context = vm.createContext({ x: 42 });
const fn = vm.compileFunction("return x + y", ["y"], {
contextExtension: context,
});
// Call the compiled function
console.log(fn(8)); // 50
Thanks to @zackradisic for the contribution!
1.4 MB smaller binary size
On top of all these fixes, Bun gets 1.4 MB smaller in this release.
β― ls -lSh ~/Build/bun-v1.2.5/bun-darwin-aarch64/bun
... 56M ...
β― ls -lSh ~/Build/bun-v1.2.6/bun-darwin-aarch64/bun
... 55M ...
Bugfixes
Fixed: Cached UDP socket address reset after connection
The UDP Socket implementation now correctly resets the cached address
property after connecting. This ensures that the socket's address information reflects the current connection state, fixing an issue in both Bun.udpSocket
and the node:dgram
module.
// Before
import { createSocket } from "node:dgram";
const socket = createSocket("udp4");
await new Promise((resolve) => {
socket.bind(resolve);
});
socket.connect(60865, "127.0.0.1", () => {
console.log(socket.address().address);
// Before: 0.0.0.0
// Bun 1.2.6: 127.0.0.1
});
Thanks to @heimskr for fixing this!
Fixed: "svelte"
export conditions in bun-plugin-svelte
bun-plugin-svelte
now properly handles the "svelte"
export condition when resolving packages. This ensures better compatibility with Svelte ecosystem packages like @threlte/core that use this export condition.
// Now works with packages that use the "svelte" export condition
import { Canvas } from "@threlte/core";
Thanks to @jarred for the contribution!
Fixed: warning emitted in setTimeout
regression
Fixed an issue where calling setTimeout
without specifying a delay parameter would incorrectly emit a TimeoutNaNWarning
. Now, only explicitly passing NaN
as the delay will trigger the warning.
// Does not emit a warning
setTimeout(() => {});
// This still emits a warning (as expected)
setTimeout(() => {}, NaN);
Thanks to @Electroid for the contribution!
Fixed: trailing slashes in Bun.s3 presign
Fixed an issue in Bun.s3
presign where trailing slashes in paths weren't removed, potentially causing incorrect URL generation when using Bun's S3 client.
Thanks to @nikeee for fixing this!
Fix for :global
selector in CSS modules
Fixed an issue where the :global()
selector in CSS modules wasn't being processed correctly. The :global()
selector is now properly handled, preserving global class names without applying CSS module scoping.
// Before this fix, global selectors wouldn't work correctly
// index.module.css
:global(.button) {
color: blue;
}
// Now properly outputs as
.button {
color: blue;
}
Thanks to @DonIsaac!
Fixed: global catch-all routes with callback handlers in Bun.serve()
Fixed an issue where global catch-all routes using a callback handler function weren't working properly, while static response objects ('/*': new Response()
) worked as expected. Now both approaches work correctly.
// Now works properly
serve({
routes: {
"/*": () => new Response("Global catch-all"),
},
});
Thanks to @pfgithub for fixing this!
Fixed: node:
prefix consistency
Bun now maintains the node:
prefix in module resolution, correctly handling Node.js built-in modules and providing more accurate error messages when resolving modules.
// Previously
import fs from "node:fs";
// Would be resolved as just "fs"
// Now
import fs from "node:fs";
// Correctly maintains "node:" prefix
// When an unknown built-in module is requested
import nonexistent from "node:nonexistent";
// Throws ERR_UNKNOWN_BUILTIN_MODULE: "No such built-in module: node:nonexistent"
This improves compatibility with Node.js module resolution and provides clearer error messages for missing built-in modules.
Fixed: module.id
for entrypoints
The module.id
property now correctly reports .
for the entry point module, matching Node.js behavior.
// When this file is executed as the main script
console.log(module.id); // "."
// When imported as a module from elsewhere
console.log(module.id); // "/path/to/file.js"
This change increases compatibility with Node.js module handling.
Fixed: N-API string conversion
Fixed several bugs in napi_get_value_string_*
and napi_create_string_*
functions to better handle edge cases during string conversions between JavaScript and native code. These improvements provide more reliable behavior when working with different string encodings in native addons.
// This should now handle edge cases more reliably
const addon = require("./build/Release/my_addon.node");
const result = addon.convertString("Hello, δΈη");
Thanks to @190n for the contribution!
Fixed: svelte module imports in bun-plugin-svelte
The Svelte plugin for Bun now correctly handles Svelte module imports with proper TypeScript transpilation. This fixes issues with module compilation where errors like "unknown option 'css'" were being thrown. The plugin now uses separate options when compiling Svelte modules versus Svelte components, and transpiles .svelte.ts
modules with Bun.Transpiler
before calling compileModule
.
// Now you can properly import .svelte.ts modules
import { myHelper } from "./helper.svelte.ts";
// And use them in your Svelte components
<script>
import {myHelper} from './helper.svelte.ts'; const result = myHelper();
</script>;
Thanks to @DonIsaac for the contribution!
Fixed: memory leak edgecase in Bun.spawn with piped output
Fixed a memory leak that occurred when using Bun.spawn
with stdout or stderr set to "pipe"
. Previously, when output pipes completed reading from file descriptors, the associated resources weren't properly cleaned up, resulting in memory leaks.
// Before: Memory would leak when spawning many processes
for (let i = 0; i < 1000; i++) {
const proc = Bun.spawn({
cmd: ["echo", "hello world"],
stdout: "pipe",
});
await proc.exited;
}
// Now: Memory is properly released when processes complete
Thanks to @DonIsaac for the contribution!
Fixed: edgecase in CSS parser number formatting
Fixed handling of infinity values in CSS to ensure proper rendering in Safari. Specifically when bundling Tailwind v4, rounded-full
class now correctly outputs border-radius: 3.40282e38px
instead of 1e999px
, matching Tailwind's official approach for representing infinity values in CSS.
// Before - caused rendering issues in Safari
// .rounded-full { border-radius: 1e999px; }
// After - properly renders in all browsers
// .rounded-full { border-radius: 3.40282e38px; }
Thanks to @zackradisic for the contribution!
Fixed: shell crash under high process load
Fixed an issue where spawning many processes very quickly using Bun's shell API ($
) could cause crashes. The fix properly handles cases where processes exit immediately after being spawned, ensuring proper resource cleanup and exit notification handling.
// This would previously crash after spawning many processes
import { $, which } from "bun";
const cat = which("cat");
// Spawn hundreds of processes in quick succession
const promises = [];
for (let i = 0; i < 100; i++) {
promises.push($`${cat} some_file.txt`.text());
}
await Promise.all(promises);