Bun v1.0.29

Jarred Sumner · February 23, 2024

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

This release fixes 8 bugs. Bun.stringWidth(a) is a ~6,756x faster drop-in replacement for the popular "string-width" package. bunx now checks for updates more frequently. Adds expect().toBeOneOf() in bun:test. Memory leak impacting Prisma is fixed. Shell now supports advanced redirects like 2>&1, &>. Reliability improvements to bunx, bun install, WebSocket client, and Bun Shell

Previous releases

  • v1.0.28 fixes 6 bugs (addressing 26 👍 reactions). Fixes bugs impacting Prisma and Astro, node:events, node:readline, and node:http2. Fixes a bug in Bun Shell involving stdin redirection and fixes bugs in bun:test with test.each and describe.only.
  • v1.0.27 fixes 72 bugs (addressing 192 👍 reactions), Bun Shell supports throwing on non-zero exit codes, stream Response bodies using async generators, improves reliability of fetch(), http2 client, Bun.Glob fixes. Fixes a regression with bun --watch on Linux. Improves Node.js compatibility
  • v1.0.26 fixes 30 bugs (addressing 60 👍 reactions), adds support for multi-statement queries in bun:sqlite, makes bun --watch more reliable in longer-running sessions, Bun.FileSystemRouter now supports more than 64 routes, fixes a bug with expect().toStrictEqual(), fixes 2 bugs with error.stack, improves Node.js compatibility

To install Bun:

curl -fsSL https://bun.sh/install | bash
npm install -g bun
brew tap oven-sh/bun
brew install bun
docker pull oven/bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun

To upgrade Bun:

bun upgrade

bunx checks for updates more frequently

bunx is like npx except powered by bun install. It starts 100x faster than npx.

A cache invalidation bug in bunx caused it to not check for updates as frequently as it should. Now we rely on the timestamp from stat to determine if it's been 24 hours since the last check, which is more reliable.

We've also made it so explicitly using a tag like bunx create-vite@latest will always check for the latest version and delete the previously installed version if it existed.

Thanks to @paperdave for fixing this.

Bun.stringWidth() ~6,756x faster "string-width" replacement

Bun.stringWidth(string) returns the visible width of a string in a terminal. This is useful when you want to know how many columns a string will take up in a terminal.

import { stringWidth } from "bun";

// text
console.log(stringWidth("hello")); // => 5

// emoji
console.log(stringWidth("👋")); // => 2

// ansi colors
console.log(stringWidth("\u001b[31mhello\u001b[39m")); // => 5

// fullwidth characters
console.log(stringWidth("你好")); // => 4

// graphemes
console.log(stringWidth("👩‍👩‍👧‍👦")); // => 2

It accounts for ANSI escape codes, fullwidth characters, graphemes, and emojis. It supports Latin1, UTF-16, and UTF-8 encodings, with optimized implementations for each.

The string-width package gets over 100m downloads/week. Making it a built-in function in Bun means you don't need to install an extra package for this common use case, and lets us optimize it. Huge thanks to @sindresorhus for creating string-width and for the inspiration to include it in Bun.

In this benchmark, Bun.stringWidth performs ~6,756x faster than string-width for ascii input > 500 characters. The range is from 48x faster in the worst-case scenario to 137,623x faster for 25,000 ascii or ansi characters.

❯ bun string-width.mjs
cpu: 13th Gen Intel(R) Core(TM) i9-13900
runtime: bun 1.0.29 (x64-linux)

benchmark                                          time (avg)             (min … max)       p75       p99      p995
------------------------------------------------------------------------------------- -----------------------------
Bun.stringWidth     500 chars ascii              37.09 ns/iter   (36.77 ns … 41.11 ns)  37.07 ns  38.84 ns  38.99 ns

❯ node string-width.mjs
cpu: 13th Gen Intel(R) Core(TM) i9-13900
runtime: node v21.4.0 (x64-linux)

benchmark                                          time (avg)             (min … max)       p75       p99      p995
------------------------------------------------------------------------------------- -----------------------------
npm/string-width    500 chars ascii             249,710 ns/iter (239,970 ns … 293,180 ns) 250,930 ns  276,700 ns 281,450 ns

To make Bun.stringWidth fast, we've implemented it in Zig using optimized SIMD instructions, accounting for Latin1, UTF-16, and UTF-8 encodings. It implements the same API as string-width and passes their tests. This also fixes an edgecase in console.table. Thanks to @nektro for implementing this!

View full benchmark

expect(a).toBeOneOf([a, b, c]) in bun:test

