Bun

Bun v0.8.0


Colin McDonnell · August 24, 2023

Bun v0.8.0 adds debugger support, implements fetch streaming, and unblocks SvelteKit. ReadStream and WriteStream from node:tty are implemented, and .setRawMode() now works on process.stdin, unblocking several interactive CLI tools. Plus Node.js compatibility updates, bug fixes, stability improvements.

Bun 1.0 is coming on September 7th! Register for the launch stream at https://bun.sh/1.0.

Bun is an incredibly fast JavaScript runtime, bundler, transpiler, and package manager — all in one. We've been releasing a lot of changes to Bun recently. Here's a recap of the last few releases. In case you missed it:

  • v0.7.0 - Web Workers, --smol, structuredClone(), WebSocket reliability improvements, node:tls fixes, and more.
  • v0.7.1 - ES Modules load 30-250% faster, fs.watch fixes, and lots of node:fs compatibility improvements.
  • v0.7.2 - Implements node:worker_threads, node:diagnostics_channel, and BroadcastChannel.
  • v0.7.3 - Coverage reporting in bun test, plus test filtering with bun test -t.

To install Bun:

curl
npm
brew
docker
curl
curl -fsSL https://bun.sh/install | bash
npm
npm install -g 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

Debugger support

Bun now implements debugger support via WebKit's Inspector Protocol. To use it, run your file or script with the --inspect flag. Consider the following simple HTTP server.

server.ts
const server = Bun.serve({
  fetch(req){
    console.log(req.url);
    return new Response("Hello world!");
  }
});

console.log(`Listening on http://localhost:${server.port}`);

When we run this file with --inspect, it starts our HTTP server and spins up a localhost WebSocket server on an unused port. This WebSocket server uses the Inspector Protocol to communicate with debugging tools, which can introspect and control the running bun process.

bun --inspect server.ts
Listening on http://localhost:3000
------------------ Bun Inspector ------------------
Listening at:
  ws://localhost:6499/0tqxs9exrgrm

Inspect in browser:
  https://debug.bun.sh/#localhost:6499/0tqxs9exrgrm
------------------ Bun Inspector ------------------

Using debug.bun.sh

The --inspect flag will also print a https://debug.bun.sh# URL to the console. This domain debug.bun.sh hosts a stripped-down version of Safari Developer Tools designed for debugging Bun. Open the link in your preferred browser and a new debugging session will start automatically.

Bun Developer Tools at debug.bun.sh

From the web debugger, you can inspect the currently running code and set breakpoints. Once a breakpoint is triggered, you can:

  • View all variables in scope
  • Execute code in the console, with full access to in-scope variables and Bun APIs
  • Step through your code step-by-step
screenshot of Bun debugger

Read the Debug Bun with the web debugger guide for more complete documentation.

bun update

The new bun update command will update all project dependencies to the latest versions that are compatible with the semver ranges in your package.json.

bun update

To update a particular dependency:

bun update zod

Note — Unlike npm, the bun upgrade command is reserved for upgrading Bun itself. It is not an alias for bun update.

Thanks to @alexlamsl for implementing this feature.

SvelteKit support

Improved support for environment variables in Worker has unblocked SvelteKit. Scaffold your project with create-svelte.

bunx create-svelte my-app
create-svelte version 5.0.5

┌  Welcome to SvelteKit!

◇  Which Svelte app template?
│  SvelteKit demo app

◇  Add type checking with TypeScript?
│  Yes, using TypeScript syntax

◇  Select additional options (use arrow keys/space bar)
│  none

└  Your project is ready!

✔ Typescript
  Inside Svelte components, use <script lang="ts">

Install community-maintained integrations:
  https://github.com/svelte-add/svelte-add

To install dependencies and start the dev server:

cd my-app
bun install
bun run dev -- --open

Open localhost:3000 to see the demo SvelteKit app in action.

A SvelteKit app running on Bun

Support for Nuxt (nuxt dev)

With improved node:tty and node:fs support, the Nuxt development server now works with the Bun runtime. To get started, use the nuxi command-line tool.

bunx --bun nuxi init my-app
cd my-app
bun install

Once dependencies are installed, start the development server with the "dev" package.json script.

bun --bun run dev
 $ nuxt dev
Nuxt 3.6.5 with Nitro 2.5.2
  > Local:    http://localhost:3000/
  > Network:  http://192.168.0.21:3000/
  > Network:  http://[fd8a:d31d:481c:4883:1c64:3d90:9f83:d8a2]:3000/

