Today, Bun has two main priorities: stability and compatibility with Node.js and Web APIs. In v0.3.0, we've made significant progress toward both of these goals. There are a lot of changes to cover, so we'll start with the highlights.
Note — We're a small team working to make building with JavaScript faster and simpler. If you're interested in joining us, check out our careers page, we're hiring JavaScript, C/C++, and Zig engineers!
To install:
curl -fsSL https://bun.sh/install | bash
To upgrade:
bun upgrade
Stability improvements
Bun now uses 3-5x less memory under load. Previously, Bun wasn't scheduling garbage collection in coordination with the event loop. (See the benchmark here)
Bun now has better formatting for
console.log()
(important for debugging!)Bun has fixed several bugs with text encoding and now uses simdutf for 3x faster
TextEncoder.encodeInto()
Bun now works in more Linux environments, including Amazon Linux 2 and builds for Vercel and Cloudflare Pages. (Previously, you might have seen errors like: "version 'GLIBC_2.29' not found")
There were also many other changes that:
- Increased test coverage for
fetch()
,Bun.spawn()
andBun.spawnSync()
, streaming files, and much more - Improved the bindings between JavaScriptCore and Zig, which has led to many garbage-collector related crashes being fixed
- Fixed various issues when using
WebCrypto
- Fixed encoding and compression issues with
fetch()
- Fixed a data corruption bug in
fetch()
- Fixed a crash when async code was run within
setTimeout()
Node.js compatibility
Bun now supports the following Node.js APIs:
node:child_process
process.stdout
,process.stderr
, andprocess.stdin
Error.captureStackTrace()
(ported from V8 to WebKit)fs.createWriteStream()
andfs.createReadStream()
process.release
New and improved APIs
At a glance, here's what changed:
console
is now anAsyncIterable
FileSystemRouter
is a Next.js-like file-system router for resolving filesbun:ffi
now supports threadsafe callbacks from native code into JavaScriptbun:test
gets 10 new matchers likeexpect().toEqual()
bun:test
supports adone
callback for async testsContent-Range
header support forBun.serve()
when streaming files- Bun's transpiler works with the TypeScript
satisfies
keyword WebSocketServer
gets apublish()
method to publish messages to all clientsArray.fromAsync()
andArrayBuffer.resize()
with thanks to WebKitBun.file().size
now always reports a numberHeaders
gets support for.toJSON()
and.getAll("Set-Cookie")
Bun.deepEquals()
which is used byexpect().toEqual()
console
is now an AsyncIterable
Bun is making it easier to read input from your console with APIs that make sense for non-browsers. console
is now an AsyncIterable
which allows you to stream lines of text from stdin by for await
-looping over console
.
for await (const line of console) {
console.log("Received:", line);
}
To write to stdout, you can also use console.write()
, which skips the formatting of console.log()
.
console.write("hello");
console.write("hello", "world", "\n");
const response = await fetch("https://example.com/");
console.write("Response: ", await response.arrayBuffer());
Automatic package installs from npm
When there is no node_modules
directory, Bun will now automatically install packages from npm on import
. To save disk space, packages are downloaded into the shared global cache directory. For security, postinstall
scripts are not run.
To specify a package version, you can either continue to use package.json
or you can include a range specifier in the import
path. These range specifiers are only supported when there is no package.json
present.
import { renderToReadableStream } from "react-dom@latest/server";
import { serve } from "bun";
serve({
async fetch(req) {
const stream = await renderToReadableStream(
<html>
<body>
<h1>Hello, world!</h1>
</body>
</html>,
);
return new Response(stream);
},
});
Bun continues to support the node_modules
folder, which means this isn't a breaking change. If you want to disable this feature, you can run Bun using the --no-install
flag.
FileSystemRouter
Bun now exposes FileSystemRouter
, a fast API for resolving incoming paths against a file-system router. Once initialized, it can rapidly resolve paths, or a Request
, against the router.
Consider the following Next.js-style pages
directory:
/path/to/project
├── server.ts
├── pages
├── index.tsx
├── settings.tsx
├── blog
│ ├── index.tsx
│ └── [slug].tsx
└── [[...catchall]].tsx
In server.ts
, you can initialize a FileSystemRouter
that points to your pages
directory.
import { FileSystemRouter } from "bun";
const router = new FileSystemRouter({
dir: import.meta.dir + "/pages",
style: "nextjs",
});
Router styles — Currently, the only supported style
is nextjs
. We plan to support others in the future, including the Next.js 13-style app
directory.
You can use the router
instance to resolve incoming requests against your defined pages. This matching is implemented in native code and is much faster than a JavaScript-based router.
router.match("/");
// { filePath: "/pages/index.tsx" }
router.match("/blog");
// { filePath: "/pages/blog/index.tsx" }
router.match("/blog/my-first-post?foo=bar");
// {
// filePath: "/pages/blog/[slug].tsx" }
// params: { slug: "hello-world" },
// query: { foo: "bar" }
// }
Expect matchers in bun:test
More methods on expect()
matchers have been implemented in bun:test
. We've also made performance improvements to expect()
which makes toEqual()
100x faster than Jest. Longer-term, we want bun:test to be an incredibly fast drop-in replacement for companies using Jest & Vitest.
import { test, expect } from "bun:test";
test("new expect() matchers", () => {
expect(1).not.toBe(2);
expect({ a: 1 }).toEqual({ a: 1, b: undefined });
expect({ a: 1 }).toStrictEqual({ a: 1 });
expect(new Set()).toHaveProperty("size");
expect([]).toHaveLength(0);
expect(["bun"]).toContain("bun");
expect(true).toBeTruthy();
expect(Math.PI).toBeGreaterThan(3.14);
expect(null).toBeNull();
});
Note — You can try out the test runner with bun wiptest
.
New methods on Headers
The Headers
class now implements the .getAll()
and .toJSON()
methods. These are both technically non-standard methods, but we think it will make your life easier.
const headers = new Headers();
headers.append("Set-Cookie", "a=1");
headers.append("Set-Cookie", "b=1; Secure");
console.log(headers.getAll("Set-Cookie")); // ["a=1", "b=1; Secure"]
console.log(headers.toJSON()); // { "set-cookie": "a=1, b=1; Secure" }
Resizable ArrayBuffer
and growable SharedArrayBuffer
The ability to .resize()
an ArrayBuffer
and .grow()
a SharedArrayBuffer
has been implemented in WebKit and is now available in Bun. Thanks, WebKit!
const buffer = new ArrayBuffer({
byteLength: 1024,
maxByteLength: 2048,
});
console.log(buffer.byteLength); // 1024
buffer.resize(2048);
console.log(buffer.byteLength); // 2048
Array.fromAsync()
The Array.fromAsync()
method has also been implemented in WebKit and is now available in Bun. It is similar to Array.from()
except it accepts async-iterable inputs. Thanks again, WebKit!
async function* listReleases() {
for (let page = 1; ; page++) {
const response = await fetch(
`https://api.github.com/repos/oven-sh/bun/releases?page=${page}`,
);
const releases = await response.json();
if (!releases.length) {
break;
}
for (const release of releases) {
yield release;
}
}
}
const releases = await Array.fromAsync(listReleases());
Changelog
There were a lot of other changes! Here's the complete list:
- Fixed various memory leaks -
a9aa3e7
,1cce9da
,9090f06
- Implemented
process.release
-901c4f5
- Fixed various bugs with
RegExp
-5398ed5
,#1537
,#1528
,74e87b5
- Fixed an issue with
fs.stat()
-f9f169b
- Prevented
process.stdout
andprocess.stderr
from being closed -8519ff0
- Changed behaviour of
Blob.size
to reportInfinity
for non-files -d5c81b7
- Fixed incorrect
exitCode
fromspawn()
-30e1fe1
- Fixed incorrect transpilation of
export = value
in TypeScript -1cb5a73
- Fixed
spawn()
not being killed when parent process is killed -1f174b9
- Fixed silent error when
port
was not a number -e5b2e3c
- Added a
done
callback to support async functions inbun:test
-c00359a
- Fixed various issues with
process.nextTick()
-fd26d2e
,2eb19a9
- Fixed
console.log(Request)
showing an emptyurl
-cb41d77
- Implemented
AbortSignal.timeout()
-fe4f39f
- Supported wildcard
imports
in package.json -a3dc33c
- Fixed timers keeping the process alive unnecessarily -
f408749
- Fixed an issue when object spreading
process.env
-a6cadce
- Fixed a bug where
Response.arrayBuffer()
would return aUint8Array
-de9a2b9
- Implemented
signalCode
forBun.spawn()
-0617896
- Supported the
Content-Range
header when serving a file -0617896d
- Supported
bin
when usingbun link
-0642cf3
- Improved the output of
console.log()
-c65c320
- Fixed an issue with streams prematurely closing -
d68f44d
- Fixed an issue when using
console.log()
with an emoji -3cb462a
- Fixed an issue when connecting to
localhost
-d90a638
- Fixed
Request.url
not showing the value from theHost
header -da25733
- Fixed memory issues in
HTMLRewriter
wheretext
was not properly cloned -a85826
,904716f
- Fixed an issue when an exception is throw within a
WebSocket
open event -c52ebd9
- Added support for npm packages that do not have a
-
before a pre-release identifier (e.g.1.0.0beta
) -5f5ef81
- Improved performance of IO polling for
Bun.spawn()
andnode:child_process
-#1496
- Fixed infinite IO
write()
on Linux -a78b6f9
- Added support for
fs.rmdir()
-92b7660
- Fixed an issue with gzip decompression when the body was not gzipped -
#1510
- Fixed an issue with
@
in the URL offetch()
-6c01a11
- Fixed an issue with a trailing
/
in the URL offetch()
-4f22c39
- Fixed rare crash during
console.log()
-b0c89ba
- Improved support for
node:http.createServer()
-bf6b174
- Implemented
FileSink.ref()
andFileSink.unref()
-d1a4f4f
- Implemented
console.timeLog()
-f677919
- Fixed
fs.createWriteStream()
-#1433
- Supported TypeScript decorators -
#1445
- Supported JavaScript callbacks using
bun:ffi
-81033c5
- Added a property for JavaScript callbacks to be marked as
threadsafe
usingbun:ffi
-006a2f3
- Fixed various
WebCrypto
issues and crashes -#1448
,#1450
- Fixed
Bun.which()
not handling absolute paths -d6520cd
- Allowed URLs to be used as an argument to
fetch()
-#1460
- Fixed rejected promises not triggering a test failure in
bun:test
-370d9c
- Fixed environment variables being coerced to non-strings -
#1256
- Made
TextDecoder
20% faster for small inputs -160466
- Fixed incorrect value for
os.EOL
-8b0a3c7
- Fixed
Buffer.toString("base64")
sometimes being incorrect -af39313
- Fixed incorrect file permissions when using
bun install
-2c4777
- Supported TypeScript
satisfies
-565996
- Implemented
WebSocketServer.publish()
when invoked outside an event handler -8753c48
- Improved performance of Node.js streams -
#1502
- Fixed
bun:sqlite
truncating numbers to int32 instead of int52 -ce6fc86
- Fixed HTTP status text not matching standards -
949d715
- Fixed
bun:sqlite
not properly handing latin1 characters -c65c320
- Supported manual redirects in
fetch()
-5854d39
- Fixed
fetch()
redirects loosing theport
-f8d9a8b
- Fixed an issue with a malformed
fetch()
response body -b230e7a