Bun

Bun v1.0.36


Ashcon Partovi · March 29, 2024

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

Bun v1.0.36 fixes 13 bugs. Adds support for fs.openAsBlob and fs.opendir, fixes edgecase in bun install with multiple bin entries in package.json, fixes bun build --target bun encoding, fixes bug with process.stdin & process.stdout, fixes bug in http2, fixes bug with .env in bun run, fixes various Bun shell bugs and improves Node.js compatibility.

Bun v1.1 (Bundows) is coming April 1st.

Previous releases

  • v1.0.35 fixed a regression from Bun v1.0.34
  • v1.0.34 fixes 7 bugs, bunx uses less memory, bun install gets faster on Docker & WSL, a conflicting devDependency hoisting bug is fixed, a cyclical CommonJS & ESM import crash is fixed, cross-mount fs.cp & fs.copyFile get 50% faster on Linux, and reliability improvements for bun install & bun's runtime.
  • v1.0.33 fixes 2 bugs, including a bug with the mv command in Bun Shell and in node:crypto creating & verifying signatures
  • v1.0.32 fixes 13 bugs and improves Node.js compatibility. 'ws' package can now send & receive ping/pong events. util.promisify'd setTimeout setInterval, setImmediate work now. FileHandle methods have been implemented.

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

Node.js compatibility improvements

New: fs.openAsBlob

Bun now supports the recently added fs.openAsBlob() API, which allows you to open a file as a Blob. This returns the same underlying Blob as Bun.file.

import { openAsBlob } from "node:fs";

const blob = await openAsBlob("hello.txt");
console.log(await blob.text()); // "Hello, world!"

New: fs.opendir

We also added support for fs.opendir(), which allows you to open a directory and read its contents.

import { opendir } from "node:fs";

const dir = await opendir(".");
for await (const dirent of dir) {
  console.log(dirent.name); // "hello.txt", "world.txt"
}

Fixed: Hang piping process.stdin to process.stdout

When at least 16 KB was piped to process.stdout, the process would sometimes hang. This has been fixed.

Fixed: Crash in http2

A crash that could occur when using the http2 client has been fixed. This crash was related to unimplemented HTTP2 features.

Fixed: Readable.toWeb(process.stdin)

The Readable.toWeb() function was not working correctly when reading from process.stdin. This has been fixed.

Now Readable.toWeb(process.stdin) is effectively the same as Bun.stdin.stream().

Fixed: Regression using process.stdout.moveCursor()

A regression in Bun v1.0.34 caused process.stdout.moveCursor() to throw ReferenceError: Can't find variable: readline. This has been fixed.

Fixed: TextDecoder error missing code property

When decoding an invalid UTF-8 string with TextDecoder, the error object would not have a code property. The Web API doesn't return a code property for invalid UTF-8 strings, but Node.js does. For compatiblity, we now return the same code property as Node.js.

This fixes a bug blocking @angular/cli new from running using Bun.

Fixed: non-blocking fs.readv & fs.writev

The node:fs APIs fs.readv and fs.writev were running in a blocking manner. This has been fixed. We now enqueue the readv & writev functions to the threadpool when their sync versions are not called.

Fixed: Edgecase with multiple bin entries in package.json

Previously, there was a bug when:

  1. exactly one version of a package had a bin entry in the NPM Registry API
  2. all other versions did not have a bin entry
  3. bin object had multiple entries

This bug caused bun install to fail to link the binaries properly.

package.json
{
  "bin": {
    "a": "bin",
    "b": "bin",
  }
}

Thanks to @dylan-conway for fixing this issue.

Fixed: missing terminate method in ws server

The ws polyfill in Bun was missing the terminate method on the WebSocketServer's WebSocket instance. This has been fixed.

Thanks to @paperdave for fixing this issue.

Runtime improvements

File descriptor leak prevention mechanism

