Bun

Bun v1.1.25


Ashcon Partovi · August 21, 2024

Bun v1.1.25 is here! This release fixes 42 bugs (addressing 470 👍). Support for node:cluster and V8's C++ API. Faster WebAssembly on Windows with OMGJIT. 5x faster S3 uploads with @aws-sdk/client-s3, Worker in standalone executables, and 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

node:cluster support

Bun now supports the node:cluster API.

This allows you to run a pool of Bun workers that share the same port, enabling higher throughput and utilization on machines with many CPU cores. This is great for load balancing in production environments.

Here's an example of how it works:

  • The primary worker spawns n child workers (usually equal to the number of CPU cores)
  • Each child worker listens on the same port (using reusePort)
  • Incoming HTTP requests are load balanced across the child workers
import cluster from "node:cluster";
import http from "node:http";
import { cpus } from "node:os";
import process from "node:process";

if (cluster.isPrimary) {
  console.log(`Primary ${process.pid} is running`);

  // Start N workers for the number of CPUs
  for (let i = 0; i < cpus().length; i++) {
    cluster.fork();
  }

  cluster.on("exit", (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} exited`);
  });
} else {
  // Incoming requests are handled by the pool of workers
  // instead of the primary worker.
  http
    .createServer((req, res) => {
      res.writeHead(200);
      res.end("hello world\n");
    })
    .listen(3000);

  console.log(`Worker ${process.pid} started`);
}

This works with the node:http APIs, as well as the Bun.serve() API.

import cluster from "node:cluster";
import { cpus } from "node:os";

if (cluster.isPrimary) {
  for (let i = 0; i < cpus().length; i++) {
    cluster.fork();
  }
} else {
  Bun.serve({
    port: 3000,
    fetch(request) {
      return new Response(`Hello from Worker ${process.pid}`);
    },
  });
}

Note that currently, reusePort is only effective on Linux. On Windows and macOS, the operating system does not load balance HTTP connections as one would expect.

Thanks to @nektro for implementing this feature!

Initial V8 C++ API support

Bun now supports V8's public C++ API, which unblocks packages like cpu-features to work in Bun.

This is notable change because Bun is not built on top of V8 like Node.js. Instead, Bun is built on top of JavaScriptCore, the JavaScript engine that powers Safari.

That means we had to implement our own C++ translation layer between V8's APIs and JavaScriptCore.

src/bun.js/bindings/v8/V8String.cpp
#include "v8.h"
#include "V8Primitive.h"
#include "V8MaybeLocal.h"
#include "V8Isolate.h"

namespace v8 {

enum class NewStringType { /* ... */ };

class String : Primitive {
public:
    enum WriteOptions { /* ... */ };

    BUN_EXPORT static MaybeLocal<String> NewFromUtf8(Isolate* isolate, char const* data, NewStringType type, int length = -1);
    BUN_EXPORT int WriteUtf8(Isolate* isolate, char* buffer, int length = -1, int* nchars_ref = nullptr, int options = NO_OPTIONS) const;
    BUN_EXPORT int Length() const;

    JSC::JSString* localToJSString()
    {
        return localToObjectPointer<JSC::JSString>();
    }
};
}

This is difficult engineering work. JavaScriptCore and V8 represent JavaScript values differently. V8 uses a moving, concurrent garbage collector with explicit handle scopes whereas JavaScriptCore uses a non-moving concurrent garbage collector that scans stack memory (sort of like an implicit handle scope).

Before, if you tried to import a package that used these APIs, you would get an error like this:

console.log(require("cpu-features")());
dyld[94465]: missing symbol called
fish: Job 1, 'bun index.ts' terminated by signal SIGABRT (Abort)

Now, packages like cpu-features can be imported and just work in Bun.

$ bun index.ts
{
  arch: "aarch64",
  flags: {
    fp: true,
    asimd: true,
    // ...
  },
}

This is just the start of support for V8's internal APIs. If your package runs into an area of the V8 API that we have not yet implemented, you'll receive an error that directs you to upvote an issue on GitHub.

Why support V8's internal APIs?

We did not originally intend to support these APIs, but decided to after we found that many popular packages depend on these APIs, such as:

In this release, only cpu-features is supported from the list above, but we're working on improving support so all of these packages and more just work in Bun.

Thanks to @190n for implementing this feature!

5x faster S3 uploads with @aws-sdk/client-s3

We fixed a bug in our node:http client implementation and that made uploading to S3 5x faster.

Worker in standalone executables

Bun's single-file standalone executables now support bundling Worker and node:worker_threads.

main.ts
my-worker.ts
main.ts
console.log("Hello from main thread!");

new Worker("./my-worker.ts");
my-worker.ts
console.log("Hello from another thread!");

To compile a standalone executable with a worker, pass the entry points to bun build --compile:

bun build --compile ./main.ts ./my-worker.ts

This will bundle my-worker.ts and main.ts as separate entry points in the resulting executable.

Embedding files in standalone executables without importing

You can now embed files in standalone executables without an explicit import statement, if you want.

bun build --compile ./main.ts ./public/**/*.png

Usually, you'd have to import the file to use it:

import logo from "./public/icons/logo.png" with {type: "file"};

But, sometimes you want to import a large number of files that may need to programmatically be referenced.

Bun.embeddedFiles

The new Bun.embeddedFiles API lets you see a list of all embedded files in a standalone executable.

import { embeddedFiles } from "bun";

for (const file of embeddedFiles) {
  console.log(file.name); // "logo.png"
  console.log(file.size); // 1234
  console.log(await file.bytes()); // Uint8Array(1234) [...]
}

This combines all non-javascript entry points and assets into a single array of Blob objects sorted by name.

Relative imports for embedded files in standalone executables

You can now reference runtime-known imports of embedded files using relative paths in standalone executables.

function getWasmFilePath(file: string) {
  return require.resolve(`./${file}.wasm`);
}

const wasmFile = getWasmFilePath("my-wasm-file");
console.log(wasmFile); // "./my-wasm-file.wasm"

Previously, this was only supported with statically-analyzable imports that are knonwn at build time. Now, you can do this with any embedded file.

Faster WebAssembly on Windows with OMGJIT

WebAssembly on Windows now supports JavaScriptCore's optimized Just-In-Time compiler (JIT) called OMGJIT.

Thanks to Ian Grunert for shipping this feature in WebKit!

Node.js compatiblity improvements

execa now works

We fixed a bug where Bun was not properly supporting setMaxListeners() for EventTarget. This affected packages like execa, which would error out with undefined is not a function.

import { execa } from "execa";

const { stdout } = await execa`echo "test"`;

If you experienced an error that looks like this, it has now been fixed:

91 |    const controller = new AbortController();
92 |    setMaxListeners(Number.POSITIVE_INFINITY, controller.signal);
      ^
TypeError: undefined is not a function
      at node:events:101:30
      at spawnSubprocessAsync (/node_modules/execa/lib/methods/main-async.js:92:2)
      at execaCoreAsync (/node_modules/execa/lib/methods/main-async.js:26:32)
      at index.mjs:3:26

Fixed: Connection hangs with node:net after destroy()

We fixed a bug where calling destroy() on a TCP connection, didn't always properly exit the process, since the event loop was still active. This would sometimes cause packages like postgres to hang indefinitely.

import net from "node:net";

const server = net.createServer((socket) => {
  socket.on("connect", (data) => {
    socket.destroy();
    // This would destroy the connection,
    // but the event loop would still be active
  });
});

server.listen(3000);

Fixed: node:net.Socket's ref and unref this

The ref and unref methods on node:net.Socket were not returning the this value.

Now, they return this.

Thanks to @nektro for fixing this bug!

Bugfixes

Fixed: TextEncoderStream yielded the wrong type

In Bun v1.1.23, we introduced support the the TextEncoderStream API. We fixed a bug where the chunks from the stream were Buffer objects instead of Uint8Array objects.

const response = await fetch("https://example.com");
const body = response.body.pipeThrough(new TextEncoderStream());

for await (const chunk of body) {
  console.log(chunk);
  // Before: Buffer
  // Now: Uint8Array
}

Fixed: crash with escaped backticks in Bun's shell

We fixed a crash in Bun's shell that would not properly handle escaped backticks.

import { $ } from "bun";

console.log(await $`cd \`find -name dir\``.text());

Thanks to @zackradisic for fixing this bug!

Fixed: JIT crash with TextEncoder

We fixed a bug where if TextEncoder was JIT'd after being run millions of times, it would crash. This was due to a bug in Bun's generated code for enabling the JIT on a function.

Fixed: Crash in jest.fn and mock()

A crash that could occur when calling functions on a jest.fn or mock() object with an unexpected this value has been fixed.

Fixed: Crash in IPC

When a child process exited before the parent process received a message, a crash could occur in some cases.

This has been fixed, thanks to @cirospaciari.

Fixed: console.log a File's name

When you use the web File constructor to create a Blob, the name of the file was not being included in the output of console.log.

Input:

console.log(new File(["hello world"], "hello.txt"));

New:

File (11 bytes) {
  name: "hello.txt",
  lastModified: 1724218333315
}

Previously:

Blob (11 bytes)

Fixed: rare crash in websocket server

A rare crash that could potentially occur when a long amount of time has passed since the server has called a callback and the ServerWebSocket object has been garbage collected has been fixed.

Thank you to 11 contributors!