✔ Nuxt DevTools is enabled v0.8.0 (experimental)
ℹ Vite client warmed up in 547ms
✔ Nitro built in 244 ms

The --bun flag ensures the server is using the Bun runtime instead of Node.js. By default the nuxt CLI uses Node.js.

Then visit http://localhost:3000 to see the default Nuxt welcome screen.

Nuxt app running on Bun

Note — There are still some issues with Nuxt's build command. When doing a production build, use bun run build instead of bun --bun run build. This will use Node.js to run Nuxt's build pipeline. Track this issue here.

Refer to the Building an app with Nuxt and Bun for a more complete walkthrough.

Fetch response body streaming

Bun now implements fetch() response body streaming, thanks to @cirospaciari. This means that you can now stream data from a fetch response, instead of waiting for the entire response to be downloaded.

const res = await fetch("https://example.com/bigfile.txt");

// read the response chunk-by-chunk!
for await (const chunk of res.body) {
  console.log(chunk);
}

Use OpenAI in Bun

Streaming is especially useful when working with APIs that take awhile to respond. Now you can stream responses from OpenAI's API in Bun.

import OpenAI from "openai";

const openai = new OpenAI({
  apiKey: "my api key",
});

const stream = await openai.chat.completions.create({
  model: "gpt-4",
  messages: [{ role: "user", content: "Say this is a test" }],
  stream: true,
});

for await (const part of stream) {
  process.stdout.write(part.choices[0]?.delta?.content || "");
}

Bun.serve() streaming improvements

Previously, the following would buffer the response body, meaning it would only send the response once the entire body had been generated.

import { serve, sleep } from "bun";
serve({
  fetch(req) {
    return new Response(
      new ReadableStream({
        async pull(controller) {
          for (let i = 0; i < 20; i++) {
            controller.enqueue("Hello world!");
            await sleep(42);
          }

          controller.close();
        },
      }),
    );
  },
});

// Hello World!Hello World!Hello World!...[~840ms]

That's not what people want! Now, the response body is streamed, meaning it's sent to the client as it's generated.

import { serve, sleep } from "bun";
serve({
  fetch(req) {
    return new Response(
      new ReadableStream({
        async pull(controller) {
          for (let i = 0; i < 20; i++) {
            controller.enqueue("Hello world!");
            await sleep(42);
          }

          controller.close();
        },
      }),
    );
  },
});

// Hello world! [42ms]
// Hello world! [42ms]
// Hello world! [42ms]

Previously, streaming was only supported when using ReadableStream with type: "direct", a Bun-specific fast path for streaming.

import { serve, sleep } from "bun";
serve({
  port: 3000,
  fetch(req) {
    return new Response(
      new ReadableStream({
        // bun specific option
        type: "direct",

        async pull(controller) {
          for (let i = 0; i < 20; i++) {
            controller.write("Hello world!");

            // in bun < 0.8.0, flush() was required
            controller.flush();

            // now bun will automatically flush pending writes once the microtask queue is drained
            await sleep(42);
          }

          controller.end();
        },
      }),
    );
  },
});

// Hello world! [42ms]
// Hello world! [42ms]
// Hello world! [42ms]
// Hello world! [42ms]

We still have optimization work ahead to make streaming using the default ReadableStream type fast. For now, we recommend using type: "direct" to get the fastest possible streaming.

Support for node:tty and process.stdin.setRawMode()

Inquirer npm package in Bun

The ReadStream and WriteStream classes from node:tty have been implemented, and process.stdin is now an instance of ReadStream. Accordingly, it's now possible to enable "raw mode" on process.stdin.

process.stdin.setRawMode(true);

Support for inquirer and other prompt libraries

This makes it possible to read keypresses without waiting for a newline character, which is important for interactive CLI tools. The popular libraries inquirer, enquirer, and prompts are now fully supported.

Run the following command to interactively scaffold a Remix app using Bun and the interactive create-remix command-line tool.

bunx --bun create-remix

Thanks to @dylan-conway for implementing this feature.

Improvements to bun test

test.each and describe.each

Thanks to @jecquas, Bun now supports test.each and describe.each from Jest. This makes it easy to run the same test with different inputs.

describe.each([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])("add(%i, %i)", (a, b, expected) => {
  test(`returns ${expected}`, () => {
    expect(a + b).toBe(expected);
  });
});

.toSatisfy() and .toIncludeRepeated()

