Bun is an incredibly fast JavaScript runtime, bundler, transpiler, and package manager — all in one. In case you missed it, here are some of the recent changes to Bun.
This release fixes 40 bugs (addressing 194 👍 reactions). import & embed sqlite databases in Bun, Resource Management ('using' TC39 stage3) support, bundler improvements when building for Node.js, bugfix for peer dependency resolution, semver bugfix, 4% faster TCP on linux, Node.js compatibility improvements and more.
Previous releases
v1.0.22
fixes 29 bugs (addressing 118 👍 reactions), fixesbun install
issues on Vercel, addsperformance.mark()
APIs, addschild_process
support for extra pipes, makesBuffer.concat
faster, addstoBeEmptyObject
andtoContainKeys
matchers, fixesconsole.table
width using emojis, and support forargv
andexecArgv
options inworker_threads
, and supports Brotli infetch
.v1.0.21
- Fixes 33 bugs (addressing 80 👍 reactions).console.table()
support.Bun.write
, Bun.file, and bun:sqlite use less memory. Large file uploads with FormData use less memory. bun:sqlite error messages get more detailed. Memory leak in errors from node:fs fixed. Node.js compatibility improvements, and many crashes fixed.v1.0.20
- Reduces memory usage infs.readlink
,fs.readFile
,fs.writeFile
,fs.stat
andHTMLRewriter
. Fixes a regression where setTimeout caused high CPU usage on Linux.HTMLRewriter.transform
now supports strings andArrayBuffer
.fs.writeFile()
andfs.readFile()
now supporthex
&base64
encodings.Bun.spawn
shows how much CPU & memory the process used.
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
Import sqlite databases in Bun
You can now import sqlite databases in Bun. This makes it simpler to get started with using sqlite in your project.
import db from './my.db' with {type: "sqlite"};
const {id} = db
.query("SELECT id FROM users LIMIT 1")
.get();
console.log(id); // 1
This also works with bun build --compile
, which is great for deploying small databases to production. It means you can build your whole app into a single executable, and then deploy that executable alongside your database file, and use a different database file in develpoment versus production.
bun build --compile ./my-app.ts
# => ./my-app
# On your remote machine:
./my-app
Internally, this is roughly equivalent to:
import { Database } from "bun:sqlite";
const db = new Database("./my.db");
Embed sqlite databases into single-file executables
If your application would benefit from embedding into the executable itself, that is also supported. To embed a sqlite database, pass embed: "true"
in the import attributes.
import db from './my.db'
with {
type: "sqlite",
// Embed the database into the executable
embed: "true"
};
const {id} = db
.query("SELECT id FROM users LIMIT 1")
.get();
console.log(id); // 1
And now you can use bun build --compile
to build your app into a single executable, and the database will be embedded into the executable itself.
bun build --compile ./my-app.ts
mv my.db /tmp/my.db # This isn't used in my-app anymore
./my-app # Since the database is embedded, it will work without the database file
You can also use embedded sqlite databases with bun build --target=bun
. In that case it will copy the database file into the output directory, and import it from there.
bun build --target=bun ./my-app.ts --outdir=out
./out/my-app # Since the database is copied into the output directory, it will work without the database file
Upgraded SQLite to v3.45.0
SQLite 3.45.0 added JSONB support, which makes storing & reading JSON data faster. We've upgraded Bun (on Linux) to use this version of SQLite.
Embed .node files with bun build --compile
You can now embed NAPI (n-api) addons .node
files with bun build --compile
. This is useful for bundling native Node.js modules, like @anpi-rs/canvas
.
import { promises } from "fs";
import { join } from "path";
import { createCanvas } from "@napi-rs/canvas";
const canvas = createCanvas(300, 320);
const ctx = canvas.getContext("2d");
ctx.lineWidth = 10;
ctx.strokeStyle = "#03a9f4";
ctx.fillStyle = "#03a9f4";
// Wall
ctx.strokeRect(75, 140, 150, 110);
// Door
ctx.fillRect(130, 190, 40, 60);
// Roof
ctx.beginPath();
ctx.moveTo(50, 140);
ctx.lineTo(150, 60);
ctx.lineTo(250, 140);
ctx.closePath();
ctx.stroke();
const pngData = await canvas.encode("png"); // JPEG, AVIF and WebP are also supported
await promises.writeFile(join(__dirname, "simple.png"), pngData);
bun build --compile canvas.ts
Then you can run the executable:
rm -rf node_modules # Not needed anymore
./canvas # => simple.png
Bugfixes for bun build --target=node
We've fixed a number of bugs in bun build --target=node
.
Requiring Node.js builtin modules like fs
and path
is now supported.
var { promises } = require("fs");
var { join } = require("path");
promises.readFile(join(__dirname, "data.txt"));
Previously, this code would fail at runtime:
bun build --target=node ./my-app.ts --outfile=app.mjs
node ./app.mjs
TypeError: (intermediate value).require is not a function
at __require (file:///app.mjs:2:22)
at file:///app.mjs:7:20
at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
at async loadESM (node:internal/process/esm_loader:28:7)
at async handleMainPromise (node:internal/modules/run_main:113:12)
Now it works:
bun build --target=node ./my-app.ts --outfile=app.mjs
node ./app.mjs
A number of other bugs were fixed, including:
- Missing dead-code elimination for
__require
function
Resource Management is now supported
We've implemented bundler support for Resource Management which is currently at TC39 stage 3 and available in TypeScript. This is a new feature that lets you manage resources like file handles, database connections, and network sockets. It is similar to the using
keyword in C#.
// in an async function:
async function * g() {
await using handle = acquireFileHandle(); // async-block-scoped critical resource
} // async cleanup
// in a block in an async context:
{
await using obj = g(); // block-scoped declaration
const r = await obj.next();
} // calls finally blocks in `g` and awaits result
You can use this at runtime in Bun and with bun build
. We've implemented a polyfill that will work in Bun, in Node.js, and in web browsers (based on esbuild's polyfill).
We've also implemented partial support in JavaScriptCore, including Symbol.dispose
, Symbol.asyncDispose
, and SuppressedError
. We have not implemented parser or AST support in JavaScriptCore, since we can use the transpiler for that for now.
Thanks to @paperdave for implementing this.
bun remove missing-pkg
no longer errors
Previously, bun remove missing-pkg
would error if the package was not installed. Now it does not error. This aligns the behavior with npm
.
Bun | NPM |
---|---|
Thanks to @kaioduarte for this contribution.
4% faster TCP sockets on Linux
On Linux, sending large amounts of data over TCP sockets is now 4% faster. We've reduced the number of system calls involved in ticking the event loop.
Before (with some numbers removed for brevity):
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288) = 524288
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288) = 524288
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288) = 524288
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 2
recvfrom(fd: 14, ubuf: 0x123, size: 524288) = 524288
sendto(fd: 15, buff: 0x7f9093666dc0, len: 1016697408, flags: NOSIGNAL) = 1571592
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288) = 524288
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288) = 524288
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288) = 524288
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 2
After (with some numbers removed for brevity):
epoll_wait(epfd: 7, events: 0x559c3cae78a0, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 127574
epoll_wait(epfd: 7, events: 0x559c3cae78a0, maxevents: 1024, timeout: 4294967295) = 1
sendto(fd: 15, buff: 0x456, len: 72939772, flags: DONTWAIT|NOSIGNAL) = 4321878
epoll_wait(epfd: 7, events: 0x559c3cae78a0, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 127574
epoll_wait(epfd: 7, events: 0x559c3cae78a0, maxevents: 1024, timeout: 4294967295) = 1
sendto(fd: 15, buff: 0x456, len: 68617894, flags: DONTWAIT|NOSIGNAL) = 4321878
epoll_wait(epfd: 7, events: 0x559c3cae78a0, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
Raised HTTP server header limit from 50 to 100
We've upgraded our uWebSockets fork to latest, and with that came a change to the default HTTP server header limit. It was raised from 50 to 100.
import.meta.dirname
and import.meta.filename
support
import.meta.dirname
and import.meta.filename
are now supported. They are aliases to import.meta.dir
and import.meta.path
. This is useful for compatibility with Node.js which released support for these very recently.
// /path/to/file.js
console.log(import.meta.dirname); // /path/to
console.log(import.meta.filename); // /path/to/file.js
FileHandle
support in fs/promises
Previously, Bun's fs/promises
did not support FileHandle
objects. Now it does.
import { promises } from "node:fs";
const filehandle = await promises.open("file.txt", "r");
const stat = await filehandle.stat();
await filehandle.close();
events.on
has been implemented
events.on
returns an AsyncIterator
that lets you loop over events as they happen.
Some packages rely on this API, and now it is supported in Bun.
import { on, EventEmitter } from "node:events";
const ee = new EventEmitter();
// Emit later on
process.nextTick(() => {
ee.emit("foo", "bar");
ee.emit("foo", 42);
});
for await (const event of on(ee, "foo")) {
// The execution of this inner block is synchronous and it
// processes one event at a time (even with await). Do not use
// if concurrent execution is required.
console.log(event); // prints ['bar'] [42]
}
// Unreachable here
Thanks to @nektro for implementing this.
process.binding('tty_wrap')
support
process.binding('tty_wrap')
is now supported. Some packages like readline-sync
rely on this API, and even though bun's tty
implementation is different from Node.js, we have implemented this Node.js internal binding API to support packages that depend on it.
process.binding("tty_wrap").TTY;
Implemented fs.fdatasync
fs.fdatasync
is now implemented, thanks to @nektro.
Fixed: 'Not a string or buffer' from zlibBufferSync error
Our polyfill of zlibBufferSync was not properly checking for non-Buffers, like Uint8Array. This has been fixed, thanks to @Electroid.
Fixed: File descriptor leak in Bun.spawn() with IPC
When IPC was in use, Bun.spawn() would neglect to close the 2nd socket's file descriptor, causing a file descriptor leak. This has been fixed.
Fixed: handle missing .host in node:url
consistently with node
The following snippet would behave differently in Bun than in Node.js:
const url = require("url");
console.log(url.parse("http://"));
This has been fixed, thanks to @kaioduarte.
Bumped Node.js version
The version of Node.js reported in process.versions.node
has been bumped to v21.6.0 which is the latest version at the time of release.
Fixed: peer dependency upgrade resolution
Let's say you installed drizzle-cli
and drizzle-orm
. drizzle-cli
had a peer dependency on drizzle-orm@^1.0.0
. You upgrade drizzle-orm
to 1.0.1
, you expect drizzle-cli
to now use drizzle-orm@1.0.1
. Previously, this would not happen -- but now it does, thanks to @eriklangille.
Fixed: semver comparison bug
The following test would behave differently when compared with node-semver
versus Bun.semver
:
import { test, expect } from "bun:test";
import { semver } from "bun";
test("semver with multiple tags work properly", () => {
expect(semver.satisfies("3.4.5", ">=3.3.0-beta.1 <3.4.0-beta.3")).toBeFalse();
});
This bug impacted bun install
and Bun.semver
.
This has been fixed, thanks to @Electroid.
Fixed: WebSocket
on connection failure threw an error
The following code would throw an error in Bun, but not in web browsers:
const ws = new WebSocket("wss://apsodkapsodkpo.com:3000");
// => Uncaught Error:
Instead, it's supposed to emit an error event. This has been fixed, thanks to @LukasKastern.
Fixed: Invalid stdio option "null"
The following code would behave differently in Bun vs Node.js:
import cp from "child_process";
cp.spawnSync("ls", ["-lagh"], { stdio: [null, "inherit", null] });
This has been fixed, Thanks to @Electroid.
Fixed: printed exception twice in Bun.serve() sometimes
A bug where the following code would print the exception twice has been fixed:
Bun.serve({
port: 3000,
fetch: async () => {
await Bun.sleep(1);
throw new Error("A");
return new Response("A");
},
error() {
return new Response("Handled");
},
});
This has been fixed, thanks to @LukasKastern.
Fixed: ServerWebSocket idleTimeout
An event loop bug was fixed that could cause ServerWebSocket
timeout to take much longer than expected.
Thanks to @LukasKastern for fixing this.
Fixed: macOS large file uploads sometimes failed
A bug impacting macOS where the final chunk of a large file upload (> 512 KB) received all at once would sometimes lose part of the final chunk has been fixed. The bug was caused by incorrect handling of KQueue events. Unlike Linux (epoll), kqueue may send both the socket hangup and the readable event at the same time, and we were not handling that case correctly when the data was larger than the read buffer.
Thanks to 12 contributors who made this release possible!
- @aayushbtw
- @cirospaciari
- @dylan-conway
- @Electroid
- @eriklangille
- @gvilums
- @hugo-syn
- @Jarred-Sumner
- @kaioduarte
- @LukasKastern
- @nektro
- @paperdave
Full Changelog: https://github.com/oven-sh/bun/compare/bun-v1.0.22...bun-v1.0.23