Bun

Bun v0.6.13


Ashcon Partovi · July 4, 2023

We're hiring C/C++ and Zig engineers to build the future of JavaScript! Join our team →

As a reminder: Bun is an incredibly fast JavaScript runtime, bundler, transpiler, and package manager — all in one.

We've been releasing a lot of changes to Bun recently, here's a recap in case you missed it:

  • v0.6.0 - Introducing bun build, Bun's new JavaScript bundler.
  • v0.6.2 - Performance boosts: 20% faster JSON.parse, up to 2x faster Proxy and arguments.
  • v0.6.3 - Implemented node:vm, lots of fixes to node:http and node:tls.
  • v0.6.4 - Implemented require.cache, process.env.TZ, and 80% faster bun test.
  • v0.6.5 - Native support for CommonJS modules (previously, Bun did CJS to ESM transpilation),
  • v0.6.6 - bun test improvements, including Github Actions support, test.only(), test.if(), describe.skip(), and 15+ more expect() matchers; also streaming file uploads using fetch().
  • v0.6.7 - Node.js compatibility improvements to unblock Discord.js, Prisma, and Puppeteer
  • v0.6.8 - Introduced Bun.password, mocking in bun test, and toMatchObject()
  • v0.6.9 - Less memory usage and support for non-ascii filenames
  • v0.6.10 - fs.watch(), bun install bug fixes, bun test features, and improved CommonJS support
  • v0.6.11 - Addressed a release build issue from v0.6.10.
  • v0.6.12 - Sourcemap support in Error.stack, Bun.file().exists(), and Node.js bug fixes.

To install Bun:

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

Mock Date in bun test

You can now mock the value of new Date() in Bun's test runner. This will affect any API that can retreive the system time, including: Date.now(), new Date(), and Intl.DateTimeFormat().format().

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

test("time travel", () => {
  const past = new Date("2022-07-05");
  setSystemTime(past);

  expect(new Date()).toBe(past);
  expect(Date.now()).toBe(past.getTime());
  expect(new Intl.DateTimeFormat("en-US").format()).toBe("07/05/2022");

  setSystemTime(); // resets
  expect(Date.now()).toBeGreaterThan(past.getTime());
});

Unlike in Jest, the Date object doesn't mutate when you mock the time. This prevents a whole class of confusing test-only bugs.

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

test("Date.prototype", () => {
  const OriginalDate = Date;
  setSystemTime(new Date("2022-07-05"));

  // both pass in Bun, but fail in Jest
  expect(OriginalDate).toBe(Date);
  expect(OriginalDate.now).toBe(Date.now);
});

10x faster Buffer.toString("base64")

Improvements to node:tls and node:net

@cirospaciari made lots of improvements to Bun's implementation of node:tls and node:net. This unlocks various npm packages that were dependent on these changes.

Postgres with TLS works

You can now use postgres package to connect to a Postgres database using TLS. Previously, you could connect without TLS, but there were some outstanding issues that prevented TLS from working.

import { postgres } from "postgres";

const sql = postgres(process.env.DATABASE_URI);
const name = "ThePrimeagen",
  age = 69;
const users = await sql`
  select
    name,
    age
  from users
  where
    name like ${name + "%"}
    and age > ${age}
`;
console.log(users); // [{ name: "ThePrimeagen", age: 69 }]

Nodemailer works

You can now use the nodemailer package to send emails.

import nodemailer from "nodemailer";

nodemailer.createTestAccount(async (err, account) => {
  const transporter = nodemailer.createTransport({
    host: account.smtp.host,
    port: account.smtp.port,
    secure: account.smtp.secure,
    debug: true,
    auth: {
      user: account.user,
      pass: account.pass,
    },
  });

  let info = await transporter.sendMail({
    from: '"Fred Foo 👻" <foo@example.com>',
    to: "example@gmail.com",
    subject: "Hello ✔",
    text: "Hello world?",
    html: "<b>Hello world?</b>",
  });

  console.log("Message sent: %s", info.messageId);
  // Message sent: <b658f8ca-6296-ccf4-8306-87d57a0b4321@example.com>

  console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info));
  // Preview URL: https://ethereal.email/message/WaQKMgKddxQDoou...
});

