Bun

Bun v1.1.14


Dave Caruso · June 19, 2024

Bun v1.1.14 is here! This release fixes 63 bugs (addressing 519 👍). Patch node_modules with bun patch. ORM-less object mapping in bun:sqlite. Log all fetch() calls as a curl command. Node.js compatibility improvements. bun install bugfixes, bun:sqlite bugfixes, Windows bugfixes. And more.

We're hiring systems engineers in San Francisco to build the future of JavaScript!

Previous releases

  • v1.1.13 fixes 97 bugs (addressing 211 👍). Reliability improvements to bun install, WebSocket server, fetch, worker_threads. worker_threads now supports eval. URL.createObjectURL, stack trace error location improvements, several bun install fixes
  • v1.1.10 fixes 20 bugs. 2x faster uncached bun install on Windows. fetch() uses up to 2.8x less memory. Several bugfixes to bun install, sourcemaps, Windows reliability improvements and Node.js compatibility improvements
  • v1.1.0 Bundows. Windows support is here!

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

bun patch

Bun v1.1.14 introduces a new subcommand for Bun's package manager: bun patch

bun patch <pkg>

Sometimes, dependencies have bugs or missing features. You could fork the package, make your changes, and publish it. But that's a lot of work for a small change. And what if you don't want to maintain a fork? What if you want to continue to receive updates from the original package, but with your changes?

bun patch makes it easy to modify dependencies without forking or vendoring. Changes persist into a .patch file which can be reviewed, shared, versioned, and reused in other projects. These .patch files are automatically applied on bun install, with the results cached in Bun's Global Cache.

To get started, run bun patch <pkg> to patch a package.

bun patch is-even

bun patch v1.1.14

+ is-even@1.0.0

5 packages installed [3.00ms]

To patch is-even, edit the following folder:

  node_modules/is-even

Once you're done with your changes, run:

  bun patch --commit 'node_modules/is-even'

Internally, this clones the package in node_modules with a fresh copy of itself independent from the shared cache. This allows you to safely make edits to files in the package's directory without impacting the Global Cache.

Since we clone the package, you can test your patches locally in your project's node_modules folder. This makes it easy to iterate on your changes and verify they work as expected.

Once you're happy with your changes, run bun patch --commit <pkg> to save your changes.

bun patch --commit is-even

When you run bun patch --commit is-even, Bun:

  1. Generates a patch file with your changes
  2. Adds the dependency to "patchedDependencies" in your package.json
  3. Saves the patch file in the root of your project under patches/

Patch files use the standard diff format, so you can review, share, and version them like any other code.

./patches/is-even@1.0.0.patch
diff --git a/index.js b/index.js
index 832d92223a9ec491364ee10dcbe3ad495446ab80..2a61f0dd2f476a4a30631c570e6c8d2d148d419a 100644
--- a/index.js
+++ b/index.js
@@ -1,14 +1 @@
-/*!
- * is-even <https://github.com/jonschlinkert/is-even>
- *
- * Copyright (c) 2015, 2017, Jon Schlinkert.
- * Released under the MIT License.
- */
-
-'use strict';
-
-var isOdd = require('is-odd');
-
-module.exports = function isEven(i) {
-  return !isOdd(i);
-};
+module.exports = (i) => (i % 2 === 0)

Thanks to @zackradisic for implementing bun patch!

Better bun:sqlite

This release adds several improvements to Bun's builtin sqlite module bun:sqlite.

ORM-less object mapping with query.as(Class)

bun:sqlite now supports mapping query results to instances of a class. This lets you attach methods, getters, and setters to query results without using an ORM.

import { Database } from "bun:sqlite";

const db = new Database("my.db");

class Tweet {
  id: number;
  text: string;
  username: string;

  get isMe() {
    return this.username === "jarredsumner";
  }
}

const tweets = db
  .query("SELECT * FROM tweets")
  .as(Tweet);

for (const tweet of tweets.all()) {
  if (!tweet.isMe) {
    console.log(`${tweet.username}: ${tweet.text}`);
  }
}

The properties don't need to be defined on the class ahead of time (it's there just to make the example easier to read) and the order of the columns in the query doesn't matter.

This is a light way to map query results to objects. Underneath, it works similarly to Object.create, which meanas it doesn't call constructors, run default initializers, and cannot access private fields.

The as method is defined on the Statement class, so you can use it with get and all methods. It's important to note that this is not an ORM. It doesn' manage relationships, generate SQL, or anything like that. It makes queries return a class instance instead of a plain object.

How it works

When you call query.as(Class), Bun creates a new object with the class's prototype and assigns the query result to it.

const query = db.query("SELECT * FROM tweets").as(Tweet);

// Get the first row as a Tweet instance
const tweet: Tweet = query.get();

// Each row is a Tweet instance
const allTweets: Tweet[] = query.all();

Simpler query bindings with strict: true

This release adds a new strict?: boolean option to the Database constructor which lets you omit the $, @, or : prefix when binding values to prepared statements.

import { Database } from "bun:sqlite";

const db = new Database(":memory:", {
  strict: false,
  strict: true,
});

