Bun

Bun v1.1.27


Dave Caruso · September 7, 2024

Bun v1.1.27 is here! This release fixes 130 bugs (addressing 250 👍). bun pm pack, faster node:zlib. Static routes in Bun.serve(). ReadableStream support in response.clone() & request.clone(). Per-request timeouts. Cancel method in ReadableStream is called. bun run handles CTRL + C better. Faster buffered streams. bun outdated package filtering. Watch arbtirary file types in --hot and --watch. Fixes to proxies on Windows. Several Node.js compatibility improvements.

We're hiring systems engineers in San Francisco to build the future of JavaScript!

To install Bun

curl
npm
powershell
scoop
brew
docker
curl
curl -fsSL https://bun.sh/install | bash
npm
npm install -g bun
powershell
powershell -c "irm bun.sh/install.ps1|iex"
scoop
scoop install bun
brew
brew tap oven-sh/bun
brew install bun
docker
docker pull oven/bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun

To upgrade Bun

bun upgrade

Native node:zlib

We rewrote node:zlib to use native code.

Runtimenode:zlibStream (MB/s)Sync (MB/s)
Bun v1.1.27deflate32.79 MB/s14.82 MB/s
Node v22.5.1deflate15.50 MB/s31.39 MB/s
Bun v1.1.26deflate15.59 MB/s10.29 MB/s
Bun v1.1.27deflateRaw32.68 MB/s14.60 MB/s
Node v22.5.1deflateRaw19.77 MB/s31.41 MB/s
Bun v1.1.26deflateRaw15.67 MB/s10.38 MB/s
Bun v1.1.27gunzip87.72 MB/s107.60 MB/s
Bun v1.1.26gunzip46.49 MB/s42.74 MB/s
Node v22.5.1gunzip31.01 MB/s75.86 MB/s
Bun v1.1.27gzip32.69 MB/s14.91 MB/s
Node v22.5.1gzip18.46 MB/s31.10 MB/s
Bun v1.1.26gzip15.29 MB/s10.11 MB/s
Bun v1.1.27inflate87.79 MB/s102.86 MB/s
Bun v1.1.26inflate50.72 MB/s43.83 MB/s
Node v22.5.1inflate26.07 MB/s74.64 MB/s
Bun v1.1.27inflateRaw87.68 MB/s101.78 MB/s
Bun v1.1.26inflateRaw53.89 MB/s43.13 MB/s
Node v22.5.1inflateRaw33.35 MB/s75.02 MB/s

Via a third-party zlib benchmark on a Linux x64 Hetzner server.

There's still more to do to optimize node:zlib in Bun, but this is a good start.

Big thanks to @nektro for implementing this.

New command: bun pm pack

bun pm pack is designed to be a drop-in replacement for npm pack.

New in Bun.serve() (HTTP server)

This release introduces reliability and quality of life improvements to Bun.serve(), our fast HTTP server.

Static routes

You can now pass a static option to serve static Response objects for a path.

Bun.serve({
  static: {
    "/api/health-check": new Response("🟢"),
    "/old-link": Response.redirect("/new-link", 301),
    "/api/version": Response.json(
      {
        app: require("./package.json").version,
        bun: Bun.version,
      },
      {
        headers: {
          "X-Powered-By": "bun",
        },
      },
    ),
  },

  async fetch(req) {
    return new Response("Dynamic!");
  },
});

ReadableStream support in request.clone() and response.clone()

Cloning a streaming Request or Response is now supported via request.clone() and response.clone().

To clone a request from Bun.serve:

Bun.serve({
  async fetch(req, server) {
    const cloned = req.clone();
    await req.json();

    // Previously, this would be empty.
    await cloned.json();

    // ... rest of the code
  },
});

To clone a Response from fetch:

const response = await fetch("https://example.com");
const cloned = response.clone();

console.log(await response.text());
console.log(await cloned.text());

Previously, this was only supported for static responses like when passing a string, an ArrayBuffer, or a Blob to the Response constructor.

The "cancel" method in ReadableStream

When a request is aborted, Bun now calls the cancel method in ReadableStream .

import { serve } from "bun";

serve({
  async fetch(req, server) {
    req.signal.addEventListener("abort", () => {
      // Previously, AbortSignal was the only way to detect if a request was aborted.
      console.log("Request aborted");
    });

    return new Response(
      new ReadableStream({
        async pull(controller) {
          controller.enqueue("Hello World");
          await Bun.sleep(1000);
        },
        cancel() {
          // New! Called when the request is aborted.
          console.log("Stream cancelled");
        },
      }),
    );
  },
});

Per-request timeouts

The new server.timeout(request: Request, seconds: number) method allows you to set a timeout per specific HTTP request.

To set a 60 second timeout for a request:

