Bun v1.1.22 is here! This release fixes 79 bugs (addressing 93 👍). Express is now 3x faster in Bun, ES modules load faster on Windows, 10% faster Bun.serve() at POST requests, source maps in bun build --compile
. Memory usage reductions to --hot
, Bun.serve(), and imports. Uint8Array.toBase64()
, Uint8Array.toHex()
, and lots of Node.js compatibiltiy improvements and bugfixes.
We're hiring systems engineers in San Francisco to build the future of JavaScript!
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
Express is now 3x faster in Bun
In this release, we made performance and compatibility improvements to Bun's node:http
implementation. This increased express
request throughput by 50% compared to Bun v1.1.21, which means its 3x faster in Bun than Node.js running the same code.
ES Modules load up to 4x faster on Windows
To make running TypeScript, JSX, ES modules, and CommonJS all just work in Bun, we transpile every file on the fly. This inherently has a performance cost.
This release brings concurrent transpilation support to Bundows, which makes loading ES modules faster.
async function benchmark(name) {
console.time(name);
await import(name);
console.timeEnd(name);
}
await benchmark("lodash-es");
In this benchmark, we're running the above code and measuring the time it to import a popular package, like lodash-es
.
Runtime | Time (ms) |
---|---|
Bun v1.1.22 | 39 |
Bun v1.1.21 | 82 |
Node.js v22.4.1 | 186 |
This optimization previously was only supported on Bun for Linux & macOS, but now it is also supported on Windows.
10% higher throughput POST requests in Bun.serve()
If you code does not read the body of an incoming Request
, there is a 10% performance improvement from the last release of Bun.
In the next version of Bun
— Bun (@bunjavascript) July 31, 2024
Bun.serve() gets 10% higher req/s on POST requests that ignore the body. pic.twitter.com/OxkIGwYaWn
Less memory usage
bun --hot
uses less memory
In this release, Bun uses 2x less memory when you use bun --hot
to reload code.
In the next version of Bun
— Jarred Sumner (@jarredsumner) August 1, 2024
bun --hot uses less memory after many runs
left: Bun v1.1.22 (new)
right: Bun v1.1.21 (old) pic.twitter.com/eDl5iqZsme
Previously, we were not always releasing the source code for old versions of modules. This also applies if your code or dependencies modifies require.cache
, which is a common pattern for reloading code.
Importing modules that are garbage collected uses less memory
We fixed a bug where importing or requiring a module that is garbage collected would keep a reference to that module's source code.
This was also caused when a module would throw an error, which would keep a reference to the module's source code.
In the next version of Bun
— Jarred Sumner (@jarredsumner) August 1, 2024
require()'ing or import'ing large modules that throw an error uses less memory pic.twitter.com/ruMqFNDzqM
Memory reductions
We also patched various memory leaks, including when:
- requiring a CommonJS module
- computing line offsets in source maps
- transpiling more than 64 files, concurrently
- creating a large number of
setTimeout()
orsetInterval()
timers
Better stack traces
We fixed a bug in how error.stack
traces are rendered in Bun. Sometimes the source URL of a call site would be empty, which would cause the stack trace to be slightly different than what most tools expect to receive. Now, Next.js renders stack traces properly.
Sourcemaps in single-file executables
bun build --compile
lets you generate a single-file executable that bundles your source code, dependencies, and Bun into a single file you can deploy to production without needing other dependencies.
In this release of Bun, --sourcemap
is now properly supported in single-file executables. This gives you better stack traces for debugging.
bun build cli.ts --compile --sourcemap
Before:
/tmp
❯ bun-1.1.21 build --compile --sourcemap errory.ts
[3ms] bundle 1 modules
[110ms] compile errory
/tmp
❯ ./errory
2 | // @bun
3 | // errory.ts
4 | throw new Error("hey " + n);
^
error: hey 42
at hey (/$bunfs/root/errory:4:9)
at /$bunfs/root/errory:6:4
Bun v1.1.21 (macOS arm64)
After:
❯ bun build --compile --sourcemap errory.ts
[1ms] bundle 1 modules
[101ms] compile errory
/tmp
❯ ./errory
1 | function hey(n: number) {
2 | throw new Error("hey " + n);
^
error: hey 42
at hey (errory.ts:2:9)
at errory.ts:4:1
Bun v1.1.22-canary.104+c527058f1 (macOS arm64)
To reduce the memory and file-size cost of sourcemaps, we compress the input source code with zstd
and serialize into a custom binary format.
Node.js compatibility
util.getSystemErrorName()
Bun now supports the util.getSystemErrorName()
function from the node:util
module. This allows you to return the name of the error code for an error object.
import { readFileSync } from "node:fs";
import { getSystemErrorName } from "node:util";
try {
readFileSync("/does/not/exist");
} catch (error) {
console.log(getSystemErrorName(error)); // "ENOENT"
}
Thanks to @nektro for implementing this feature!
util.aborted()
Bun now supports the util.aborted()
function from the node:util
module. This allows you to listen to an AbortSignal
and receive a promise that resolves when the signal is aborted.
import { aborted } from "node:util";
const controller = new AbortController();
const promise = aborted(controller.signal);
promise.then(() => {
console.log("Aborted!");
});
events.getEventListeners()
Bun also now supports the events.getEventListeners()
function for EventTarget
objects from the node:events
module. This allows you to get all listeners for a given event name on a given emitter.
import { getEventListeners } from "node:events";
const emitter = new EventTarget();
emitter.addEventListener("foo", function foo() {});
console.log(getEventListeners(emitter, "foo"));
// [ [Function: foo] ]
Previously, this was only supported for EventEmitter
objects from the node:events
module.
NODE_EXTRA_CA_CERTS
Bun now supports the NODE_EXTRA_CA_CERTS
environment variable, which allows you to specify a file containing one or more CA certificates to use when verifying TLS certificates. This works for the runtime, bun install
, and everything else in Bun that makes HTTPS requests.
NODE_EXTRA_CA_CERTS=ca.pem bun install
NODE_EXTRA_CA_CERTS=another.pem bun run script.ts
Fixed: Support async interators in fs.promises.writeFile()
Node.js has undocumented support for async iterators in fs.promises.writeFile()
. This was relied upon by various packages, and we decided to support it in Bun.
import { writeFile } from "node:fs/promises";
const iterator = async function* () {
yield "Hello";
yield "World";
};
await writeFile("hello.txt", iterator);
Fixed: Bun now exits with 1
if process.on("exit")
throws
We fixed a bug where Bun would not propagate the right exit code if the exit
or beforeExit
event listeners threw an error.
process.on("exit", () => {
throw new Error("Oh no!");
});
This has been fixed thanks to @nektro!
Fixed: Worker
constructor would misinterpret eval
property
There was a bug where the Worker
constructor would misinterpret the eval
property, when being explicitly defined. This was a regression that was introduced in Bun v1.1.13, and has now been fixed.
const worker = new Worker("console.log('hello!')", { eval: false });
// Before: hello!
// After: BuildMessage: ModuleNotFound
Thanks to @billywhizz for fixing this bug!
Fixed: os.freemem()
now works on macOS
This was previously already implemented on Linux and Windows so this API now works on all platforms.
This has been fixed thanks to @nektro!
Web APIs
Uint8Array.toBase64()
Bun now has support for base64 encoding and decoding methods on Uint8Array
. This was implemented in WebKit, which followed the TC39 stage 3 proposal for these additions.
Uint8Array.prototype.toBase64()
converts aUint8Array
to a base64 stringUint8Array.prototype.fromBase64()
converts a base64 string to aUint8Array
new Uint8Array([1, 2, 3, 4, 5]).toBase64(); // "AQIDBA=="
Unit8Array.fromBase64("AQIDBA=="); // [1, 2, 3, 4, 5]
These are Web standard alternatives to ubiquitous the Buffer.prototype.toString("base64")
and Buffer.from(string, "base64")
patterns in Node.js.
Uint8Array.toHex()
These methods were also added for converting Uint8Array
to and from hex strings.
Uint8Array.prototype.toHex()
converts aUint8Array
to a hex stringUint8Array.prototype.fromHex()
converts a hex string to aUint8Array
new Uint8Array([1, 2, 3, 4, 5]).toHex(); // "0102030405"
Unit8Array.fromHex("0102030405"); // [1, 2, 3, 4, 5]
Thanks to @dcrousso and @constellation for implementing these features in WebKit!
bun build
Converting require.main === module
to import.meta.main
Previously, using require.main === module
would mark the module as CommonJS (since it uses module
). Now, Bun rewrites this into import.meta.main
, meaning you can use this pattern alongside import statements.
import * as fs from "fs";
if (typeof require !== "undefined" && require.main === module) {
console.log("main!", fs);
}
1 | import "fs";
^
error: Cannot use import statement with CommonJS-only features
at /index.js:1:8
note: Try require("fs") instead
note: This file is CommonJS because 'module' was used
This also fixes support for require.main === module
in single-file executables using CommonJS modules and bun build --compile
.
Thanks to @paperdave for implementing these features!
--ignore-dce-annotations
Some JavaScript tools support special annotations that can influence during dead-code elimination. For example, the @__PURE__
annotation tells bundlers that a function call is pure (regardless of if it actually is), and that the call can be removed if it is not used.
let button = /* @__PURE__ */ React.createElement(Button, null);
Bundling or running the above code will result in an empty file, since the button
variable is not used.
Sometimes, a library may include incorrect annotations, which can cause Bun to remove side effects which were needed. To workaround these issue, you can use the --ignore-dce-annotations
flag when running bun build
to ignore all annotations. This should only be used if dead-code elimination breaks bundles, and fixing the annotations should be preferred to leaving this flag on.
Thanks to @paperdave for implementing this feature!
Fixed: Assignment to module.exports
in bundles
When bundling CommonJS modules, Bun tries to convert module.exports
into exports
if possible. This helps minification, but assigning to either module.exports
or exports
must undo optimization. This did not happen for instances of module.exports
textually before the assignment, which is important for functions.
function main() {
console.log(module.exports);
}
module.exports = 123;
main();
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
// input.ts
var require_input = __commonJS((exports, module) => {
function main() {
- console.log(exports);
+ console.log(module.exports);
}
module.exports = 123;
main();
});
export default require_input();
Thanks to @paperdave for fixing this!
Fixed: Minifying await import("react-dom/server")
A bug causing the following error when using --minify-identifier
on a package that dynamically imports react-dom
has been fixed:
SyntaxError: Cannot declare an imported binding name twice
For some packages like React, Bun applies extra optimizations to convert CommonJS into ESM. In this conversion, the generated import statements had the wrong scope assigned, causing the minifier to two import statements the same minified variable name.
Fixed bugs
We spend a lot of time fixing bugs in Bun. That's because our top priority is to support Bun in production.
We fixed 180 bugs in July pic.twitter.com/KwXyZy9sTG
— Bun (@bunjavascript) August 2, 2024
If you experience a bug that we haven't fixed, please file an issue or upvote an existing issue on GitHub so we can prioritize it. It helps if you are able to provide a minimal reproducible example.
Missing preview with uncaught error in Bun.serve()
When you pass development: true
to Bun.serve()
, we enable a builtin error handler that shows a preview of the error when an uncaught exception is thrown in the handler.
We fixed a bug where the error preview would not be shown if an uncaught exception was thrown in Bun.serve()
. This was a regression that was introduced in Bun v1.1.9, and has now been fixed.
TextDecoder
not properly decoding 192 or 193 correctly
We fixed a bug where TextDecoder
would not properly decode UTF-8 sequences that started with 192
or 193
.
const decoder = new TextDecoder();
const bytes = new Uint8Array([192, 191]);
console.log(decoder.decode(bytes));
// Before: "?"
// After: "\uFFFD\uFFFD"
Non-ASCII template literals with addition operator
We fixed a transpiler bug where template literals with an addition operator and non-ASCII input would not be transpiled correctly in certain cases. This was a regression that was introduced in Bun v1.1.17.
console.log(`${123}➖` + "123456 456");
// Before: 123➖
// After: 123➖123456 456
Thanks to @paperdave for fixing this bug!
Bun.serve()
reliability improvements
We have made several reliability improvements to Bun.serve()
. This includes:
- No longer creating unnecessary
DOMException
objects when HTTP requests fail or are aborted - Fixed a bug where throwing an error when consuming a
ReadableStream
via async iterators could cause an uncatchable global error to be thrown - Unnecessarily abrupt TLS shutdown caused clients to not receive a WebSocket close frame in some cases
- Serving a
Bun.file()
in certain cases would omit the trailing newline from the response body - A crash that could occur when abruptly stopping a Bun.serve() server while there are still open HTTP requests has been fixed
WebSocket server publish after close
A bug where calling .publish()
on a closed ServerWebSocket
would incorrectly attempt to publish a message instead of returning 0
has been fixed. In rare cases this could cause a crash.
WebSocket client receives spurious 1006 close code
We fixed a bug in the WebSocket
client, where it would receive a spurious 1006
close code, even after sending the server a close frame with a different code. This was because Bun was doing a fast-shutdown, which meant that the close frame was sometimes not sent properly.
Thanks to @cirospaciari for fixing this bug!
Missing Date
header when sending Bun.file()
as response
We fixed a bug where the Date
header was not being sent when sending a File
as a response in Bun.serve()
. This was because Bun uses the sendfile()
syscall for sending files, and that code path was not setting the Date
header.
const server = Bun.serve({
async fetch(req) {
const file = Bun.file("hello.txt");
return new Response(file);
},
});
const response = await fetch(server.url);
console.log(response.headers.get("Date"));
// Before: undefined
// After: "Wed, 06 Aug 2024 19:46:05 GMT"
Thanks to @cirospaciari for fixing this bug!
Cloned File
would use the wrong filename
There was a bug where cloning a File
from another file, while defining a new name, would use the wrong name. This has now been fixed.
const original = Bun.file("original.txt");
const clone = new File([original], "clone.txt");
console.log(clone.name);
// Before: "original.txt"
// After: "clone.txt"
bun upgrade
on Windows with space in home directory
We fixed a bug where bun upgrade
would fail on Windows if your home directory contained a space.
bun upgrade
Bun v1.1.22 is out! You're on v1.1.21
Expand-Archive : A positional parameter cannot be found that accepts argument 'PC\AppData\Local\Temp\1.1.22'.
At line:1 char:47
+ ... lyContinue';Expand-Archive -Path bun.zip C:\Users\My PC\AppDat ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Expand-Archive], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Expand-Archive
error: Failed to verify Bun (code: FileNotFound))
If you experienced the following error, it has now been fixed, but you will have to run the PowerShell installer once more to receive the fixed build:
powershell -c "irm bun.sh/install.ps1 | iex"
Thanks to @paperdave for fixing this issue!
UDP socket hangs on macOS 13
Bun uses an undocumented sendmsg_x()
syscall as a fast-path on macOS to send multiple UDP packets at once, similar to the sendmmsg()
syscall on Linux. Our usage of the API caused issues on macOS 13, which would cause UDP packets to not be sent. This has now been fixed.