const query = db.query(`select $message;`);

query.all({
  $message: "Hello world"
  message: "Hello world"
});

When strict: true, queries will throw if they are missing any bindings.

To keep backwards compatibility, strict defaults to false. It is recommended to enable for new projects.

Track changes with query.run(sql)

The run method on Database and Statement now returns an object with changes and lastInsertRowid properties. This makes it easier to track changes and get the last inserted row id.

import { Database } from "bun:sqlite";

const db = new Database(":memory:");

db.run(`CREATE TABLE hey ( id INTEGER, data TEXT)`);
const { changes, lastInsertRowid } = db.run(
  `insert into hey (id, data) values (1, 'foo')`,
);
console.log({
  changes, // => 1
  lastInsertRowid, // => 1
});

This is important when verifying that a query has made changes to the database. Previously, run would return undefined.

BigInts with safeIntegers: true

When safeIntegers is true, bun:sqlite will return integers as bigint types.

import { Database } from "bun:sqlite";

const db = new Database(":memory:", { safeIntegers: true });
const query = db.query(
  `SELECT ${BigInt(Number.MAX_SAFE_INTEGER) + 102n} as max_int`,
);
const result = query.get();
console.log(result.max_int); // => 9007199254741093n

These integers must fit within 64-bit range. If an input exceeds that, bun:sqlite will throw an error.

import { Database } from "bun:sqlite";

const db = new Database(":memory:", { safeIntegers: true, strict: true });
db.run("CREATE TABLE test (id INTEGER PRIMARY KEY, value INTEGER)");

const query = db.query("INSERT INTO test (value) VALUES ($value)");

try {
  query.run({ value: BigInt(Number.MAX_SAFE_INTEGER) ** 2n });
} catch (e) {
  console.log(e.message); // => BigInt value '81129638414606663681390495662081' is out of range
}

Sometimes, you only wnat this behavior for specific queries. The safeIntegers method is available on Statement.

import { Database } from "bun:sqlite";

const db = new Database(":memory:", { strict: true });

const query = db.query("SELECT $value as value").safeIntegers(true);

const result = query.get({ value: BigInt(Number.MAX_SAFE_INTEGER) + 102n });
console.log(result.value); // => 9007199254741093n

Fixed: query.values() with duplicate column names

A bug caused duplicate column names to be ignored when calling query.values(). This has been fixed.

import { Database } from "bun:sqlite";
const db = new Database();
const result = db.prepare("select 1 as id, 2 as id").values();
console.log(result);

Fixed:

[
  [ 1, 2 ]
]

Previously:

[
  [ 2 ]
]

This bug would most frequently occur when using JOIN.

Fixed: bun:sqlite with table joins crash fixed.

A crash that sometimes occurred when joining tables with duplicate columns has been fixed. This impacts using Drizzle's driver for bun:sqlite and .leftJoin.

bun install reliability

Fixed: Tarballs failing to download exit with non-zero code

When a tarball failed to download, bun install would exited with a zero exit code. This has been fixed. When any HTTP request fails, bun install will now exit with a non-zero exit code so that CI systems stop the build.

Fixed: bun install handling transitive folder dependencies

Sometimes packages are published to the npm registry with a file: dependency. Sometimes

Thanks to @dylan-conway for fixing this.

Fixed: bun install extracting bug fix

It is possible for a .tar.gz archive to contain multiple entries for the same directory (one with ./ and one without it). Bun now handles this while extracting, instead of reporting an error that the directory it's extracting to already exists.

Thanks to @dylan-conway for fixing this.

Fixed: bun install --production in a workspace included devDependencies

When --production is passed to bun install in a workspace, the dev dependencies of the workspace packages would be included in the install. This is no longer the case.

This also fixed an assertion failure when in this situation.

Fixed: Not passing npm_config_user_agent on Windows

In order for project initialization scripts like create-next-app to know which package manager to use, package managers are expected to set the npm_config_user_agent environment variable. Bun on Windows was not always passing modified environment variables to child processes, so that has been fixed.

