Bun v1.1.25 is here! This release fixes 42 bugs (addressing 470 👍). Support for node:cluster
and V8's C++ API. Faster WebAssembly on Windows with OMGJIT. 5x faster S3 uploads with @aws-sdk/client-s3, Worker in standalone executables, and more Node.js compatibility improvements and bug fixes.
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
node:cluster
support
Bun now supports the node:cluster
API.
This allows you to run a pool of Bun workers that share the same port, enabling higher throughput and utilization on machines with many CPU cores. This is great for load balancing in production environments.
In the next version of Bun
— Bun (@bunjavascript) August 18, 2024
Initial node:cluster support has landed. Here's 1.29 million HTTP requests per second in TypeScript. pic.twitter.com/YzU1cZxk3S
Here's an example of how it works:
- The primary worker spawns
n
child workers (usually equal to the number of CPU cores) - Each child worker listens on the same port (using
reusePort
) - Incoming HTTP requests are load balanced across the child workers
import cluster from "node:cluster";
import http from "node:http";
import { cpus } from "node:os";
import process from "node:process";
if (cluster.isPrimary) {
console.log(`Primary ${process.pid} is running`);
// Start N workers for the number of CPUs
for (let i = 0; i < cpus().length; i++) {
cluster.fork();
}
cluster.on("exit", (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} exited`);
});
} else {
// Incoming requests are handled by the pool of workers
// instead of the primary worker.
http
.createServer((req, res) => {
res.writeHead(200);
res.end("hello world\n");
})
.listen(3000);
console.log(`Worker ${process.pid} started`);
}
This works with the node:http
APIs, as well as the Bun.serve()
API.
import cluster from "node:cluster";
import { cpus } from "node:os";
if (cluster.isPrimary) {
for (let i = 0; i < cpus().length; i++) {
cluster.fork();
}
} else {
Bun.serve({
port: 3000,
fetch(request) {
return new Response(`Hello from Worker ${process.pid}`);
},
});
}
Note that currently, reusePort
is only effective on Linux. On Windows and macOS, the operating system does not load balance HTTP connections as one would expect.
Thanks to @nektro for implementing this feature!
Initial V8 C++ API support
Bun now supports V8's public C++ API, which unblocks packages like cpu-features
to work in Bun.
This is notable change because Bun is not built on top of V8 like Node.js. Instead, Bun is built on top of JavaScriptCore, the JavaScript engine that powers Safari.
That means we had to implement our own C++ translation layer between V8's APIs and JavaScriptCore.
#include "v8.h"
#include "V8Primitive.h"
#include "V8MaybeLocal.h"
#include "V8Isolate.h"
namespace v8 {
enum class NewStringType { /* ... */ };
class String : Primitive {
public:
enum WriteOptions { /* ... */ };
BUN_EXPORT static MaybeLocal<String> NewFromUtf8(Isolate* isolate, char const* data, NewStringType type, int length = -1);
BUN_EXPORT int WriteUtf8(Isolate* isolate, char* buffer, int length = -1, int* nchars_ref = nullptr, int options = NO_OPTIONS) const;
BUN_EXPORT int Length() const;
JSC::JSString* localToJSString()
{
return localToObjectPointer<JSC::JSString>();
}
};
}
This is difficult engineering work. JavaScriptCore and V8 represent JavaScript values differently. V8 uses a moving, concurrent garbage collector with explicit handle scopes whereas JavaScriptCore uses a non-moving concurrent garbage collector that scans stack memory (sort of like an implicit handle scope).
Before, if you tried to import a package that used these APIs, you would get an error like this:
console.log(require("cpu-features")());
dyld[94465]: missing symbol called
fish: Job 1, 'bun index.ts' terminated by signal SIGABRT (Abort)
Now, packages like cpu-features
can be imported and just work in Bun.
$ bun index.ts
{
arch: "aarch64",
flags: {
fp: true,
asimd: true,
// ...
},
}
This is just the start of support for V8's internal APIs. If your package runs into an area of the V8 API that we have not yet implemented, you'll receive an error that directs you to upvote an issue on GitHub.
Why support V8's internal APIs?
We did not originally intend to support these APIs, but decided to after we found that many popular packages depend on these APIs, such as:
cpu-features
node-canvas@v2
node-sqlite3
libxmljs
- and many packages using
nan
In this release, only cpu-features
is supported from the list above, but we're working on improving support so all of these packages and more just work in Bun.
Thanks to @190n for implementing this feature!
5x faster S3 uploads with @aws-sdk/client-s3
We fixed a bug in our node:http client implementation and that made uploading to S3 5x faster.
In the next version of Bun
— Ciro Spaciari (@cirospaciari) August 21, 2024
Uploading files via @aws-sdk/client-s3 gets 5x faster pic.twitter.com/tptxegT7vh
Worker in standalone executables
Bun's single-file standalone executables now support bundling Worker
and node:worker_threads
.
console.log("Hello from main thread!");
new Worker("./my-worker.ts");
console.log("Hello from another thread!");
To compile a standalone executable with a worker, pass the entry points to bun build --compile
:
bun build --compile ./main.ts ./my-worker.ts
This will bundle my-worker.ts
and main.ts
as separate entry points in the resulting executable.
Embedding files in standalone executables without importing
You can now embed files in standalone executables without an explicit import
statement, if you want.
bun build --compile ./main.ts ./public/**/*.png
Usually, you'd have to import the file to use it:
import logo from "./public/icons/logo.png" with {type: "file"};
But, sometimes you want to import a large number of files that may need to programmatically be referenced.
Bun.embeddedFiles
The new Bun.embeddedFiles
API lets you see a list of all embedded files in a standalone executable.
import { embeddedFiles } from "bun";
for (const file of embeddedFiles) {
console.log(file.name); // "logo.png"
console.log(file.size); // 1234
console.log(await file.bytes()); // Uint8Array(1234) [...]
}
This combines all non-javascript entry points and assets into a single array of Blob
objects sorted by name.
Relative imports for embedded files in standalone executables
You can now reference runtime-known imports of embedded files using relative paths in standalone executables.
function getWasmFilePath(file: string) {
return require.resolve(`./${file}.wasm`);
}
const wasmFile = getWasmFilePath("my-wasm-file");
console.log(wasmFile); // "./my-wasm-file.wasm"
Previously, this was only supported with statically-analyzable imports that are knonwn at build time. Now, you can do this with any embedded file.
Faster WebAssembly on Windows with OMGJIT
WebAssembly on Windows now supports JavaScriptCore's optimized Just-In-Time compiler (JIT) called OMGJIT.
In the next version of Bun
— Bun (@bunjavascript) August 17, 2024
JavaScriptCore's optimizing WebAssembly JIT "OMGJIT" is enabled on Windows, making this wasm hashing benchmark 20% faster pic.twitter.com/8qhKVS4E6z
Thanks to Ian Grunert for shipping this feature in WebKit!
Node.js compatiblity improvements
execa
now works
We fixed a bug where Bun was not properly supporting setMaxListeners()
for EventTarget
. This affected packages like execa
, which would error out with undefined is not a function
.
import { execa } from "execa";
const { stdout } = await execa`echo "test"`;
If you experienced an error that looks like this, it has now been fixed:
91 | const controller = new AbortController();
92 | setMaxListeners(Number.POSITIVE_INFINITY, controller.signal);
^
TypeError: undefined is not a function
at node:events:101:30
at spawnSubprocessAsync (/node_modules/execa/lib/methods/main-async.js:92:2)
at execaCoreAsync (/node_modules/execa/lib/methods/main-async.js:26:32)
at index.mjs:3:26
Fixed: Connection hangs with node:net
after destroy()
We fixed a bug where calling destroy()
on a TCP connection, didn't always properly exit the process, since the event loop was still active. This would sometimes cause packages like postgres
to hang indefinitely.
import net from "node:net";
const server = net.createServer((socket) => {
socket.on("connect", (data) => {
socket.destroy();
// This would destroy the connection,
// but the event loop would still be active
});
});
server.listen(3000);
Fixed: node:net.Socket's ref and unref this
The ref
and unref
methods on node:net.Socket
were not returning the this
value.
Now, they return this
.
Thanks to @nektro for fixing this bug!
Bugfixes
Fixed: TextEncoderStream
yielded the wrong type
In Bun v1.1.23, we introduced support the the TextEncoderStream
API. We fixed a bug where the chunks from the stream were Buffer
objects instead of Uint8Array
objects.
const response = await fetch("https://example.com");
const body = response.body.pipeThrough(new TextEncoderStream());
for await (const chunk of body) {
console.log(chunk);
// Before: Buffer
// Now: Uint8Array
}
Fixed: crash with escaped backticks in Bun's shell
We fixed a crash in Bun's shell that would not properly handle escaped backticks.
import { $ } from "bun";
console.log(await $`cd \`find -name dir\``.text());
Thanks to @zackradisic for fixing this bug!
Fixed: JIT crash with TextEncoder
We fixed a bug where if TextEncoder
was JIT'd after being run millions of times, it would crash. This was due to a bug in Bun's generated code for enabling the JIT on a function.
Fixed: Crash in jest.fn and mock()
A crash that could occur when calling functions on a jest.fn
or mock()
object with an unexpected this
value has been fixed.
Fixed: Crash in IPC
When a child process exited before the parent process received a message, a crash could occur in some cases.
This has been fixed, thanks to @cirospaciari.
Fixed: console.log a File's name
When you use the web File
constructor to create a Blob, the name of the file was not being included in the output of console.log
.
Input:
console.log(new File(["hello world"], "hello.txt"));
New:
File (11 bytes) {
name: "hello.txt",
lastModified: 1724218333315
}
Previously:
Blob (11 bytes)
Fixed: rare crash in websocket server
A rare crash that could potentially occur when a long amount of time has passed since the server has called a callback and the ServerWebSocket
object has been garbage collected has been fixed.