Thanks to @TiranexDev, bun test now supports additional matchers. These matchers are part of jest-extended and are now natively supported by bun test.

Sample usage of .toSatisfy():

const isOdd = (value: number) => value % 2 !== 0;
it("toSatisfy", () => {
  expect(1).toSatisfy(isOdd);
  expect(2).not.toSatisfy(isOdd);
});

Sample usage of .toIncludeRepeated():

test("toIncludeRepeated", () => {
  // check if string contains substring exactly 2 times
  expect("hello hello").toIncludeRepeated("hello", 2);
  // works with .not
  expect("hello hello world").not.toIncludeRepeated("hello", 1);
});

40x faster buffer.toString("hex")

The Node.js Buffer.toString("hex") function gets optimized with SIMD, leading to 40x faster performance.

Bun.inspect.custom

Objects can now be augmented with custom formatters using the Bun.inspect.custom symbol. For compatibility reasons, util.inspect.custom from Node.js's node:util works too.

class Password {
  value: string;
  constructor(value: string) {
    this.value = value;
  }
  [Bun.inspect.custom]() {
    return "Password <********>";
  }
}

const p = new Password("secret");
console.log(p);
// => "Password <********>"

Global File constructor

The File constructor has been added as a new global. File instances can be constructed.

const file = new File(["hello world"], "hello.txt", {
  type: "text/plain",
  lastModified: Date.now() - 1000,
});

file.size; // 11
file.name; // "hello.txt"
file.type; // "text/plain"
file.lastModified; // 1693597759310573

A JIT crash in Buffer-related functions has been fixed. This crash was caused by incorrect side effects when passed to DOMJIT which led to a crash during type validation when the functions were called. This impacted several libraries and the crash began after a JavaScriptCore upgrade in Bun v0.7.3.

This crash would cause EXC_BREAKPOINT to be thrown after enough calls to Buffer.alloc, Buffer.allocUnsafe, Buffer.isBuffer were called.

Bugfixes and stability improvements

Buffer.toString("hex") memory leak fix

A memory leak has been fixed in the implementation of buffer.toString("hex").

NAPI fixes and support for resvg, sharp

A couple bugs in the NAPIClass constructor and napi_create_external_arraybuffer / napi_create_external_buffer have been fixed. The resolves issues when using resvg-js or sharp.

Better error when this is invalid

When calling a method with an unexpected value for this, Bun now reports an informative error.

const { json } = new Response(`"hello"`);
json();
// ^ TypeError: Expected `this` to be instanceof Response

Handle cross-device file copies

Bun now detects when a file copying operation (e.g fs.copyFile) is attempting to copy files across devices or partitions and falls back to a manual file copy syscall.

Fix Bun.deepEqual URL comparison

#4105 fixes a bug where URLs we're not properly compared by their internal href.

10% Regression in async-await performance on macOS has been fixed

  • The daylight savings time cache is no longer updated on each microtask call. This regression began in v0.7.x.

Several fixes to streams

#4251 includes a number of improvements and bugfixes in the implementation of ReadableStream have been fixed. This includes:

  • Pending writes to HTTP response bodies are automatically flushed once the microtask queue has been drained, fixing #1886
  • Improved error handling inside pull.

Changelog

As Bun 1.0 approaches, we've been tracking down remaining memory leaks and crashes.