For example, creating a new Next.js project on Windows will properly tell Next.js to use bun install.

 PS> bun create next-app
 ✔ What is your project named? … my-app
 ✔ Would you like to use TypeScript? … No / Yes
 ✔ Would you like to use ESLint? … No / Yes
 ✔ Would you like to use Tailwind CSS? … No / Yes
 ✔ Would you like to use `src/` directory? … No / Yes
 ✔ Would you like to use App Router? (recommended) … No / Yes
 ✔ Would you like to customize the default import alias (@/*)? … No / Yes
 Creating a new Next.js app in D:\Projects\my-app.

-Using npm.
+Using bun.

 Initializing project with template: app-tw

Thanks to @SunsetTechuila for fixing this.

Debugging fetch()

This release adds a new environment variable to help debug network traffic when using fetch() or node:http.

When BUN_CONFIG_VERBOSE_FETCH=1 is passed as an environment variable, Bun will log all network traffic through fetch and node:http clients.

When BUN_CONFIG_VERBOSE_FETCH=curl is set as an environment variable, Bun will print all network traffic through fetch and node:http clients as curl commands.

process.env.BUN_CONFIG_VERBOSE_FETCH = "curl";

await fetch("https://example.com", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ foo: "bar" }),
});

This prints the fetch request as a curl command to let you copy-paste into your terminal to replicate the request.

[fetch] $ curl --http1.1 "https://example.com/" -X POST -H "content-type: application/json" -H "Connection: keep-alive" -H "User-Agent: Bun/1.1.14" -H "Accept: */*" -H "Host: example.com" -H "Accept-Encoding: gzip, deflate, br" --compressed -H "Content-Length: 13" --data-raw "{\"foo\":\"bar\"}"
[fetch] > HTTP/1.1 POST https://example.com/
[fetch] > content-type: application/json
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.1.14
[fetch] > Accept: */*
[fetch] > Host: example.com
[fetch] > Accept-Encoding: gzip, deflate, br
[fetch] > Content-Length: 13

[fetch] < 200 OK
[fetch] < Accept-Ranges: bytes
[fetch] < Cache-Control: max-age=604800
[fetch] < Content-Type: text/html; charset=UTF-8
[fetch] < Date: Tue, 18 Jun 2024 05:12:07 GMT
[fetch] < Etag: "3147526947"
[fetch] < Expires: Tue, 25 Jun 2024 05:12:07 GMT
[fetch] < Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
[fetch] < Server: EOS (vny/044F)
[fetch] < Content-Length: 1256

You can use this to debug network requests, to replicate network requests, or to inspect what different JavaScript/TypeScript tools are sending over the network without needing to wire up a proxy.

Node.js compatibility improvements

node:fs/promises functions now accept FileHandle

When using node:fs/promises, the readFile, writeFile, and appendFile functions accept FileHandle objects as input.

import { open, readFile } from 'node:fs/promises';

using file = await open("example.txt", "rw");
const contents = await readFile(file);
console.log(contents);
await writeFile(file, `New data: ${Math.random()}\n`);

Thanks to @nektro for implementing this.

process.env.NODE_TLS_REJECT_UNAUTHORIZED

You can now set process.env.NODE_TLS_REJECT_UNAUTHORIZED at runtime to disable or enable SSL certificate verification. This is useful for testing or debugging, but should not be enabled in production.

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
await fetch("https://self-signed.badssl.com"); // no error
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "1";
await fetch("https://self-signed.badssl.com"); // throws error

Previously, this environment variable could only be passed at startup. Now, it can be set at runtime.

Fixed: Hang in node:http / express

A bug impacting node:http server where req.completed was never set to true would cause some requests in Express to hang indefinitely. This has been fixed thanks to @cirospaciari

Fixed: blob.type

A race condition would sometimes make blob.type from fetch undefined. Thanks to @cirospaciari, this has now been resolved.

const blob = await fetch("https://bun.sh/logo.svg").then((res) => res.blob());
console.log("Blob type is:", blob.type);

Fixed: blob.name is writable

You can now set the name property on a Blob object.

const blob = await fetch("https://bun.sh/logo.svg").then((res) => res.blob());
blob.name = "bun!!!!!";
console.log(blob.name); // "bun!!!!!"

Previously, setting blob.name would throw a TypeError:

1 | const blob = await fetch("https://bun.sh/logo.svg").then((res) => res.blob());
2 | blob.name = "bun!!!!!";
    ^
TypeError: Attempted to assign to readonly property.

Fixed: WebSocket over SSL with large binary payloads

Previously, when sending large binary payloads over wss:// WebSocket connections, the message might not have been sent. This was due to not calling set_retry_write from BoringSSL. Thanks to @cirospaciari for fixing this.

Fixed: Bun no longer crashes Windows Terminal

On Windows, the flag ENABLE_VIRTUAL_TERMINAL_INPUT was always enabled on the console input. Without removing this flag before exit, most shells would break (cmd.exe arrow keys break, Powershell would often crash, nu would not be able to accept new lines, etc). This flag was removed, as Bun no longer needed it set for it's input handling (prompt(), node:readline, bun init, etc)

Thanks to @paperdave for fixing this.

Fixed: WebSocket header casing inconsistent with node

Previously, using new WebSocket would convert all headers into lowercased text. This had some issues with some servers that expected headers to have certain casing, even though headers are meant to be case insensitive. Now, well-known headers will be normalized to their canonical form, and custom headers will retain their exact casing.

For example:

const ws = new WebSocket("ws://localhost:3000", {
  headers: {
    "Origin": "http://localhost:3000",
    "authorization": "password",
    "X-some-Other-header": "bun!",
  },
});

Will send these headers:

> Origin: http://localhost:3000
> Authorization: password
> X-some-Other-header: bun!

Thanks to @cirospaciari for fixing this.

Fixed: memory leak in Bun Shell

A memory leak impacting the Bun Shell has been fixed. This would cause the shell objects to never be garbage collected, preventing memory from being reclaimed.

Fixed: Regression with error.line

A bug where error.line and error.column would return 0 has been fixed. This regression was introduced in v1.1.13.

Thanks to 10 contributors!