Bun v1.0.26

Jarred Sumner ยท February 3, 2024

Bun is an incredibly fast JavaScript runtime, bundler, transpiler, and package manager โ€” all in one. In case you missed it, here are some of the recent changes to Bun.

This release fixes 30 bugs, adds support for multi-statement queries in bun:sqlite, makes bun --watch more reliable in longer-running sessions, Bun.FileSystemRouter now supports more than 64 routes, fixes a bug with expect().toStrictEqual(), fixes 2 bugs with error.stack, fixes a bug with fileURLToPath, improves Node.js compatibility

Previous releases

  • v1.0.25 fixes 4 bugs, adds vm.createScript. Fixes a crash in fs.readFile, a crash in Bun.file().text(), a crash in IPC, and a transpiler bug involving loose equals
  • v1.0.24 fixes 9 bugs and adds Bun Shell, a fast cross-platform shell with seamless JavaScript interop. Fixes a socket timeout bug, a potential crash when socket closes, a Node.js compatibility issue with Hapi, a process.exit bug, and bun install binlinking bug, bun inspect regression, and bun:test expect().toContain bug
  • v1.0.23 fixes 40 bugs (addressing 194 ๐Ÿ‘ reactions). import & embed sqlite databases in Bun, Resource Management ('using' TC39 stage3) support, bundler improvements when building for Node.js, bugfix for peer dependency resolution, semver bugfix, 4% faster TCP on linux, Node.js compatibility improvements and more"

To install Bun:

curl -fsSL https://bun.sh/install | bash
npm install -g 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

Multi-statement queries in bun:sqlite

bun:sqlite gets support for multi-statement queries. This allows you to run multiple SQL statements in a single call to db.run() delimited by a ;, similar to how it works in the sqlite command-line tool.

import { Database } from "bun:sqlite";

const db = new Database(":memory:");

  CREATE TABLE users (
    name TEXT

  INSERT INTO users (name) VALUES ("Alice");
  INSERT INTO users (name) VALUES ("Bob");

const names = db
  .query("SELECT name FROM users")
  .map(({ name }) => name)
  .join(", ");
console.log(names); // Alice, Bob

Only Database.prototype.run and ``Database.prototype.execsupport multi-statement queries.db.queryanddb.prepare` do not.

bun --watch reliability improvement

bun --watch now defensively closes all file descriptors on reload. Filesystem watchers inherently need to keep many file descriptors open when watching many files, but keeping file descriptors open for a long time can lead to resource exhaustion and many other issues. Bun already set O_CLOEXEC internally, but that doesn't always mean all file descriptors closed.

Part of what makes bun --watch fast is we immediately reload the entire process when an imported file changes. There's very little work that happens between the file change and the process reload. This is in contrast to other tools that might recompile the file to check for changes, and only re-run the process on change.

On Linux, Bun now uses the close_range(2) system call to close all file descriptors > 8 before reloading the process. This reliably ensures that all file descriptors are closed (for those of you running modern Linux kernels).

On macOS, we use the POSIX_SPAWN_CLOEXEC_DEFAULT flag to close all file descriptors on spawn. We also fixed an issue where signals were not being reset to their default behavior on reload.

abort-controller polyfill now uses native AbortController

The popular abort-controller npm package polyfill now uses the native AbortController in Bun instead of the polyfill.

This fixes an issue where Bun would report an error like "Expected signal to be an AbortSignal" when a library used the abort-controller polyfill with fetch or Request or Response.

The abort-controller polyfill is a polyfill for the AbortController and AbortSignal web APIs

Fixed: Bun.FileSystemRouter now supports more than 64 routes

Bun.FileSystemRouter is Bun's built-in Next.js pages-inspired filesystem router. It now supports more than 64 routes.

Previously, Bun would throw an uncatchable exception when you tried to use more than 64 routes. This was caused by an assertion failure in JavascriptCore when an object is defined with more than 63 "inline" properties. This has been fixed.

Fixed: error.stack sometimes undefined

Bun implements the V8 Stack Trace API (despite Bun using the JavaScriptCore engine), which adds methods like Error.prepareStackTrace and Error.captureStackTrace to the Error object.

There was a bug where error.stack was sometimes undefined when it should have been a string. This has been fixed.

This bug impacted Firebase & Google Cloud libraries when they were about to log an error. It caused these libraries to throw a different error while trying to log the error, which was not helpful.

Fixed: error.stack CallSite lineNumber was sometimes negative

The following would sometimes print negative numbers:

Error.prepareStackTrace = (error, stack) => {
  return [stack[0].getLineNumber(), stack[0].getColumnNumber()];

const error = new Error();

Bun returned negative numbers when the line number was not available. This unfortunately broke the popular source-map-support library since it asserts that line and column numbers are positive integers. This has been "fixed" by never returning negative numbers.

Fixed: Error.prepareStackTrace is now a function by default

In Node.js, Error.prepareStackTrace is a function by default. In Bun, it was undefined by default. This has been fixed.

The default Error.prepareStackTrace function does what you would expect: it returns a string of the stack trace. You can continue to set Error.prepareStackTrace to a custom function to customize the stack trace, or to undefined (in which case the default function is used).

Fixed: module.path is now __dirname instead of module.id

In Node.js, the module object has a path property which is supposed to be the directory of the module, equivalent to __dirname. In Bun, module.path was incorrectly an alias for module.id. This has been fixed.

Fixed: expect(a).toStrictEqual(b) with deleted properties

A bug in bun:test's expect.toStrictEqual implementation caused it to behave incorrectly when comparing objects with deleted properties. This has been fixed, thanks to @dylan-conway.

import { test, expect } from "bun:test";

test("expect.toStrictEqual", () => {
  const a = { a: 1, b: 2 };
  const b = { a: 1, b: 2 };
  delete b.b;

Previously, this would throw an error with a confusingly empty diff:

2 |
3 | test("expect.toStrictEqual", () => {
4 |   var obj1 = { a: 1 };
5 |   var obj2 = {};
6 |   delete obj1.a;
7 |   expect(obj1).toStrictEqual(obj2);
error: expect(received).toStrictEqual(expected)


- Expected  - 0
+ Received  + 0

      at hey.test.ts:7:3
โœ— expect.toStrictEqual [0.29ms]

Now it correctly passes.

This happened because toStrictEqual used a fast path that failed to handle deleted properties correctly in some cases.

Fixed: event loop scheduling bug in Bun.serve() websockets

We've made some infrastructure tweaks to ensure that microtasks are always drained after event loop callbacks are executed. There was a bug where we skipped doing this in websockets, in Bun.spawn(), and in IPC handlers. Neglecting to drain the microtask queue can cause very high memory growth until the microtasks get drained (imagine if you just kept adding to a queue without ever removing anything).

Fixed: new Response(Bun.file()) sometimes logged an error to stderr

When you passed a Bun.file() to new Response() to Bun.serve() connected to HTTP (not HTTPS), if the client aborted the request at the right time, Bun would log Error: NOTCONN to stderr with no way to suppress it. This was confusing and unhelpful. Aborting a request is a normal part of the web, and Bun should not log an unsupressable error when it happens.

Windows is happening on February 15th

78% of Bun's tests pass on Windows, but it's still not enough to release.

Most of the changes in this release were Windows-related, but this changelog excludes that since its not "released" yet.

Thanks to 28 contributors!