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, severalbun install
fixesv1.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 improvementsv1.1.0
Bundows. Windows support is here!
To install Bun
curl -fsSL https://bun.sh/install | bash
npm install -g bun
powershell -c "irm bun.sh/install.ps1|iex"
scoop install 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
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:
- Generates a patch file with your changes
- Adds the dependency to
"patchedDependencies"
in yourpackage.json
- 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.
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.
Print fetch() as a curl
command
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
.