#4028[install] Handle bun add of existing peerDependencies correctly by @alexlamsl
#4026Running missing scripts exits with non-0 by @YashoSharma
#4030Bind require.resolve`() by @Jarred-Sumner
#4034Normalize Request URLs by @Jarred-Sumner
#4042Fix path.normalize edge case. by @Hanaasagi
#4043Compile Bun's transpiler to WASM and add test analyzer by @Jarred-Sumner
#4048Fix iterating headers with set-cookie by @dylan-conway
#4000implement fetching data urls by @dylan-conway
#4054Fix Bun.hash functions by @jhmaster2000
#4064Fix path.format compatibility issue. by @Hanaasagi
#4073Fix require("console") #3820 by @paperdave
#4076Set exports to {} in user-constructed CommonJSModuleRecords by @paperdave
#4086Fix XLSX.read coredump by @Hanaasagi
#4027Add support for bun --revision by @YashoSharma
#4106Fix segfault in base64url encoder #4062 by @Jarred-Sumner
#4109Handle thundering herd of setInterval by @Jarred-Sumner
#4111Fix memory leak in Buffer.toString('base64url') by @Jarred-Sumner
#4113Run files without extensions by @dylan-conway
#4117Make astro build slightly faster
#4125Support TypeScript's export type * as Foo from 'bar' by @Jarred-Sumner
#4126bun-wasm fixes & improvements by @jhmaster2000
#4131Deprecate loading node_modules.bun by @Jarred-Sumner
#4129Fix custom config path not working. by @Hanaasagi
#4114Fix worker event loop ref/unref + leak by @paperdave
#4152Make builtins' source origin use a valid url by @paperdave
#4155Fix importing too long of strings by @paperdave
#4162Fix method name typo by @Hanaasagi
#4157Fix event loop issue with Bun.connect by @paperdave
#4172Update docs our current status of node compatibility by @paperdave in https://github.com/oven-sh/bun/pull/4172
#4173Create domjit.test.ts by @dylan-conway in https://github.com/oven-sh/bun/pull/4173
#4150Fix prisma linux generation by @cirospaciari in https://github.com/oven-sh/bun/pull/4150
#4181Fix leaking .ptr by @Jarred-Sumner in https://github.com/oven-sh/bun/pull/4181
#4192correct guide's bunfig example option by @xxxhussein in https://github.com/oven-sh/bun/pull/4192
#4193refactor: move HTMLRewriter to c++ bindings by @bru02 in https://github.com/oven-sh/bun/pull/4193
#4191Fix(node:fs): add buffer parameter in fs.read callback. by @Hanaasagi in https://github.com/oven-sh/bun/pull/4191
#4154Allow IncomingRequest.req to be overwritten. by @paperdave in https://github.com/oven-sh/bun/pull/4154
#4098Support Nitro by @paperdave in https://github.com/oven-sh/bun/pull/4098
#4194Add util.inspect.custom support to util.inspect/Bun.inspect/console.log by @paperdave in https://github.com/oven-sh/bun/pull/4194
#4187Remove most C API usages, add debugger pretty printers for Headers, URLSearchParams, FormData, Worker, EventTarget by @Jarred-Sumner in https://github.com/oven-sh/bun/pull/4187
#4208Implement BigIntStats by @paperdave in https://github.com/oven-sh/bun/pull/4208
#4206feat: add self-closing & can-have-content by @bru02 in https://github.com/oven-sh/bun/pull/4206
#4213Add inline sourcemaps when --inspect is enabled by @Jarred-Sumner in https://github.com/oven-sh/bun/pull/4213
#4220Fixes #172 by @Jarred-Sumner in https://github.com/oven-sh/bun/pull/4220
#4221Fix crash impacting sharp & resvg by @Jarred-Sumner in https://github.com/oven-sh/bun/pull/4221
#4210Add unsupported (yet) comment to distroless image by @o-az in https://github.com/oven-sh/bun/pull/4210
#4163Fix(bundler): use different alias mappings based on the target. by @Hanaasagi in https://github.com/oven-sh/bun/pull/4163
#4231Fix test failures from 3a9a6c63a by @Jarred-Sumner in https://github.com/oven-sh/bun/pull/4231
#4222Implement --inspect-brk by @Jarred-Sumner in https://github.com/oven-sh/bun/pull/4222
#4230Fixes #1675 by @Jarred-Sumner in https://github.com/oven-sh/bun/pull/4230
#4235Fix memory leak in buffer.toString("hex") by @Jarred-Sumner in https://github.com/oven-sh/bun/pull/4235
#4237Buffer.toString('hex') gets 40x faster by @Jarred-Sumner in https://github.com/oven-sh/bun/pull/4237
#4243feat: Implement Bun.inspect.custom by @paperdave
#4156Implement napi_ref_threadsafe_function by @paperdave
#4242Fix crypto.EC constructor by @paperdave
#4226Fix(bundler): allow generating exe file in nested path. by @Hanaasagi
#4245Fix emitKeyPresses with backspace + quote by @paperdave
#4127fetch(stream) add stream support for compressed and uncompressed data by @cirospaciari
#4244import errors have code set to ERR_MODULE_NOT_FOUND and require errors have code set to MODULE_NOT_FOUND by @Jarred-Sumner
#4250fix stdin stream unref and resuming by @dylan-conway
#4247fix fsevents and stub for qwikcity by @paperdave
#4264fix(parser): yield before ]shouldn't be a syntax error by @paperdave
#4256Ask for bun --revision instead bun -v in PR template by @xHyroM
#4273Fix more types. by @xxxhussein
#4251Bunch of streams fixes by @Jarred-Sumner

View the complete changelog