Bun

Bun v0.2.2

Jarred Sumner · October 27, 2022

To upgrade:

bun upgrade

To install:

curl https://bun.sh/install | bash

TLDR:

  • TCP/TLS Socket API
  • WebCrypto API
  • fs.createReadStream works now
  • Bun.peek
  • Install Bun with Homebrew
  • Several bug fixes

TCP sockets

Bun now has support for TCP clients and servers. You can use it to implement database clients, game servers, or anything else that needs to use TCP instead of HTTP.

A TCP echo server runs 24% faster in Bun compared to Node:

image

This API is designed to be fast. Instead of promises or assigning callbacks for each socket, like the Node.js EventEmitter or the standard WebSocket API, callbacks are defined once for optimal performance and better code readability.

Start a TCP server

import { listen } from "bun";

listen({
  hostname: "localhost",
  port: 8080,
  socket: {
    open(socket) {},
    data(socket, data) {
      console.log(data instanceof Uint8Array); // true
    },
    drain(socket) {},
    close(socket) {},
    error(socket, error) {},
  },
});

Start a TCP client

import { connect } from "bun";

connect({
  hostname: "localhost",
  port: 8080,
  socket: {
    data(socket, data) {
      console.log("Received:", data);
    },
  },
});

There is also support for:

  • Hot-reloading a TCP socket without downtime
  • Assigning custom data to a socket
  • TLS/SSL support

You can read more about TCP sockets in the README.

This API is very new and I imagine there are still some bugs to fix. Please file issues about any bugs you run into!

Bug fixes

Bun has a special transform for importing builtin modules which converts them from asynchronous ESM imports into synchronous ESM import.meta.require. A transpiler bug when default was imported in these builtin Bun modules along with other named imports caused a runtime error:

// this code would break
import fs, { writeFileSync } from "fs";

// but this code worked
import * as fs from "fs";

The bug was due to how Bun's transpiler rewrote those imports. In this case, default is equivalent to the namespace object (only for these builtin modules which may not actually point to JavaScript source code)

Now, Bun transpiles the above into:

var fs = import.meta.require("node:fs");

// note: live bindings are not preserved
var { writeFileSync } = fs;

When an import fails to resolve at build time, Bun throws a ResolveError. ResolveError includes a position object that exposes detailed line & column information about where the file was originally imported.

There was a bug where Bun would crash if you attempted to access the position object when it came from an await import which imported another file that failed to resolve

File A:

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

describe("ResolveError", () => {
  it("position object does not segfault", async () => {
    try {
      await import("./file-b");
    } catch (e) {
      expect(Bun.inspect(e.position).length > 0).toBe(true);
    }
  });
});

file-b.js:

import "./does-not-exist.js";

This bug has been fixed.


Error handling logic for new Response(Bun.file(fileDescriptorAsANumber)) when used with the HTTP server expected it to be used with file paths instead of file descriptor numbers, causing a crash. Thanks to @sno2 for the fix.

export default {
  // This would crash due to incorrect error handling logic
  fetch(req) {
    return new Response(Bun.file(123));
  },
};

An edgecase involving hex-escaped RegExp literals with lookbehinds caused our Oniguruma polyfill from v0.2.1 to throw an error. This was fixed thanks to @dylan-conway


More bug fixes:

Bun.peek

Sometimes you have code that is async, but you know it is not actually async. The new peek function will allow you to read a promise's result without await or .then, but only if the promise has already fulfilled or rejected.

import { peek } from "bun";

const promise = Promise.resolve("immediate");
const result = peek(promise); // no await!

console.log(result); // "immediate"

It is useful for performance-sensitive code where you want to reduce the number of extra microticks. It's an advanced API and you (probably) shouldn't use it unless you know what you're doing.

You can read more about peek in the README.

Node.js streams

In addition to new APIs, we continue to make progress towards Node.js compatibility. Bun now supports fs.createReadStream and has generally improved support for Node.js streams.

import { createReadStream } from "node:fs";

// Reads the first 100 bytes of a file
const reader = createReadStream("example.txt", {
  encoding: "utf-8",
  start: 0,
  end: 100,
});

reader.on("data", (chunk) => {
  console.log(chunk);
});

WebCrypto API

The standard WebCrypto API is now implemented, thanks to the many contributors at WebKit.

WebCrypto is a low-level API, so we only recommend using it when you know a little more than a thing or two about cryptography. However, there are popular libraries that depend on WebCrypto that you can now use with Bun.

Here's an example using a package that depends on WebCrypto: jose by @panva.

const jwt = await new jose.SignJWT({ "urn:example:claim": true })
  .setProtectedHeader({ alg: "ES256" })
  .setIssuedAt()
  .setIssuer("urn:example:issuer")
  .setAudience("urn:example:audience")
  .setExpirationTime("2h")
  .sign(privateKey);

console.log(jwt);

Homebrew

You can now install or upgrade Bun with our official Homebrew tap.

brew tap oven-sh/bun
brew install bun

You can also install a specific version.

brew install bun@0.2.2

You can also upgrade with either brew or bun.

brew upgrade bun
bun upgrade # still works

All the PRs

New Contributors

Full Changelog