Bun Update: Enhanced Web APIs, Testing Tools, and Core Fixes
Bun's latest update introduces the `URLPattern` Web API for URL matching, adds fake timers to `bun:test`, and enables custom proxy headers for `fetch()`, along with numerous performance and stability bug fixes.
This update for Bun brings a host of new features, improvements, and critical bug fixes, enhancing developer experience across various domains.
Installing Bun
To install Bun, choose your preferred method:
curl:
curl -fsSL https://bun.sh/install | bash
npm:
npm install -g bun
PowerShell:
powershell -c "irm bun.sh/install.ps1|iex"
Scoop (Windows):
scoop install bun
Homebrew (macOS/Linux):
brew tap oven-sh/bun
brew install bun
Docker:
docker pull oven/bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun
Upgrading Bun
To upgrade your existing Bun installation:
bun upgrade
URLPattern Web API
Bun now supports the URLPattern Web API, offering declarative pattern matching for URLs, much like regular expressions for strings. This feature is particularly valuable for routing in web servers and frameworks.
Key features include:
- Constructor: Create patterns from strings or
URLPatternInitdictionaries. test(): Check if a URL matches the pattern (returns a boolean).exec(): Extract matched groups from a URL (returnsURLPatternResultornull).- Pattern properties: Access
protocol,username,password,hostname,port,pathname,search, andhash. hasRegExpGroups: Detect if the pattern uses custom regular expressions.
Examples:
// Match URLs with a user ID parameter
const pattern = new URLPattern({ pathname: "/users/:id" });
pattern.test("https://example.com/users/123"); // true
pattern.test("https://example.com/posts/456"); // false
const result = pattern.exec("https://example.com/users/123");
console.log(result.pathname.groups.id); // "123"
// Wildcard matching
const filesPattern = new URLPattern({ pathname: "/files/*" });
const match = filesPattern.exec("https://example.com/files/image.png");
console.log(match.pathname.groups[0]); // "image.png"
This implementation passes 408 Web Platform Tests.
Fake Timers for bun:test
Bun's test runner now includes support for fake timers, enabling you to control time in your tests without relying on real-time passage. This is crucial for accurately testing code that utilizes setTimeout, setInterval, and other timer-based APIs.
import { test, expect, jest } from "bun:test";
test("fake timers", () => {
jest.useFakeTimers();
let called = false;
setTimeout(() => {
called = true;
}, 1000);
expect(called).toBe(false);
// Advance time by 1 second
jest.advanceTimersByTime(1000);
expect(called).toBe(true);
jest.useRealTimers();
});
Available jest methods:
useFakeTimers(options?): Enable fake timers, with an optional{ now: number | Date }to set the current time.useRealTimers(): Restore real timers.advanceTimersByTime(ms): Advance all timers by the specified milliseconds.advanceTimersToNextTimer(): Advance to the next scheduled timer.runAllTimers(): Run all pending timers.runOnlyPendingTimers(): Run only currently pending timers (excluding those scheduled by the executed timers).getTimerCount(): Get the number of pending timers.clearAllTimers(): Clear all pending timers.isFakeTimers(): Check if fake timers are active.
Custom Proxy Headers in fetch()
The fetch() API's proxy option now accepts an object format, enabling the inclusion of custom headers sent to the proxy server. This is highly beneficial for scenarios requiring proxy authentication tokens, custom routing headers, or other proxy-specific configurations.
// String format still works
fetch(url, { proxy: "http://proxy.example.com:8080" });
// New object format with custom headers
fetch(url, {
proxy: {
url: "http://proxy.example.com:8080",
headers: {
"Proxy-Authorization": "Bearer token",
"X-Custom-Proxy-Header": "value",
},
},
});
These headers are sent in CONNECT requests for HTTPS targets and in direct proxy requests for HTTP targets. If a Proxy-Authorization header is provided, it takes precedence over credentials embedded within the proxy URL.
http.Agent Connection Pool Now Properly Reuses Connections
A critical bug has been resolved where http.Agent with keepAlive: true failed to reuse connections in certain cases. This fix ensures proper connection pooling and improved performance for persistent connections.
import http from "node:http";
const agent = new http.Agent({ keepAlive: true });
http.request(
{
hostname: "example.com",
port: 80,
path: "/",
agent: agent,
},
(res) => {
// Connection is now properly reused on subsequent requests
},
);
This issue was addressed by fixing three independent bugs:
- An incorrect property name (
keepalivevskeepAlive) caused the user's setting to be ignored. Connection: keep-aliverequest headers were not being handled correctly.- Response header parsing used incorrect comparison logic and was case-sensitive, violating RFC 7230.
Standalone Executables No Longer Load Config Files at Runtime
Standalone executables built with bun build --compile will now, by default, skip loading tsconfig.json and package.json from the filesystem at runtime. This change significantly improves startup performance and prevents unexpected behavior if configuration files in the deployment environment differ from those used during compilation.
If your executable requires reading these configuration files at runtime, you can re-enable this behavior using new CLI flags:
# Enable runtime loading of tsconfig.json
bun build --compile --compile-autoload-tsconfig ./app.ts
# Enable runtime loading of package.json
bun build --compile --compile-autoload-package-json ./app.ts
# Enable both
bun build --compile --compile-autoload-tsconfig --compile-autoload-package-json ./app.ts
Alternatively, use the JavaScript API:
await Bun.build({
entrypoints: ["./app.ts"],
compile: {
autoloadTsconfig: true,
autoloadPackageJson: true,
autoloadDotenv: true,
autoloadBunfig: true,
},
});
console.log Now Supports %j Format Specifier
The %j format specifier for console.log and related console methods now outputs the JSON stringified representation of a value, aligning with Node.js behavior. Previously, %j was not recognized and was treated as literal text.
console.log("%j", { foo: "bar" });
// {"foo":"bar"}
console.log("%j %s", { status: "ok" }, "done");
// {"status":"ok"} done
console.log("%j", [1, 2, 3]);
// [1,2,3]
SQLite 3.51.1
bun:sqlite has been updated to SQLite v3.51.1, which includes fixes for the EXISTS-to-JOIN optimization and other query planner improvements.
Bug Fixes
A wide array of bug fixes have been implemented across various Bun components:
bun:test Fixes
spyOnassertion failure: Fixed fuzzer-detected assertion failure whenspyOnwas used with indexed property keys (e.g.,spyOn(arr, 0)).expect.extend()TypeError:expect.extend()now correctly throws aTypeErrorfor objects containing non-function callables (like class constructors).jest.mock()assertion failure: Addressed fuzzer-detected assertion failure whenjest.mock()was called with invalid arguments (e.g., a non-string first argument).
Bundler and Dev Server Fixes
- Dev Server error messages: Fixed "null" error messages in rare Dev Server cases; now falls back to
event.messageifevent.erroris null. - HMR error overlay: The HMR error overlay now correctly displays error information.
- Out of memory errors: Properly handles
Promiserejections in the bundler instead of incorrectly throwing out-of-memory errors. - Standalone executables bytecode cache: Fixed standalone executables (
bun build --compile) failing to load bytecode cache due to improper 8-byte alignment in embedded Mach-O and PE sections.
bun install Fixes
- Security scanner dependencies: The security scanner now correctly collects dependencies from workspace packages, scanning the full dependency tree.
- Lockfile resolution: Fixed an off-by-one error in the lockfile resolution bounds check during
bun installwith update requests. bun publish --helpdescription: Corrected the--dry-rundescription forbun publish --helpto "Perform a dry run without making changes."
Windows Fixes
fs.access()/fs.accessSync(): ResolvedEUNKNOWNerrors when checking Windows named pipes (e.g.,\\.\pipe\my-pipe).- Git dependencies with long paths: Git dependencies on Windows now function correctly with long paths.
- Console codepage: Fixed Windows console codepage not being properly saved and restored, which could cause garbled text on non-English systems when using
bunx.
Node.js Compatibility Improvements
Buffer.prototype.hexSlice()andBuffer.prototype.toString('base64'): Fuzzer-detected issues in these methods now throw proper errors instead of crashing when output exceeds JavaScript's maximum string length.Buffer.prototype.*Writemethods: Correctly handles non-numericoffsetandlengtharguments, treatingNaNoffsets as 0 and clamping lengths to available buffer space, matching Node.js.assert.deepStrictEqual(): Fixed incorrect behavior whereNumberandBooleanwrapper objects with different values were treated as equal.TLSSocket.isSessionReused(): Now correctly uses BoringSSL'sSSL_session_reused()API for accurate session reuse detection, matching Node.js behavior, instead of returningtrueaftersetSession().napi_typeoffor boxed strings: Correctly returnsnapi_objectfor boxedStringobjects (new String("hello")), aligning with JavaScript'stypeofbehavior for all boxed primitives.Http2Server.setTimeout()/Http2SecureServer.setTimeout(): These methods now return the server instance instead ofundefined, allowing for method chaining.- Error stack traces during GC: Fixed crashes when populating error stack traces during garbage collection (e.g., with
node:readlineor unhandled promise rejections).
Bun API Fixes
Bun.secrets: Fixed crashing when called insideAsyncLocalStorage.run()or other async context managers.Bun.mmap: Addressed fuzzer-detected assertion failure; now properly validates and rejects negativeoffsetorsizeoptions with clear error messages.Bun.plugin: Now returns an error instead of potentially crashing for an invalidtargetoption.new Bun.FFI.CString(ptr): Fixed a regression from v1.2.3 that caused a "function is not a constructor" error.- Class constructors without
new: Fuzzer-detected assertion failure resolved; class constructors (likeBun.RedisClient) now properly throwTypeError: Class constructor X cannot be invoked without 'new'. - Empty/used
ReadableStream: Fixed a fuzzer-detected bug that could cause errors to be silently ignored when creating empty or usedReadableStreams. Glob.scan()cwdboundary: FixedGlob.scan()escaping thecwdboundary with patterns like.*/*or.*/**/*.ts, which incorrectly traversed into parent directories.Bun.indexOfLine: Fixed fuzzer-detected issue when called with a non-numberoffsetargument.FormData.from()with largeArrayBuffer: Now throws a proper error when called with very largeArrayBufferinput (>2GB).
bun:ffi Fixes
linkSymbolscrashing: FixedlinkSymbolscrashing when theptrfield was not a valid number orBigInt.- JS numbers to FFI pointers: Corrected incorrect conversion of JavaScript numbers to FFI pointers, which could lead to identical JS number values producing different pointer values and causing crashes.
- Internal bindings overflow: Fixed crashes when using libraries (like
@datadog/pprof) that triggered an overflow in internal bindings.
Security
- Chunk terminators: Improved stricter validation of chunk terminators per RFC 9112.
TypeScript Definitions
Bun.serve()protocolproperty: FixedBun.serve()type definitions to include theprotocolproperty, which was already available at runtime.
Other Fixes
- String length boundary check: Fixed an off-by-one error in string length boundary checks that incorrectly rejected strings with length exactly equal to the maximum allowed length.