Bun

Bun v1.1.11


Jarred Sumner · June 1, 2024

Bun v1.1.11 is here! This release Fixes 36 bugs (addressing 362 👍). bun update saves dependency updates to package.json. A peer dependencies resolution bug in bun install is fixed. setTimeout gets 5x faster throughput on Linux. Node.js compatibility improvements to fs, zlib, timers, http, dgram and module. Event loop improvements on macOS. Windows reliability improvements.

We're hiring systems engineers in San Francisco to build the future of JavaScript!

Previous releases

  • v1.1.10 fixes 20 bugs. 2x faster uncached bun install on Windows. fetch() uses up to 2.8x less memory. Several bugfixes to bun install, sourcemaps, Windows reliability improvements and Node.js compatibility improvements
  • v1.1.9 fixes 67 bugs (addressing 150 👍). Fixes to: workspaces in bun install, sourcemaps in bun build, IPv6 & VPN connectivity, Loading UNC paths, junctions, symlinks, and pnpm on Windows, fetch() gets faster, added dns.prefetch() API, atob() gets 8x faster, toString('base64url') gets 5x faster, expect().toBeReturned() matcher, Node.js compatibility improvements, Bun Shell fixes, and lots more bugfixes.
  • v1.1.0 Bundows. Windows support is here!

To install Bun

curl
npm
powershell
scoop
brew
docker
curl
curl -fsSL https://bun.sh/install | bash
npm
npm install -g bun
powershell
powershell -c "irm bun.sh/install.ps1|iex"
scoop
scoop install bun
brew
brew tap oven-sh/bun
brew install bun
docker
docker pull oven/bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun

To upgrade Bun

bun upgrade

Fixed: bun update writes to package.json

We fixed a long-standing bug where bun update would update dependencies, but only write the changes to the bun.lockb and not the package.json.

To help prevent breaking changes, bun update preserves the semver ranges when choosing which dependencies to update. If you want to update to the latest version, use bun update --latest.

Thanks to @dylan-conway for implementing this!

Fixed: bun install peer dependencies resolution

A bug that could cause peer dependencies to be hoisted incorrectly has been fixed, thanks to @dylan-conway. This bug would frequently reproduce with the ajv and expo npm packages.

Here's an example of an error this bug could cause:

Module build failed (from ./node_modules/babel-loader/lib/index.js):
Error: Cannot find module 'ajv/dist/compile/codegen'
Require stack:
- repro/node_modules/ajv-keywords/dist/definitions/typeof.js
- repro/node_modules/ajv-keywords/dist/keywords/typeof.js
- repro/node_modules/ajv-keywords/dist/keywords/index.js
- repro/node_modules/ajv-keywords/dist/index.js
- repro/node_modules/schema-utils/dist/validate.js
- repro/node_modules/schema-utils/dist/index.js
- repro/node_modules/babel-loader/lib/index.js
- repro/node_modules/loader-runner/lib/loadLoader.js
- repro/node_modules/loader-runner/lib/LoaderRunner.js
- repro/node_modules/webpack/lib/NormalModuleFactory.js
- repro/node_modules/webpack/lib/Compiler.js
- repro/node_modules/webpack/lib/webpack.js
- repro/node_modules/webpack/lib/index.js
- repro/node_modules/webpack-cli/lib/webpack-cli.js
- repro/node_modules/webpack-cli/lib/bootstrap.js
- repro/node_modules/webpack-cli/bin/cli.js
- repro/node_modules/webpack/bin/webpack.js
    at Module._resolveFilename (node:internal/modules/cjs/loader:1144:15)
    at Module._load (node:internal/modules/cjs/loader:985:27)
    at Module.require (node:internal/modules/cjs/loader:1235:19)
    at require (node:internal/modules/helpers:176:18)
    at Object.<anonymous> (repro/node_modules/ajv-keywords/dist/definitions/typeof.js:3:19)
    at Module._compile (node:internal/modules/cjs/loader:1376:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1435:10)
    at Module.load (node:internal/modules/cjs/loader:1207:32)
    at Module._load (node:internal/modules/cjs/loader:1023:12)
    at Module.require (node:internal/modules/cjs/loader:1235:19)

We've added integrations tests in Bun's repository to prevent this type of bug from occurring again.

A bug in bun install led to workspaces being relinked every time, even when they didn't need to be.

Now:

bun install
bun install v1.1.11

Checked 9 installs across 10 packages (no changes) [5.00ms]
bun install
bun install v1.1.11

Checked 9 installs across 10 packages (no changes) [5.00ms]

Previously:

bun-1.1.10 install
bun install v1.1.10

- work2@workspace:packages/work2
- work3@workspace:packages/work3

2 packages installed [4.00ms]
bun-1.1.0 install
bun install v1.1.10

