We're hiring systems engineers in San Francisco to build the future of JavaScript!
This release fixes 41 bugs (addressing 595 👍). It includes node:http2 server and gRPC server support, ca and cafile support in bun install, Bun.inspect.table, bun build --drop, Promise.try, Buffer.copyBytesFrom, iterable SQLite queries, iterator helpers, and several Node.js compatibility improvements and bugfixes.
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
Support for node:http2
server and gRPC
In this release of Bun, we added support for our most upvoted feature request: HTTP2 server and gRPC support. Bun has supported the node:http2 client since v1.0.13, but we didn't support the server, until now.
In Bun, node:http2
runs 2.4x faster than in Node v23.
In the next version of Bun
— Bun (@bunjavascript) October 17, 2024
node:http2 server support is implemented. For the same code:
Bun v1.1.31: 128,879 req/s (2.4x faster)
Node v23.0.0: 52,785 req/s pic.twitter.com/SIM0I0Td4T
You can use the node:http2
API to create an HTTP2 server.
import { createSecureServer } from "node:http2";
import { readFileSync } from "node:fs";
const server = createSecureServer({
key: readFileSync("privkey.pem"),
cert: readFileSync("cert.pem"),
});
server.on("error", (error) => console.error(error));
server.on("stream", (stream, headers) => {
stream.respond({
":status": 200,
"content-type": "text/html; charset=utf-8",
});
stream.end("<h1>Hello from Bun!</h1>");
});
server.listen(3000);
With HTTP2 support, you can also use gRPC with packages like @grpc/grpc-js
.
const grpc = require("@grpc/grpc-js");
const protoLoader = require("@grpc/proto-loader");
const packageDefinition = protoLoader.loadSync("benchmark.proto", {});
const proto = grpc.loadPackageDefinition(packageDefinition).benchmark;
const fs = require("fs");
function ping(call, callback) {
callback(null, { message: "Hello, World" });
}
function main() {
const server = new grpc.Server();
server.addService(proto.BenchmarkService.service, { ping: ping });
const tls =
!!process.env.TLS &&
(process.env.TLS === "1" || process.env.TLS === "true");
const port = process.env.PORT || 50051;
const host = process.env.HOST || "localhost";
let credentials;
if (tls) {
const ca = fs.readFileSync("./cert.pem");
const key = fs.readFileSync("./key.pem");
const cert = fs.readFileSync("./cert.pem");
credentials = grpc.ServerCredentials.createSsl(ca, [
{ private_key: key, cert_chain: cert },
]);
} else {
credentials = grpc.ServerCredentials.createInsecure();
}
server.bindAsync(`${host}:${port}`, credentials, () => {
console.log(
`Server running at ${tls ? "https" : "http"}://${host}:${port}`,
);
});
}
main();
CA support in bun install
You can now configure CA certificates for bun install
. This is useful when you need to install packages from your company's private registry, or if you want to use self-signed certificate.
[install]
# The CA certificate as a string
ca = "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
# A path to a CA certificate file. The file can contain multiple certificates.
cafile = "path/to/cafile"
If you don't want to change your bunfig.toml
file, you can also use the --ca
and --cafile
flags.
bun install --cafile=/path/to/cafile
bun install --ca="..."
If you are using an existing .npmrc
file, you can also configure CA certificates there.
cafile=/path/to/cafile
ca="..."
bun build --drop
You can use --drop
to remove function calls from your JavaScript bundle. This is useful if you want to remove debug code from your production bundle. For example, if you pass --drop=console
, all calls to console.log
will be removed from your code.
await Bun.build({
entrypoints: ["./index.tsx"],
outdir: "./out",
drop: ["console", "anyIdentifier.or.propertyAccess"],
})
bun build ./index.tsx --outdir ./out --drop=console --drop=anyIdentifier.or.propertyAccess
bun --drop
--drop
also works at runtime.
for (let i = 0; i < 3; i++) {
console.log("hello called!");
}
Before --drop
, you'd see hello called!
3 times.
bun ./index.ts
hello called!
hello called!
hello called!
With --drop=console
, console.log
calls get dropped and you don't see hello called!
anymore.
bun --drop=console ./index.ts
# no output
Bun.inspect.table()
You can now use Bun.inspect.table
to format tabular data into a string. It is similar to console.table
, except it returns a string rather than printing to the console.
console.log(
Bun.inspect.table([
{ a: 1, b: 2, c: 3 },
{ a: 4, b: 5, c: 6 },
{ a: 7, b: 8, c: 9 },
]),
);
// ┌───┬───┬───┬───┐
// │ │ a │ b │ c │
// ├───┼───┼───┼───┤
// │ 0 │ 1 │ 2 │ 3 │
// │ 1 │ 4 │ 5 │ 6 │
// │ 2 │ 7 │ 8 │ 9 │
// └───┴───┴───┴───┘
You can also pass an array of property names to display only a subset of properties.
console.log(
Bun.inspect.table(
[
{ a: 1, b: 2, c: 3 },
{ a: 4, b: 5, c: 6 },
],
["a", "c"],
),
);
// ┌───┬───┬───┐
// │ │ a │ c │
// ├───┼───┼───┤
// │ 0 │ 1 │ 3 │
// │ 1 │ 4 │ 6 │
// └───┴───┴───┘
If you want to enable ANSI colors, you can set the colors
property on the options object.
console.log(
Bun.inspect.table(
[
{ a: 1, b: 2, c: 3 },
{ a: 4, b: 5, c: 6 },
],
{
colors: true,
},
),
);
Iterable SQLite queries
Bun has a built-in SQLite API, that makes it easy to query SQLite databases. Instead of returning an array of rows, you can now return an iterator that yields rows as they are returned from the database.
import { Database } from "bun:sqlite";
const sst = new Database(":memory:");
sst.run("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)");
class User {
id: number;
name: string;
email: string;
get nameAndEmail() {
return `${this.name} <${this.email}>`;
}
}
const rows = sst
.query("SELECT * FROM users")
.as(User)
.iterate() // <--- here
// call the .nameAndEmail property on each row
.map((row) => row.nameAndEmail);
for (const row of rows) {
console.log(row);
}
// prints nothing because sst has no users
You can also call the .iterate()
method on a query to get an iterator that yields rows as they are returned from the database.
for (const row of db.query("SELECT * FROM users").iterate()) {
console.log(row); // { id: 1, name: "Bun" }
}
Thanks to @Skywalker13 for implementing this!
Iterator helpers
In this release of Bun, there are new APIs that make it easier to work with JavaScript iterators and generators. These APIs were introduced in a TC39 proposal, and are now available in Safari and Chrome. Thanks to the WebKit team for implementing these APIs, especially @sosukesuzuki and @shvaikalesh!
iterator.map(fn)
Returns an iterator that yields the results of the fn
function applied to each value of the original iterator, similar to Array.prototype.map
.
function* range(start: number, end: number): Generator<number> {
for (let i = start; i < end; i++) {
yield i;
}
}
const result = range(3, 5).map((x) => x * 2);
result.next(); // { value: 6, done: false }
iterator.flatMap(fn)
Returns an iterator that yields the values of the original iterator, but flattens the results of the fn
function, similar to Array.prototype.flatMap
.
function* randomThoughts(): Generator<string> {
yield "Bun is written in Zig";
yield "Bun runs JavaScript and TypeScript";
}
const result = randomThoughts().flatMap((x) => x.split(" "));
result.next(); // { value: "Bun", done: false }
result.next(); // { value: "is", done: false }
// ...
result.next(); // { value: "TypeScript", done: false }
iterator.filter(fn)
Returns an iterator that only yields values that pass the fn
predicate, similar to Array.prototype.filter
.
function* range(start: number, end: number): Generator<number> {
for (let i = start; i < end; i++) {
yield i;
}
}
const result = range(3, 5).filter((x) => x % 2 === 0);
result.next(); // { value: 4, done: false }
iterator.take(n)
Returns an iterator that yields the first n
values of the original iterator.
function* odds(): Generator<number> {
let i = 1;
while (true) {
yield i;
i += 2;
}
}
const result = odds().take(1);
result.next(); // { value: 1, done: false }
result.next(); // { done: true }
iterator.drop(n)
Returns an iterator that yields all values of the original iterator, except the first n
values.
function* evens(): Generator<number> {
let i = 0;
while (true) {
yield i;
i += 2;
}
}
const result = evens().drop(2);
result.next(); // { value: 4, done: false }
result.next(); // { value: 6, done: false }
iterator.reduce(fn, initialValue)
Reduces the values of an iterator with a function, similar to Array.prototype.reduce
.
function* powersOfTwo(): Generator<number> {
let i = 1;
while (true) {
yield i;
i *= 2;
}
}
const result = powersOfTwo()
.take(5)
.reduce((acc, x) => acc + x, 0);
console.log(result); // 15
iterator.toArray()
Returns an array that contains all the values of the original iterator. Make sure that the iterator is finite, otherwise this will cause an infinite loop.
function* range(start: number, end: number): Generator<number> {
for (let i = start; i < end; i++) {
yield i;
}
}
const result = range(1, 5).toArray();
console.log(result); // [1, 2, 3, 4]
iterator.forEach(fn)
Calls the fn
function on each value of the original iterator, similar to Array.prototype.forEach
.
function* randomThoughts(): Generator<string> {
yield "Bun is written in Zig";
yield "Bun runs JavaScript and TypeScript";
}
const result = randomThoughts().forEach((x) => console.log(x));
// Bun is written in Zig
// Bun runs JavaScript and TypeScript
iterator.find(fn)
Returns the first value of the original iterator that passes the fn
predicate, similar to Array.prototype.find
. If no such value exists, it returns undefined
.
function* range(start: number, end: number): Generator<number> {
for (let i = start; i < end; i++) {
yield i;
}
}
const result = range(0, 99).find((x) => x % 100 === 0);
console.log(result); // undefined
Promise.try
The Promise.try
method is similar to Promise.resolve
, but it also works with synchronous functions.
Promise.try(() => {
return 1 + 1;
}).then((result) => {
console.log(result); // 2
});
This is a stage4 TC39 proposal (i.e. it has graduated from proposal to standardized ECMAScript). Thanks to @rkirsling for implementing this!
9x faster Error.captureStackTrace
In the next version of Bun
— Jarred Sumner (@jarredsumner) October 14, 2024
Error.captureStackTrace(err) gets 9x faster pic.twitter.com/Ej7XW8KPNk
expect(fn).toThrow()
prints the return value if it does not throw
Before:
And now:
Thanks to @nektro for implementing this!
Warn on first idle request timeout
In the next version of Bun
— Jarred Sumner (@jarredsumner) October 11, 2024
Added a warning when a request times out automatically in Bun.serve() without explicitly passing idleTimeout and without using AbortSignal pic.twitter.com/krMmUNi14x
New error: Mismatched JSX closing tag
Previously, the following code would not throw an error, even though it's invalid JSX:
const Oopsie = () => {
return (
<h1>
That should be an h1! Not a div.
</div>
);
};
We should have always thrown an error here, but we were not. Now we do:
❯ bun build Oopsie.tsx
5 | </div>
^
error: Expected closing JSX tag to match opening tag "<h1>"
at Oopsie.tsx:5:7
3 | <h1>
^
note: Opening tag here:
at Oopsie.tsx:3:6
Thanks to @DonIsaac for fixing this!
Node.js compatibility
New: Buffer.copyBytesFrom(view[, offset[, length]])
You can now use the Buffer.copyBytesFrom
method to create a new Buffer
by copying bytes from a typed array or DataView.
const u16 = new Uint16Array([0, 0xffff]);
const buf = Buffer.copyBytesFrom(u16, 1, 1);
u16[1] = 0;
console.log(buf.length); // 2
console.log(buf[0]); // 255
console.log(buf[1]); // 255
Thanks to @nektro for implementing this!
Fixed: fs.open()
We fixed various issues in fs.open()
, so it more closely matches Node.js's behavior. Thanks to @nektro for fixing this!
Fixed: Node-API bugs
We've been working on fixing bugs in Bun's implementation of Node-API. Thanks to @190n for fixing these bugs!
napi_create_empty_array
does not crash when the array is too largenapi_create_empty_array
uses empty slots, instead ofundefined
valuesnapi_get_value_int32
uses the same bitwise conversions as in JavaScriptnapi_get_value_int64
now matches Node.js's behaviornapi_throw_error
andnapi_create_error
are now tested and don't sometimes crash
Fixed: Setting UV_THREADPOOL_SIZE
There are some Node.js packages that set the UV_THREADPOOL_SIZE
environment variable to control the number of threads used by libuv. While Bun only uses libuv on Windows, and only for some APIs, it will now respect this environment variable.
Fixed: Buffer.from
JSON serialization
When you call JSON.stringify
on a Buffer, it adds {type: "Buffer", value: [...buffer contents]}
to the JSON. This already worked, but Buffer.from would not accept this JSON. Now it does.
const buf = Buffer.from([1, 2, 3]);
JSON.stringify(buf); // "{"type":"Buffer","data":[1,2,3]}"
Buffer.from(JSON.parse('{"type":"Buffer","data":[1,2,3]}'))[0] === 1; // works
Previously, this would throw:
TypeError: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object.
at [file]:3:20
Thanks to @nektro for fixing this!
Fixed: node:child_process.SpawnResult
.stderr
should be null if .stdio
is not "pipe"
import child_process from "node:child_process";
const res = child_process.spawn("ls", {
stdio: "inherit",
});
console.log(res.stderr); // now 'null'
This unblocked use of the tinyexec
package.
Thanks to @nektro for fixing this!
Bugs squashed
Fixed: Format of console.table
with numeric keys
We fixed a bug where console.table
would format with incorrect spacing when the keys were numeric. Thanks to @pfgithub for fixing this!
console.table({test: {"10": 123, "100": 154}});
// Before
┌──────┬─────┬─────┐
│ │ 10 │ 100 │
├──────┼─────┼─────┤
│ test │ │ │
└──────┴─────┴─────┘
// After
┌─────────┬─────┬─────┐
│ (index) │ 10 │ 100 │
├─────────┼─────┼─────┤
│ test │ 123 │ 154 │
└─────────┴─────┴─────┘
Fixed: Crash using Bun.color
In a recent release, we introduced Bun.color
, an API that allows you to parse and transform colors in different formats, including css, ansi, hex, rgb, hsl, and more.
We fixed a bug where Bun.color
would crash if you did not pass it a format string.
Fixed: Regression with instanceof
An instanceof
regression introduced in Bun v1.1.30 through a WebKit upgrade has been fixed. The bug could be caused by JavaScript code storing the output value of instanceof
in the same variable as the input. The error message would look like:
error: [BUG] Unreachable
at ... (input.js:2247:47)
This bug was most often seen when using sass
with source maps enabled.
Fixed thanks to @hyjorc1!
Fixed: Transpiler bug with using
and generators
We fixed a bug where a variable declaration before a using
statement would cause the transpiler to hoist a generator outside the scope of the using
declaration. This would cause a ReferenceError
, but has now been fixed thanks to @snoglobe.
Fixed: File permissions with bun install
Some npm packages are published with incorrect file permissions, we added a check in Bun to ensure that files extracted from an npm package are at-least readable. This matches the behavior of npm install
.
Fixed: Missing metadata publishing with bun publish
A bug was fixed where bun publish
was sending publish requests without important package metadata, including dependencies and binaries from bin
or directories.bin
. This would cause published packages to install incorrectly due to missing transitive dependencies and binaries in node_modules/.bin
.
Fixed: empty string argument for fs.mkdir
An integer overflow caused by passing an empty string to fs.mkdir
has been fixed. This would only happen when the recursive
option was set to true
.
const fs = require("fs");
fs.mkdirSync("", { recursive: true }); // throws ENOENT
Fixed: fetch keepalive
option
The keepalive
option for fetch
was previously ignored. Now it disables the "Connection: keep-alive" header from being sent by default.
const res = await fetch("https://example.com", { keepalive: false });
console.log(res.headers.get("connection")); // "close"