Bun.serve({
  async fetch(req, server) {
    server.timeout(req, 60); // 60 seconds
    await req.formData();
    return new Response("Slow response");
  },
});

Thanks to @cirospaciari for implementing this.

Faster buffered streams

We didn't just add new features to Bun.serve() this release. We also made it faster.

New CLI flag: --max-http-header-size

Passing --max-http-header-size sets the size of the HTTP request header buffer.

https://github.com/oven-sh/bun/pull/13577

Fixed: server.requestIP after await

Previously, using server.requestIP after a callback or call to await would lose the request metadata. This has been fixed.

Bun.serve({
  port: 3000,
  async fetch(req, server) {
    console.log(server.requestIP(req));
    // { address: '127.0.0.1', family: 'IPv4', ... }

    await req.formData();

    console.log(server.requestIP(req));
    //    now: { address: '127.0.0.1', family: 'IPv4', ... }
    // before: undefined
  },
});

Fixed: ws.publish on a closed WebSocket

In Bun.serve, calling .publish on a websocket will send a message to all clients except the websocket itself. When using this in the close handler, the WebSocket was already closed and no message could be sent. This has now been fixed.

Thanks to @cirospaciari for fixing this.

Fixed: memory leak when accessing request body and buffered methods

A memory leak has been fixed in code like the below:

Bun.serve({
  async fetch(req) {
    if (req.body) {
      await req.json();
    }

    // ... rest of the code
  },
});

When the Request body's ReadableStream was accessed, in certain cases a buffering method like req.json may use a different implementation and the Request object was incorrectly holding a strong reference to the Promise returned by req.json in those cases, leaking memory.

While adding clone() support for streams, we revised the implementation of Request and Response to fix this and similar issues.

bun outdated filtering

bun outdated now has --filter, which allows you to choose which workspaces you want to audit for outdated packages.

# Path patterns:
# Matches all workspaces in `./packages`
bun outdated --filter="./packages/*"

# Name patterns:
# All workspaces with names starting with `pkg`
bun outdated --filter="pkg*"

# All workspaces excluding `pkg1`
bun outdated --filter="*" --filter="!pkg1"

Additionally, positional arguments filter the list of dependencies, which can be helpful if you only want to check certain packages for updates.

# All dependencies starting with `is-` (could display `is-even`, `is-odd`, ...)
bun outdated "is-*"

# All dependencies from a particular scope
bun outdated "@discordjs/*"

# With `--filter`. Matches all outdated `jquery` dependencies in all workspaces.
bun outdated jquery --filter="*"

bun run Ctrl + C behavior is fixed

bun run wasn't propagating POSIX signals from the bun process to the child process (only from child -> parent). This led to strange bugs in certain cases, like CTRL + C not exiting the child process until later when an I/O error occurs from reading stdin on a disconnected terminal. This visually manifested as the ^[[A character appearing when you press ↑ Up instead of the previous command.

This has been fixed.

Thanks to @xales for debugging this.

Watch arbitrary file types in --watch and --hot

Bun's builtin watch & hot reloading now supports watching arbitrary file types.

To watch a file, import it.

import "./tailwind.css";
// ... rest of my app

Bun's watcher relies on the module graph to determine which files trigger a reload. This reduces unnecessary reloads, improving your iteration cycle time.

Previously, only JavaScript, TypeScript, JSON, TOML and .txt files were watched. Now, Bun will watch any file that is imported regardless of file extension.

Fixed: Default idleTimeout no longer set in node:http

In Bun 1.1.26, the default idleTimeout was 10 seconds, which was a breaking change as versions before had unlimited timeout. The default value of idleTimeout is now 0, which disables the timeout.

Node.js compatibility improvements

This release also includes a number of Node.js compatibility improvements.

Fixed: Crash while throwing an exception from N-API

A bug causing a crash in N-API when an exception was thrown has been fixed.

Fixed: Crash in N-API when accessing heap-allocated JSValue

JavaScriptCore & V8 are different engines with different approaches to garbage collection. JavaScriptCore scans the stack. V8 relies on handle scopes.

Previously, Bun didn't implement handle scopes. This led to crashes in N-API packages like DuckDB that heap-allocate napi values without referencing them in stack memory. The garbage collector wouldn't see them, and clean up the memory prematurely.

Thanks to @190n for implementing handle scopes.

Fixed: Crash impacting Next.js middleware

A crash in node:vm when using a class extending a built-in class like URL or Response inside of node:vm has been fixed. This crash was reproducible in Next.js middleware.

Fixed: Missing ERR_INVALID_ARG_TYPE in some Buffer methods

In Buffer.isAscii and Buffer.isUtf8, the error thrown when given invalid arguments now properly includes code: "ERR_INVALID_ARG_TYPE".