- work2@workspace:packages/work2
- work3@workspace:packages/work3

2 packages installed [4.00ms]

Thanks to @dylan-conway for implementing this!

New: 5x faster setTimeout throughput

We've re-implemented setTimeout, setImmediate, and setInterval to improve performance, fix bugs and address Node.js compatibility issues.

Running setTimeout 2,000,000 times on Linux x64

Every call to setTimeout, setInterval, or setImmediate has a cost. In this release, we've reduced the cost of setTimeout by 5x on Linux.

DeltaRuntimeExecution timePeak RSS
-Bun v1.1.11648 ms484 MB
5xBun v1.1.103454 ms710 MB
1.8xNode.js 221182 ms487 MB

View benchmark

Fixed: setTimeout this value is now a Timeout object

In Bun, this was previously undefined. In Node.js, this in a setTimeout callback is a Timeout object. This was a bug, and we've fixed it.

setTimeout(function () {
  console.log(this); // Timeout
}, 1000);

As before, you can continue to pass clearTimeout either the Timeout object or the timeout ID.

const timer = setTimeout(() => {}, 1000);

// Timeout implements Symbol.toPrimitive, which lets you use it as a number:
const myTimerID = timer[Symbol.toPrimitive]();
const myTimerID2 = timer + 0;

clearTimeout(timer);
clearTimeout(myTimerID);
clearTimeout(myTimerID2);

This fix also applies to setInterval and setImmediate.

Fixed: setInterval rescheduling behavior matches Node.js

In Node.js, setInterval timers are rescheduled based on when the timer callback was called.

That means if you have a slow synchronous callback, the next interval will be scheduled based on when the callback started, not when it finished.

let last = performance.now();
setInterval(function () {
  const now = performance.now();
  console.log("It's been", (now - last) | 0, "ms since the last interval");
  for (let i = 0; i < 1e9; i++) {}
  this.refresh();
  last = performance.now();
}, 500);

In Bun v1.1.11 and Node.js v22, this code prints something like:

❯ bun set.js # Bun v1.1.11 & Node.js v22
It's been 502 ms since the last interval
It's been 0 ms since the last interval
It's been 231 ms since the last interval
It's been 239 ms since the last interval
It's been 247 ms since the last interval
It's been 244 ms since the last interval
It's been 220 ms since the last interval
It's been 220 ms since the last interval

Previously, Bun would print:

❯ bun set.js # Bun v1.1.10 and earlier
It's been 502 ms since the last interval
It's been 501 ms since the last interval
It's been 499 ms since the last interval
It's been 501 ms since the last interval
It's been 501 ms since the last interval
It's been 501 ms since the last interval
It's been 501 ms since the last interval
It's been 501 ms since the last interval

Fixed: setTimeout shouldn't await promises

Previously, Bun would await promises returned from setTimeout and setInterval.

This meant the following code would hang:

setTimeout(async () => {
  return new Promise();
}, 1000);

Bun no longer awaits promises returned from setTimeout and setInterval.

Thanks to @zawodskoj for fixing this!

Node.js compatibility improvements

This release includes several Node.js compatibility improvements.

Fixed: fs.promises.cp should create directories on Linux

On Linux, fs.promises.cp now creates directories if they don't exist. This matches Node.js behavior, and already worked as expected on Windows and macOS.

Thanks to @nektro for fixing this!

Fixed: node:zlib brotli parameters

A bug in node:zlib prevented setting the params argument in zlib.brotliCompress and zlib.brotliDecompress. This has been fixed, thanks to @nektro.

New: addAbortListener and getMaxListeners in node:evenets

The addAbortListener and getMaxListeners methods have been implemented in node:events (for EventEmitter, but not for EventTarget yet).

Fixed: path.toNamespacedPath with backslash edgecase

An edgecase in path.toNamespacedPath has been fixed, thanks to @dylan-conway and @huseyinacacak-janea. This bug also ocurred in Node.js.

The following assertions now pass:

assert.strictEqual(path.win32.toNamespacedPath("\\\\?\\foo\\"), "\\\\?\\foo\\");
assert.strictEqual(path.win32.toNamespacedPath("\\\\?\\foo"), "\\\\?\\foo\\");
assert.strictEqual(
  path.win32.toNamespacedPath("\\\\?\\c:\\Windows/System"),
  "\\\\?\\c:\\Windows\\System",
);

Fixed: node:http server max body size

Node.js doesn't set a max request body size by default. Bun sets a default max request body size, and a bug caused it to also be applied to the node:http server's max request body size. This has been fixed, thanks to @JonnyBurger.

Fixed: Uncaught exceptions now fail in bun test

When an uncaught exception occurs between test cases, Bun makes it more clear that it happened between test cases.