POSIX defaults file descriptors to be inherited by child processes. This frequently causes file descriptor leaks in all sorts of ways. Bun has always used the O_CLOEXEC flag to prevent file descriptors from being inherited, but there were cases where this was not enough.

On modern Linux kernels, Bun now uses the close_range system call to prevent file descriptors from being inherited without the O_CLOEXEC flag. This is a much more reliable way to prevent file descriptor leaks.

On macOS, Bun exclusively uses posix_spawn to spawn child processes which has an equivalent mechanism to prevent file descriptor leaks which makes this harder to happen in the first place.

Restore stdin/stdout to their original state on SIGINT/SIGTERM

Previously, Bun would not always restore stdin/stdout to their original state on SIGINT/SIGTERM. This has been fixed.

Non-blocking TTY reads

On POSIX, Bun now reads from stdin in a non-blocking manner. Normally, this would cause the other end of the pipe to also be non-blocking, but we have a workaround (thanks to reading some code from libuv). We use ttyname_r to open the TTY as a non-blocking file descriptor without affecting the other end of the pipe.

Fixed: Crash using Bun.escapeHTML

We fixed a crash where specific input could crash Bun.escapeHTML, which was caused by an off-by-one bug.

Thanks to Querijn Voet for reporting this issue.

Fixed: Edgecase with WebSocket handshake

A bug in our WebSocket client implementation could cause non-101 status codes to be treated as a WebSocket upgrade request. This bug was not known to ever exist in the wild, but we fixed it anyway.

Fixed: potential file descriptor redirection bug in Bun.spawn

A bug that could cause redirecting stdin, stdout, or stderr led to Bun closing the file descriptors before the child process finished starting. This bug was caused by misunderstanding how the dup2 libc function works. When dup2 receives the same file descriptor as the destination, it does nothing. Now when redirecting the same file descriptor, Bun will instead remove the O_CLOEXEC flag from the file descriptor which allows the child process to inherit the file descriptor. This bug only impacted linux, and was more difficult to reproduce than just inheriting file descriptors.

Fixed: incorrect .env propagation in bun run

A regression caused .env files to be loaded unexpectedly when using bun run before the script is run. This regression has been fixed and we've improved our test suite to prevent this from occurring again.

Shell (Bun.$) stability improvements

We fixed various bugs and regressions with the Bun shell, including:

  • A race condition where the shell would hang.
  • A possible crash when the subprocess exited before the event loop was ready.
  • A possible crash when stdin was over 128kb in size.
  • When spawning a subprocess that uses interactive mode, user input would not be sent to the subprocess. This affected packages like @inquirer/prompts.
shell.js
prompt.js
input.js
shell.js
import { $ } from "bun";

await $`bun input.js | bun prompt.js`;
prompt.js
const { select } = require("@inquirer/prompts");

async function run() {
  const foobar = await select({
    message: "Foo or Bar",
    choices: [
      { name: "Foo", value: "foo" },
      { name: "Bar", value: "bar" },
    ],
  });
  console.error("Choice:", foobar);
}

run();
input.js
// Simulate pressing the down arrow key
const writer = Bun.stdout.writer();
await Bun.sleep(100);
writer.write("\\x1b[B");

// Simulate pressing the enter key
await Bun.sleep(100);
writer.write("\\x0D");
writer.flush();

Thanks to @zackradisic for fixing these issue.

Fixed: bun build --target bun used wrong encoding

Bun transpiles files to latin1 because the file size is reduced when being stored as utf-16. There was a bug where bun build --target bun would encode as utf-8 instead, which caused issues with non-latin1 characters. This has been fixed.

Given the following file, previously it would have been encoded as-is in utf-8.

console.log({ 我: "a" });

Now, it is encoded properly in latin1.

// @bun
console.log({ "\u{6211}": "a" });

Thanks to @pfgithub for fixing this issue.

Bundows (Bun v1.1) is April 1st

Bun v1.1 will ship on April 1st.

Thank you to 13 contributors!