bun:test gets a new matcher: toBeOneOf. This is useful when you want to check if a value is one of a list of values.

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

test("my test here", () => {
  expect(1).toBeOneOf([1, 2, 3]);

This matcher is also supported by the popular jest-extended package.

How is this different than expect(a).toInclude(b)?

expect(a).toInclude(b) checks if a includes b. expect(a).toBeOneOf([a, b, c]) checks if a is one of [a, b, c]. toBeOneOf operates on the actual value, while toInclude operates on the expected value.

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

test("my test here", () => {
  expect([1, 2, 3]).toInclude(1);
  expect(1).toBeOneOf([1, 2, 3]);

When the goal is to check if a value is one of a list of values, toBeOneOf is more readable and gives a better error message when it fails.

Shell supports advanced redirects like 2>&1, &>

Bun Shell now supports advanced redirects like 2>&1 and &>

To output stderr to stdout, you can use 2>&1:

import { $ } from "bun";

await $`vite build 2>&1 output.txt`;

To redirect both stdout and stderr, you can use &>:

import { $ } from "bun";

await $`next build &> output.txt`;

We've also added support for 2>> to append stderr.

Thanks to @zackradisic for implementing this.

Fixed: bun install semver pre-release bug

An edgecase where bun install would sometimes fail to resolve a pre-release version that node-semver resolved correctly has been fixed.

This bug impacted the svelte-eslint-parser package with an error like:

error: No version matching ">=0.34.0-next.4 <1.0.0" found for specifier "svelte-eslint-parser"

Thanks to @dylan-conway for fixing this.

Fixed: WebSocket client short messages bug

A bug where a message with a payload of 1 or 2 bytes would sometimes cause timeouts in the WebSocket client has been fixed. This bug did not impact the WebSocket server. Thanks to @lithdew for fixing this. This was @lithdew's first PR to Bun, but we've been using @lithdew's open source code in many places throughout Bun for awhile now.

Fixed: Memory leak in napi impacting Prisma

A memory leak in napi (which impacted Prisma) has been fixed.

In a microbenchmark, it reduced memory usage from 1720 megabytes to 275 megabytes (a 6x reduction).

Thanks to @camero2734 for fixing this.

Fixed: Memory leak regression in response.blob()

A regression introduced in Bun v1.0.26 causing a memory leak in response.blob() has been fixed. Thanks to @nektro for fixing this!

Fixed: Bun.password.verify on long passwords with bcrypt

bcrypt has a maximum password length of 72 characters. This is part of the reason why Bun.password.hash defaults to the more modern Argon2 algorithm.

To compensate for the max password length in bcrypt, when algorithm is set to bcrypt and the password is longer than 72 characters, Bun.password.hash uses SHA-512 on the password input and sends the SHA-512 hash to bcrypt (the choice of SHA-512 was suggested by the author of the popular libsodium cryptography library).

But, we weren't doing this for Bun.password.verify when the password was longer than 72 characters and the algorithm was set to bcrypt. This meant you could run into situations where Bun.password.verify would return false for a password that was previously hashed with Bun.password.hash.

This has been fixed so that Bun.password.verify now behaves the same as Bun.password.hash when the password is longer than 72 characters when using bcrypt. Thanks to @argosphil for fixing this.

Occasionally, packages published to npm include a vendored node_modules directory in a subdirectory (often by accident). Previously, bun install would ignore these vendored node_modules folders when using hardlinks to install, which broke packages relying on this behavior. We've fixed it so bun install no longer ignores vendored node_modules folders.

Thanks to @eemelipa for fixing this.

Fixed: Shell substitution bug adding extra space character

Previously, the following code in Bun Shell would add an extra space character:

echo $(echo id)/$(echo region) # => "id / region"

Now it correctly outputs:

echo $(echo id)/$(echo region) # => "id/region"

Thanks to @zackradisic.

Fixed: Shell latin1 template encoding bug

A bug where Bun Shell would sometimes handle non-ascii latin1 characters incorrectly has been fixed. Thanks to @zackradisic.

Fixed: bunx with multiple users on the same machine

bunx now works correctly when multiple users on the same machine use it. Since bunx stores its cache in the temporary directory, multiple users on the same machine share the same cache directory. This sometimes caused permissions issues. We've fixed this by including the uid in the cache key so that different users don't share the same cache.

Thanks to @paperdave for fixing this.

Bundows is soon

We continue to work on Windows support. Windows will be supported in Bun v1.1.

Thanks to 8 contributors!