Breaking change: The exit code of bun test is now 1 when an uncaught exception or unhandled rejection occurs between test cases. This is what it should always have been, and what you'd expect from a test runner.

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

test("uncaught rejection", async () => {
  setTimeout(() => {
    throw new Error("Uh-oh!");
  }, 100);
  await Bun.sleep(99);
});

Now this outputs:

bun test a.test.ts
bun test v1.1.11

a.test.ts:
✓ uncaught rejection [100.05ms]

# Unhandled error between tests
-------------------------------
1 | import { test, expect } from "bun:test";
2 |
3 | test("uncaught rejection", async () => {
4 |   setTimeout(() => {
5 |     throw new Error("Uh-oh!");
              ^
error: Uh-oh!
      at a.test.ts:5:11
-------------------------------

 1 pass
 0 fail
 1 error
Ran 1 tests across 1 files. [106.00ms]

Previously, this output was less clear. It didn't clearly show the error occurred between test cases and it did not show a count of how many rejection errors occurred.

bun-1.1.10 test a.test.ts
bun test v1.1.10 (5102a944)

a.test.ts:
✓ uncaught rejection [100.32ms]
1 | import { test, expect } from "bun:test";
2 |
3 | test("uncaught rejection", async () => {
4 |   setTimeout(() => {
5 |     throw new Error("Uh-oh!");
              ^
error: Uh-oh!
      at a.test.ts:5:11

 1 pass
 0 fail
Ran 1 tests across 1 files. [111.00ms]

Fixed: Custom function name in stack traces

When you set the name property of a function dynamically, Bun now uses that name in stack traces.

const fn = function OriginalName() {
  console.trace();
};
Object.defineProperty(fn, "name", { value: "NewName" });
fn();

In Bun v1.1.11, this code prints:

at NewName (abc.js:2:3)
at abc.js:5:1

Previously, Bun would print:

at OriginalName (abc.js:2:3)
at abc.js:5:1

This regressed in Bun v0.6.12 and has been fixed, thanks to @refi64. We've added a test to prevent this from happening again.

Fixed: Sourcemaps with code splitting

When code splitting is enabled in bun build, a bug could cause minified sourcemaps to be invalid in some cases. This has been fixed, thanks to @paperdave.

macOS reliability improvements

macOS, Linux, and Windows each use different system calls to wait for I/O-related events. On macOS, a bug in our event loop code could cause Bun to wait extra time for I/O events to occur. Previously, we assumed that a kqueue event would only be either readable or writable:

int events = LIBUS_SOCKET_READABLE;
if (kev.flags & EVFILT_WRITE) {
  events = LIBUS_SOCKET_WRITABLE;
}

This was incorrect. A kqueue event can be both readable and writable. We've fixed this bug.

Fixed: Hang in UDP sockets

Reading from UDP sockets in Bun could hang on macOS x64 and Windows x64. This was caused by an issue in our event loop code. We've fixed this bug.

Windows reliability improvements

Fixed: Bun.build CPU usage

A bug causing transpiler threads in Bun.build on Windows to spin loop infinitely using CPU has been fixed, thanks to @paperdave.

Fixed: External imports with backslashes

On Windows, building the following code with --external would fail:

bun build --external="*" ./index.js
import { loadFonts } from "../base";
console.log(loadFonts);

Thanks to @paperdave for fixing this!

Fixed: Various DNS issues on Windows

We've updated the c-ares asynchronous DNS resolution library which fixes several bugs on Windows that make it more consistent with Linux and macOS.

Fuzz testing

Fuzz testing parts of Bun's runtime APIs led to several bugfixes in this release.

Fixed: Crash when calling Bun.CryptoHasher without new

A mistake in Bun's JavasScriptCore bindings generator led to a crash when calling Bun.CryptoHasher without new. This has been fixed. We've added a test to ensure this doesn't happen again.

Fixed: Crash on Linux when reading a Bun.file() errors

A crash that could occur when reading a Bun.file() asynchronously errors on Linux has been fixed. This was reported several times and reproduced by a new test.

Fixed: Crash in Bun.$.escape() without arguments

A crash has been fixed when calling Bun.$.escape() without arguments. This was reproduced by a new test.

Fixed: Crash in bun:ffi read fn without arguments

When calling read.u32() in bun:ffi without arguments, a crash could occur. This has been fixed.

Fixed: Crash in Node.js Module.prototype._compile

When no arguments or invalid arguments were passed to Module.prototype._compile, a crash could occur when an exception was thrown. This has been fixed.

Fixed: Crash in CryptoHasher.byteLength

A crash could occur when calling Bun.CryptoHasher.byteLength with certain hash algorithms. This has been fixed, thanks to @nektro.

Thanks to 16 contributors!