Fixed: Missing ERR_INVALID_URL in new URL

To match Node.js, the error thrown by new URL when giving an invalid URL now includes code: "ERR_INVALID_URL"

Fixed: AES-GCM encryption of empty messages

Thanks to @wpaulino, an empty message now properly round-trips in AES-GCM encryption.

Fixed: util.promisify with crypto.generateKeyPair

Thanks to @wpaulino, calling util.promisify on crypto.generateKeyPair will now return the proper promisified function.

Fixed: Handling out-of-memory errors in Buffer

When converting a large string to a Buffer, Bun now handles out-of-memory errors by throwing an OutOfMemoryError instead of crashing.

Fixed: node:tls accepting an arbitrary stream for a TLS socket. Fixed mssql package

The tls.connect function from node:tls accepts a socket option. This lets you upgrade any net.Socket to TLS. However, this parameter can actually be any arbitrary stream.Duplex, which Bun previously did not support, as it relies the underlying network connection to engage in TLS more efficiently.

This resolves the mssql package throwing Error: socket must be an instance of net.Socket when used.

Thanks to @cirospaciari

Fixed: node:http agent ignored

In certain cases, the node:http agent was not being used. This has been fixed.

Thanks to @nektro

Fixed: node:http setTimeout & server.setTimeout ignored

With server.timeout now implemented, we've also added support for server.setTimeout and request.setTimeout in node:http.

Thanks to @cirospaciari for fixing this.

Fixed: node:vm inconsistencies

Various subtle mistakes in node:vm have been fixed:

  • vm.runInThisContext now operates in global context
  • script.runInNewContext now operates in it's own context
  • script.runInThisContext and vm.runInThisContext don't take context as an argument
  • vm.runInThisContext, vm.runInContext, and vm.runInContext take a string as the options argument.

Thanks to @SunsetTechuila for fixing these!

Fixed: Missing net.Socket.destroySoon

Previously, this method in node:net was undefined. This is now fixed.

fetch reliability improvements

Proxies in fetch() on Windows work better now

On Windows, using a proxy with fetch often led to error: Syscall being thrown.

This has been resolved thanks to @cirospaciari.

Fixed: a memory leak with FormData or URLSearchParams body

In Bun v1.1.25, a regression was introduced where sending FormData and URLSearchParams in the body of a fetch() request would leak memory. This regression is now fixed.

Lots more fixes

Fixed: Reading large files Bun.file was truncated

When reading a file over 4gb into a single ArrayBuffer, the buffer would be truncated to 4gb. This has now been fixed.

Fixed: Clearing screen in some terminals

The ANSI escape code used to clear the terminal screen has been changed from \x1b[H\x1b[2J to \x1B[2J\x1B[3J\x1B[H, which is compatible with more terminals. Most notably, this fixes Visual Studio Code's terminal, which did not fully clear before this change.

Thanks to @jakebailey for fixing this.

Fixed: Handling out-of-memory errors in Bun.escapeHTML

When giving large enough strings to Bun.escapeHTML, it will now throw an OutOfMemoryError instead of crashing.

Bun.deepEquals performance improvements for Map and Set

After a the WebKit upgrade for Bun v1.1.19, the implementation of Map and Set changed to the point we had to remove their fast paths in Bun.deepEquals. Thanks to @wpaulino, a new fast path has been added, which is better than what was previously in place.

Additionally, the fast path covers custom classes that extend Map or Set, giving them nearly indistinguishable performance in Bun.deepEquals from the base class itself.

In bun install, an issue updating package binaries while they are currently open has been fixed

Thanks to @dylan-conway

Fixed: LCOV reporter properly reports non-hit lines

Bun's LCOV reporter previously would sometimes report 100% line coverage when that wasnt always the case. This has now been fixed thanks to @fmorency.

Fixed: bun add -g postinstall message

When installing a global package with a blocked postinstall script, the message will now include the correct command:

 $ bun install -g @delance/runtime
 bun add v1.1.27

 installed @delance/runtime@2024.8.103 with binaries:
  - delance-langserver

 68 packages installed [1.98s]

-Blocked 1 postinstall. Run `bun pm untrusted` for details.
+Blocked 1 postinstall. Run `bun pm -g untrusted` for details.

Thanks to @RiskyMH for fixing this

Fixed: Race condition in Bun.build

A race condition causing a Bun.build promise to hang has been fixed. This could be caused if you queued many Bun.build calls in a short period of time.

Fixed: bun shell not keeping parent process alive

A bug where the following code would exit prematurely, wtihout printing "never reached!!! why!!" has been fixed.

import { $ } from "bun";
console.log("before shell");
(async function () {
  await `echo hi`;
  console.log("never reached!!! why!!");
})();

Thanks to 17 contributors!