Bun v1.1.21 is here! This release fixes 72 bugs (addressing 63 👍). 30% faster fetch() decompression, New --fetch-preconnect flag, improved Remix support, Bun gets 4 MB smaller on Linux, bundle packages excluding dependencies, many bundler fixes and node compatibility improvements.
We're hiring systems engineers in San Francisco to build the future of JavaScript!
Previous releases
v1.1.20
&v1.1.19
Fixes 54 bugs (addressing 248 👍). JavaScript gets faster on Windows. Raspberry Pi 4 support._auth
support in.npmrc
.bun install
preserves package.json indentation.aws-cdk-lib
support. Memory leak fixes innew Response(request)
andfs.readdir
. Several reliability improvements.v1.1.18
Fixes 55 bugs (addressing 493 👍). Support for npmrc. Improved constant folding & enum inlining. Improved console.log output for functions. Fixes patching dependencies in workspaces. Fixes several bugs inbun install
when linking binaries. Fixes a crash indns.lookup
, and normalizes DNS errors to better match Node. Fixes an assertion failure inBun.escapeHTML
. Upgrades JavaScriptCore.v1.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
fetch() decompresses 30% faster
Decompressing fetch()
response bodies compressed with gzip or deflate get 30% faster on Linux x64, thanks to libdeflate.
In the next version of Bun
— Jarred Sumner (@jarredsumner) July 27, 2024
fetch() decompresses gzip'd data 30% faster, thanks to libdeflate. pic.twitter.com/obCjWo2fHv
We've also enabled libdeflate with the websocket server, to make decompressing small messages faster.
New: --fetch-preconnect
warms up a connection ahead of time
We've added a new flag --fetch-preconnect=<url> <file>
that starts up an HTTP request to the given URL before any code is executed.
bun --fetch-preconnect='https://example.com' ./index.ts
For many production services, the first thing a script in Bun does is send HTTP requests to a server using fetch
or node:http
. The slowest part of many network requests is the initial connection setup, which can involve DNS lookups, TCP connection establishment, and TLS negotiation. Sometimes this takes 100ms before sending the HTTP request.
This work is network-bound. What if you could start up that connection before any code is executed, so that by the time the rest of your code runs, the connection is already established? This is what --fetch-preconnect
does. It's a lot like <link rel="preconnect">
in HTML, but for server-side JavaScript.
Along the way, we've also added a JavaScript API to do the same thing:
fetch.preconnect(url: string): void;
This is useful for serverless environments and databases that run on top of fetch
or node:http
.
bun --fetch-preconnect=https://example.com ./hey.mjs
[8.37ms] fetch(https://example.com)
❯ bun hey.mjs # Without preconnect
[51.92ms] fetch(https://example.com)
❯ node hey.mjs
fetch(https://example.com): 68.417ms
❯ cat hey.mjs
│ File: hey.mjs
1 │ if (globalThis.Bun) {
2 │ await Bun.sleep(1000);
3 │ } else {
4 │ await new Promise(resolve => setTimeout(resolve, 1000));
5 │ }
6 │
7 │ console.time("fetch(https://example.com)");
8 │ await fetch("https://example.com");
9 │ console.timeEnd("fetch(https://example.com)");
Improved Remix support
We fixed a bug where overriding globals like Request
or Response
could cause issues with node:http
. Internally, Bun's implementation of node:http
uses Bun.serve()
which expects the user to return the native Response
object.
Remix and other frameworks globally polyfill Request
and Response
, and Bun's node:http
was previously using the global Request
and Response
objects. We fixed this by always using the native Request
and Response
objects in node:http
.
We also fixed similar issues in Bun's polyfill of node-fetch
and undici
.
If you experienced the following error, it has now been fixed:
// error: Expected a Response object, but received 'Response {
// [Symbol(Body internals)]: {
// body: ReadableStream {
// ...
We also added an integration test for Remix, so we can continue to ensure compatibility in the future.
bun install retries on HTTP 500 errors
In this release, bun install
will now retry on network errors with an HTTP status code > 499.
We've seen reports from users and from our own CI where the official npm registry returned HTTP 500 errors, which would cause bun install
to fail. Now, bun install
will retry on these errors, which should help compensate for temporary issues with the npm registry (reducing spurious CI failures).
4 MB smaller on Linux
In this release, we've shrunk Bun's Linux x64 binary size by 4 MB, which makes it 23% smaller than the official Node.js v22.5.1 executable.
Binary Size | Version |
---|---|
92 MB | Bun v1.1.21 |
96 MB | Bun v1.1.20 |
97 MB | Bun v1.1.19 |
97 MB | Bun v1.1.18 |
114 MB | Node.js v22.5.1 |
1ms faster on Linux
We've also improved Bun's startup time on Linux by 1ms.
hyperfine "bun --reivison" "bun-1.1.20 --revision"
Benchmark 1: bun --revision
Time (mean ± σ): 1.4 ms ± 0.2 ms [User: 0.9 ms, System: 0.4 ms]
Range (min … max): 1.2 ms … 2.6 ms 1143 runs
Benchmark 2: bun-1.1.20 --revision
Time (mean ± σ): 2.3 ms ± 0.2 ms [User: 1.1 ms, System: 1.1 ms]
Range (min … max): 2.2 ms … 5.0 ms 1099 runs
These size & start time improvements came from a combination of linker flags, compiler flags, link-time optimization and a few small code changes.
Bundle packages excluding dependencies with bun build --packages=external
You can now control if package dependencies are included in your bundle or not. If the import does not start with .
, ..
or /
, then it is considered a package.
bun build ./index.ts --packages external
You can also use this in the JavaScript API:
await Bun.build({
entrypoints: ["./index.ts"],
packages: "external",
});
This is useful when bundling libraries. It lets you reduce the number of files your users have to download, while continuing to support peer or external dependencies.
Thanks to @zpix1 for implementing this feature!
Improved hashes for code-splitting in bun build
Previously, Bun would generated "isolated" hashes for files during code-splitting, so it can be done in parallel. However, this would mean that if an imported file was changed, the hash of the file that owns the import would not change.
const other = await import('./second');
export const second = 1;
This has been fixed, and hashes are also shorter by switching to a base32 encoding, similar to how esbuild
does it.
Thanks to @paperdave for working on this!
Fixed: Import resolves as .mjs
when file is .mts
We fixed a bug where a .mjs
import would not resolve to a .mts
file.
import "./foo.mjs";
export const foo = 1;
This has now been fixed thanks to @190n!
Fixed: Crash with Bun.write
on Windows
We fixed a bug in Bun.write()
on Windows, where Bun could crash after creating the directory for a file that didn't exist.
import { write, file } from "bun";
const txt = file("/does/not/exist/example.txt");
await write(txt, "hello world");
Fixed: setSystemTime
would not work with a number
There was a bug where using setSystemTime
from the bun:test
module would not work with a number, instead of a Date
object.
import { test, expect, setSystemTime } from "bun:test";
test("setSystemTime", () => {
const future = Date.now() + 1000;
setSystemTime(future);
expect(Date.now()).toBe(future);
// Before: <test failed>
// After: <test passed>
});
Thanks to @cirospaciari for fixing this bug!
Fixed: Crash when merging nested TypeScript namespaces
There was a bug in Bun where a TypeScript namespaces could crash Bun. This was an edge-case in Bun's transpiler where it was not properly handling when a function would merge with a class or namespace.
namespace X {
export function Y(): void {}
export namespace Y {
export const Z = 1;
}
}
Thanks to @paperdave for fixing this bug!
This has now been fixed thanks to @190n!
Fixed: Crash with aborted request after server closed
We fixed a rare crash in Bun.serve()
that could occur if a Request
was manually aborted after the the server was closed before the request finished sending the body.
Thanks to @cirospaciari for fixing this bug!
Fixed: Crash with empty value in CryptoHasher.update()
We fixed a bug in CryptoHasher.update()
where an empty value would cause a crash.
import { CryptoHasher } from "bun";
const hasher = new CryptoHasher("sha1");
hasher.update(); // <crash>
Thanks to @cirospaciari for fixing this bug!
Fixed: Crash while printing error stacks
A crash could occur when printing stack traces in Bun. This was caused by not correctly checking if the stackframe came from a JavaScript function or from a native function.
Thanks to @nektro for fixing this bug!
Fixed: using expect.any()
with expect.toThrow()
The expected value for expect.toThrow()
can now use asymmetric matcher expect.any()
. The following code will work as expected:
test("toThrow asymmetric matcher", () => {
expect(() => {
throw new Error("error!");
}).toThrow(expect.any(Error)); // passes
});
Thanks to @ippsav!
Fixed a crash with the return value of test.each
in bun test
There was a bug in bun test
where a null pointer could be used for the return value of test.each
, resulting in a crash if the value was used in JavaScript. Now, test.each
will return undefined
.
console.log(test.each([1, 2])("test.each %d", () => {})); // Before: <crash>, After: undefined
Thanks to @dylan-conway!
Fixed: Code coverage on Windows now excludes node_modules
We fixed a bug in bun test --coverage
where the code coverage report would include files from node_modules
on Windows. This was caused by only checking /
as the path separator, when on Windows it may either be \
or /
.
Thanks to @dariushalipour for fixing this bug!
Node.js compatibility improvements
Fixed: Silent errors in node:http request 'data' callback
A bug causing errors to be silent in the data
callback of a node:http
client request has been fixed.
Previously, the following code would not error:
const { request } = require("node:http");
const req = request("http://www.google.com", (res) => {
res.on("data", (chunk) => {
throw new Error("oopsie");
});
}).end();
Now, it correctly throws an error:
1 | const { request } = require("node:http");
2 | const req = request("http://www.google.com", (res) => {
3 | res.on("data", (chunk) => {
4 | throw new Error("oopsie");
^
error: oopsie
at silent.js:4:15
at emit (node:events:180:48)
at addChunk (node:stream:2029:22)
at readableAddChunk (node:stream:1983:30)
at node:http:136:44
Bun v1.1.21 (macOS arm64)
Fixed: Readable.fromWeb
stopped early with fetch()
streams
We fixed a bug where Readable.fromWeb
when passed a ReadableStream
from fetch()
would stop reading early in some cases if the stream was not finished being downloaded yet.
import { Readable } from "node:stream";
const res = await fetch("https://bun.sh");
const stream = Readable.fromWeb(res.body);
stream.on("data", (chunk) => {
console.log("Received", chunk.length);
});
New:
❯ bun chunk.js
Received 16384
[... more chunks ...]
Received 3883
Previously:
❯ bun-1.1.20 chunk.js # Before
This bug specifically impacted fetch()
's ReadableStream.
Fixed: Allow undefined
extension in path.basename()
We fixed a compatibility bug in path.basename()
where it would not allow undefined
as the extension.
import path from "path";
path.basename("foo", undefined);
path.posix.basename("bar", undefined);
path.win32.basename("baz", undefined);
If you encountered the following error, it has now been fixed:
> 2 | path.posix.basename("bar", undefined);
^
TypeError: "ext" property must be of type string, got undefined
code: "ERR_INVALID_ARG_TYPE"
Thanks to @190n!
Fixed: Unref in node:dgram
before socket is connected
We fixed a bug in node:dgram
where would not properly unref the UDP socket, if it was done before the socket was connected.
import dgram from "node:dgram";
const socket = dgram.createSocket("udp4");
socket.unref(); // <would not unref>
Thanks to @190n for fixing this bug!
Fixed: napi_threadsafe_function
would keep the process alive after finalizing
We fixed a regression in Node-API where after a napi_threadsafe_function
was finalized, it would continue to keep the process alive. Thanks to @dylan-conway for fixing this!
...
napi_threadsafe_function tsfn;
...
napi_release_threadsafe_function(tsfn, napi_tsfn_release);
// `tsfn` will allow the process to exit if all threads have released it.
...
Fixed: process.exitCode should be undefined until process.exit() is called
In Node.js, process.exitCode
is undefined until either:
process.exitCode
is set manuallyprocess.exit()
is called
We fixed a bug in Bun where process.exitCode
would be set to 0
before process.exit()
was called.
console.log(process.exitCode); // Before: 0, After: undefined
Thanks to @nektro for fixing this bug!
Fixed: Crash in node:zlib brotli decompression
A crash could occur when synchronously decompressing a brotli stream composed of many chunks over a long period of time. This has been fixed. The crash was caused by not properly keeping the value from JavaScript alive while the decompression was happening.
Thanks to @dylan-conway for fixing this bug!
Bundler fixes
Slightly smaller bundles
In the next version of Bun
— dave caruso (@thepaperdave) July 24, 2024
Bun's bundler gets slightly smarter at dead-code elimination pic.twitter.com/nUoNbVgM8J
Fixed: Crash in bun build
when bundling with external property access
We fixed a bug in bun build
where there could be a crash when a module used property access on an imported external module.
This has been fixed thanks to @paperdave!
Fixed: Trailing slash in require()
using bun build
We fixed a bug where a require()
call with a trailing slash would not work when using bun build
.
To match Node.js, if you try to require process/
, it should resolve to node_modules/process/...
. Previously, Bun was confused and resolved to the built-in node:process
module.
var process2 = require("process/");
// Error: Cannot find module 'process/'
This has now been fixed thanks to @paperdave!
Fixed: import with rope strings crash
There was a bug in Bun where a non-literal value in with
option on import()
would crash Bun. This was because Bun's transpiler was not properly visiting the with
option.
import("./foo", { with: "text" }); // would work
import("./foo", { with: "te" + "xt" }); // would crash
This has now been fixed, thanks to @paperdave!
Fixed: Class hoisting bug
A bug causing the following error has been fixed:
TypeError: The superclass is not a constructor.
When a class declaration has side effects, it cannot be hoisted. We fixed a bug where Bun's bundler would hoist a class declaration with side effects.
async function hi() {
const { default: MyInherited } = await import('./other.js');
const myInstance = new MyInherited();
console.log(myInstance.greet())
}
hi();
const MyReassignedSuper = class MySuper {
greet() {
return 'Hello, world!';
}
};
class MyInherited extends MyReassignedSuper {};
export default MyInherited;
This produces the correct output now:
- class MyInherited extends MyReassignedSuper {
- }
- var MyReassignedSuper, other_default;
- MyReassignedSuper = class MySuper {
- greet() {
- return "Hello, world!";
- }
+ var MyReassignedSuper = class MySuper {
+ greet() {
+ return "Hello, world!";
+ }
+ }, MyInherited, other_default;
+ MyInherited = class MyInherited extends MyReassignedSuper {
+ };
+ other_default = MyInherited;
// ...
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, {
get: all[name],
enumerable: true,
configurable: true,
set: (newValue) => all[name] = () => newValue
});
};
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
// other.js
var exports_other = {};
__export(exports_other, {
default: () => other_default
});
var MyReassignedSuper = class MySuper {
greet() {
return "Hello, world!";
}
}, MyInherited, other_default;
var init_other = __esm(() => {
MyInherited = class MyInherited extends MyReassignedSuper {
};
other_default = MyInherited;
});
// entry.js
async function hi() {
const { default: MyInherited2 } = await Promise.resolve().then(() => (init_other(), exports_other));
const myInstance = new MyInherited2;
console.log(myInstance.greet());
}
hi();
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, {
get: all[name],
enumerable: true,
configurable: true,
set: (newValue) => all[name] = () => newValue
});
};
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
// other.js
var exports_other = {};
__export(exports_other, {
default: () => other_default
});
class MyInherited extends MyReassignedSuper {
}
var MyReassignedSuper, other_default;
var init_other = __esm(() => {
MyReassignedSuper = class MySuper {
greet() {
return "Hello, world!";
}
};
other_default = MyInherited;
});
// entry.js
async function hi() {
const { default: MyInherited2 } = await Promise.resolve().then(() => (init_other(), exports_other));
const myInstance = new MyInherited2;
console.log(myInstance.greet());
}
hi();
Internal: LLVM 18 on macOS & Windows
We've upgraded from LLVM 16 to LLVM 18 on macOS and Windows. This let us add more debug assertions on macOS which help catch some bugs earlier.