Bun

Bun v1.1.26


Jarred Sumner · August 24, 2024

Bun v1.1.26 is here! This release fixes 9 bugs (addressing 652 👍). bun oudated shows outdated dependencies. Bun.serve() gets a new idleTimeout option for socket timeouts, and subscriberCount(topic: string) for a count of websocket clients subscribed to a topic. bun test.only skips all other tests. expect.assertions(n) in async functionn is fixed. "use strict" in CommonJS modules is now preserved. Plus, more node.js compatibility improvements and bug fixes.

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

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

bun outdated shows outdated dependencies

bun outdated is a new subcommand that shows you which dependencies are outdated.

bun outdated

The Update column displays what the next semver-matching version is. The Latest column displays the latest tagged version from the npm registry.

This was one of the top most upvoted issues on Bun's GitHub repository. Huge thanks to @dylan-conway for implementing this feature!

test.only skips all other tests

Previously, the following would run the first two tests, and skip the third test. This was very confusing! Why did we do it this way!?

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

test("i should not run!", () => {
  // previously: runs
  // now: does not run
  console.log("why am i running?");
});

// ... more tests in a much longer file ...

test.only("i should run!", () => {
  // runs
  console.log("yes only i should run");
});

test("I definitely should not run!", () => {
  // does not run
  console.log("i don't run");
});

This was really frustrating because the only way around it was the --only CLI flag.

bun test --only

Now, you don't have to do that. bun test will detect there was a .only test in the file and skip all the tests that don't have .only in them.

The existence of a .only test in a file is also reset when the next module starts loading tests. This means that a .only will only impact the current file instead of all files.

subscriberCount(topic: string) in Bun.serve()

Bun.serve()'s builtin-in WebSocket server now supports subscriberCount(topic: string) to get the number of websocket clients subscribed to a topic.

import { serve } from "bun";

const server = serve({
  port: 3002,
  websocket: {
    open(ws) {
      const count = server.subscriberCount("chat");
      ws.subscribe("chat");
      ws.publish(`chat`, `🐰 #${count} joined the chat`);
    },
  },

  fetch(req, server) {
    return server.upgrade(req);
  },
});

idleTimeout in Bun.serve()

You can now set a custom idle timeout (in seconds) for Bun.serve()'s builtin-in HTTP(s) server.

import { serve } from "bun";

const server = serve({
  async fetch(req) {
    await Bun.sleep(1000 * 60 * 2); // 2 minutes
    return new Response("Hello, it's been 2 minutes!");
  },
  // minutes:
  idleTimeout: 60 * 4, // 4 minutes
});

Previously, this was always set to 10 seconds. The default continues to be 10 seconds, but in cases where you need that to be a longer or shorter amount of time, you can now set it to a different value.

This option was previously only available for connected WebSocket clients, and now it's available for regular HTTP(s) requests as well.

Thanks to @cirospaciari for implementing this feature!

Fixed: expect.assertions(n) in async functions

expect.assertions(n: number) now works as expected in async functions.

The following test that should've failed previously would not have failed.

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

test("this test should NOT pass", async () => {
  expect.assertions(1);
  await Bun.sleep(2);

  // the assertion is commented out.
  // so this test should always fail.
  // expect(1).toBe(1);
});

Now, the test will fail:

bun test
bun test v1.1.26

passy.test.ts:
AssertionError: expected 1 assertions, but test ended with 0 assertions
✗ this test should NOT pass [3.57ms]

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

expect().toThrow() supports asymmetric matchers

You can now use expect().toThrow() with asymmetric matchers like expect.objectContaining():

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

test("this test should NOT pass", () => {
  expect(() => {
    const error = new Error("oh no!");
    error.code = "ERR_OH_NO";
    throw error;
  }).toThrow(expect.objectContaining({ message: "oh no!", code: "ERR_OH_NO" }));
});

Fixed: done callback in tests causing a hang

Previously, there were a number of cases where using a done callback in test would cause the test to hang indefinitely when not called.

For example, this test would fail and then hang forever:

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

test("this test should NOT hang", (done) => {
  process.nextTick(() => {
    throw new Error("oh no!");
    done();
  });
});

One could argue, "The done callback was never called! It's only logical that it would hang forever!" but that's not the case. The test already failed. It had an uncaught exception. Therefore, the test does not need to hang forever (or timeout). It can fail immediately once all microtasks have been drained and the task queue is empty.

In other words, tests will continue running once all other known work is complete instead of waiting for the done callback to be called after an exception is thrown.

Fixed: mock().mockName() not returning this

The mock() function in Bun's test runner now returns this. This is to align the behavior with jest so that existing tests written for jest can be ported to Bun.

Node.js compatibility improvements

Fixed: Top-level "use strict" in CommonJS modules preserved at runtime

Previously, Bun would strip a top-level "use strict" directive from CommonJS modules at runtime. This was intended for ES modules as it is unnecessary in those cases, but it IS necessary for CommonJS modules and is now fixed.

Preivously, the following code would print "I am globalThis" in Node.js, and "I am undefined" in Bun.

// Before:
"use strict";

function whoami() {
  return this;
}

const me = whoami();

if (me === globalThis) {
  console.log("I am globalThis!");
} else {
  console.log("I am undefined!");
}

exports.whoami = whoami;

Now, it prints "I am globalThis" in both Node.js and Bun.

Fixed: util.inherits bug

A bug where util.inherits was using Object.create instead of Object.setPrototypeOf has been fixed. This caused the snowflake sdk to fail to load.

Thanks to @dylan-conway for fixing this bug!

Fixed: crypto.randomValues() crash in certain cases

A rare JIT-related crash in crypto.randomValues() has been fixed.

Fixed: crash in Buffer in certain cases

A rare crash in Buffer found while running the Node.js test suite has been fixed.

Fixed: ERR_INVALID_THIS code returned in certain web APIs

Node.js' test suite expects certain web APIs to return an error with the ERR_INVALID_THIS code property.

For compatibility, Bun now returns these errors with the ERR_INVALID_THIS code.

Fixed: dns.resolveSrv confusing weight and priority

A bug where the priority field was being set to the weight field in dns.resolveSrv has been fixed, thanks to @cirospaciari.

Fixed: rare crash in fetch()

A rare crash in fetch() involving AbortSignal has been fixed, thanks to @cirospaciari.

Thanks to 3 contributors!