.isUtf8() and .isAscii()

You can now use isUtf8() and isAscii() from Node's buffer module.

import { test, expect } from "bun:test";
import { isUtf8, isAscii } from "node:buffer";

test("isUtf8 and isAscii", () => {
  expect(isUtf8(new Buffer("What did the 🦊 say?"))).toBeTrue();
  expect(isAscii(new Buffer("What did the 🦊 say?"))).toBeFalse();
});

Slightly less memory for HTTP requests

Bun now uses 8 less bytes per HTTP request thanks to Zig's packed struct. This allows us to store the same information in a more memory efficient layout.

In the example below, each bool is stored as 1 byte.

const Request = struct {
  is_transfer_encoding: bool,
  is_waiting_body: bool,
  // ...
  aborted: bool,
};

Now, with a packed struct, each bool is stored as 1 bit.

const Request = struct {
  const Flags = packed struct {
    is_transfer_encoding: bool,
    is_waiting_body: bool,
    // ...
    aborted: bool,
  };
  flags: Flags,
};

WebSocket client reliability improvements

This release fixes a number of bugs in Bun's WebSocket client implementation.

Zero-length messages are supported

Did you know that you can send & receive zero-length messages with WebSockets? Previously, Bun would ignore those, but now they're supported.

const ws = new WebSocket("wss://ws.postman-echo.com/raw");
ws.onopen = () => {
  ws.send("");
  ws.send(new ArrayBuffer(0));
};

ws.onmessage = (event) => {
  console.log(event.data); // ""
  console.log(event.data); // ArrayBuffer(0)
};

Initial payload race condition fix

Previously, when the initial HTTP response was received and contained websocket message(s) were sent in the same event loop tick, Bun would sometimes ignore the messages. This is now fixed.

Similarly, there was an edgecase where the initial message could be dispatched after a subsequent mesage was received (the 2nd message would appear before the 1st message). This has also been fixed.

Bug fixes

Changed .toEqual() to now ignore private properties (prefixed with #).

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

test("toEqual() with #property", () => {
  class Foo {
    bar = 1;
    #baz = "";
  }
  class Bar {
    bar = 1;
  }
  expect(new Foo()).toEqual(new Bar());
});

Fixed crash with workspace: dependency in package.json.

Previously, if you ran bun add with the following package.json, Bun would crash. This is now fixed.

package.json
{
  "dependencies": {
    "foo": "workspace:bar"
  }
}

Fixed zero-length environment variables not working.

Previously, if you ran bun run with the following .env file, Bun would not detect the environment variable. This is now fixed.

.env
FOO=

Fixed wrong text decoding from HTMLRewriter.

Previously, if you can the following code in Bun, it would output the wrong result. This is now fixed.

new HTMLRewriter()
  .on("p", {
    element(element) {
      console.log(element.getAttribute("id"));
    },
  })
  .transform(new Response('<p id="Šžõäöü"></p>'));

// Before fix: Šžõäöü
// After fix: Šžõäöü

Fixed overloads for Buffer.toString().

Previously, if you used toString() on a Node.js Buffer, Bun would not properly handle the 2nd overload. The 1st overload specifies a start and end, whereas the 2nd overload specified an offset and length instead of end.

interface Buffer {
  toString(encoding?: BufferEncoding, start?: number, end?: number): string;
  toString(offset: number, length: number, encoding?: BufferEncoding): string;
}
const { Buffer } = require("node:buffer");
const buf = Buffer.from("hello world", "latin1");

console.log(buf.toString("latin1", 1, 2));
console.log(buf.toString(1, 2, "latin1"));

Thanks to @Hanaasagi for fixing!

Fixed missing export from node:http module.

We were missing two exports. Oops!

var defaultObject = {
   globalAgent,
+  ClientRequest,
+  OutgoingMessage,
   [Symbol.for("CommonJS")]: 0,
};

Thanks to @stijnvanhulle for fixing!

Other bug fixes

  • Fixed utf-8 decoding for some Node-APIs.

  • Fixed file descriptors leaking when a new JavaScript file is loaded.

Changelog

See the changelog on GitHub