---
title: Bun v1.1.21
description: 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.
date: "2024-07-27T06:43:53.051Z"
author: ashcon
---

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.

{% callout %}
We're hiring [systems engineers](/careers) in San Francisco to build the future of JavaScript!
{% /callout %}

#### Previous releases

- [`v1.1.20`](/blog/bun-v1.1.19) & [`v1.1.19`](/blog/bun-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 in `new Response(request)` and `fs.readdir`. Several reliability improvements.
- [`v1.1.18`](/blog/bun-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 in `bun install` when linking binaries. Fixes a crash in `dns.lookup`, and normalizes DNS errors to better match Node. Fixes an assertion failure in `Bun.escapeHTML`. Upgrades JavaScriptCore.
- [`v1.1.0`](/blog/bun-v1.1) Bundows. Windows support is here!

#### To install Bun

{% codetabs %}

```sh#curl
$ curl -fsSL https://bun.sh/install | bash
```

```sh#npm
$ npm install -g bun
```

```sh#powershell
$ powershell -c "irm bun.sh/install.ps1|iex"
```

```sh#scoop
$ scoop install bun
```

```sh#brew
$ brew tap oven-sh/bun
$ brew install bun
```

```sh#docker
$ docker pull oven/bun
$ docker run --rm --init --ulimit memlock=-1:-1 oven/bun
```

{% /codetabs %}

#### To upgrade Bun

```sh
$ bun upgrade
```

### fetch() decompresses 30% faster

Decompressing `fetch()` response bodies compressed with gzip or deflate get 30% faster on Linux x64, thanks to [libdeflate](https://github.com/ebiggers/libdeflate).

{% raw %}

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">In the next version of Bun<br><br>fetch() decompresses gzip&#39;d data 30% faster, thanks to libdeflate. <a href="https://t.co/obCjWo2fHv">pic.twitter.com/obCjWo2fHv</a></p>&mdash; Jarred Sumner (@jarredsumner) <a href="https://twitter.com/jarredsumner/status/1817067294591451208?ref_src=twsrc%5Etfw">July 27, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

{% /raw %}

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.

```sh
$ 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:

```ts
fetch.preconnect(url: string): void;
```

This is useful for serverless environments and databases that run on top of `fetch` or `node:http`.

```ts
 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:

```js
// 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.

```sh
$ 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.

```bash#CLI
$ bun build ./index.ts --packages external
```

You can also use this in the JavaScript API:

```ts#JavaScript
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](https://github.com/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.

```ts#entry.ts
const other = await import('./second');
```

```ts#second.ts
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 [@paperclover](https://github.com/paperclover) 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.

```js#bar.mts
import "./foo.mjs";
```

```ts#foo.mts
export const foo = 1;
```

This has now been fixed thanks to [@190n](https://github.com/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.

```js
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.

```js
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](https://github.com/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.

```ts
namespace X {
  export function Y(): void {}
  export namespace Y {
    export const Z = 1;
  }
}
```

Thanks to [@paperclover](https://github.com/paperclover) for fixing this bug!

This has now been fixed thanks to [@190n](https://github.com/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](https://github.com/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.

```js
import { CryptoHasher } from "bun";

const hasher = new CryptoHasher("sha1");
hasher.update(); // <crash>
```

Thanks to [@cirospaciari](https://github.com/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](https://github.com/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:

```js
test("toThrow asymmetric matcher", () => {
  expect(() => {
    throw new Error("error!");
  }).toThrow(expect.any(Error)); // passes
});
```

Thanks to [@ippsav](https://github.com/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`.

```js
console.log(test.each([1, 2])("test.each %d", () => {})); // Before: <crash>, After: undefined
```

Thanks to [@dylan-conway](https://github.com/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](https://github.com/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:

```js
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:

```sh
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.

```js
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:

```sh
❯ bun chunk.js
Received 16384
[... more chunks ...]
Received 3883
```

Previously:

```sh
❯ 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.

```js
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:

```js
> 2 | path.posix.basename("bar", undefined);
                          ^
TypeError: "ext" property must be of type string, got undefined
 code: "ERR_INVALID_ARG_TYPE"
```

Thanks to [@190n](https://github.com/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.

```js
import dgram from "node:dgram";

const socket = dgram.createSocket("udp4");
socket.unref(); // <would not unref>
```

Thanks to [@190n](https://github.com/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](https://github.com/dylan-conway) for fixing this!

```c
...
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 manually
- `process.exit()` is called

We fixed a bug in Bun where `process.exitCode` would be set to `0` before `process.exit()` was called.

```js
console.log(process.exitCode); // Before: 0, After: undefined
```

Thanks to [@nektro](https://github.com/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](https://github.com/dylan-conway) for fixing this bug!

## Bundler fixes

### Slightly smaller bundles

{% raw %}

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">In the next version of Bun<br><br>Bun&#39;s bundler gets slightly smarter at dead-code elimination <a href="https://t.co/nUoNbVgM8J">pic.twitter.com/nUoNbVgM8J</a></p>&mdash; clo (@paperclover_dev) <a href="https://twitter.com/paperclover_dev/status/1816033742231855600?ref_src=twsrc%5Etfw">July 24, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

{% /raw %}

### 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 [@paperclover](https://github.com/paperclover)!

### 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.

```js
var process2 = require("process/");
// Error: Cannot find module 'process/'
```

This has now been fixed thanks to [@paperclover](https://github.com/paperclover)!

### 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.

```js
import("./foo", { with: "text" }); // would work
import("./foo", { with: "te" + "xt" }); // would crash
```

This has now been fixed, thanks to [@paperclover](https://github.com/paperclover)!

### 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.

{% codetabs %}

```ts#entry.js
async function hi() {
  const { default: MyInherited } = await import('./other.js');
  const myInstance = new MyInherited();
  console.log(myInstance.greet())
}

hi();
```

```ts#other.js
const MyReassignedSuper = class MySuper {
  greet() {
    return 'Hello, world!';
  }
};

class MyInherited extends MyReassignedSuper {};

export default MyInherited;
```

{% /codetabs %}

This produces the correct output now:

{% codetabs %}

```diff#diff.js
-   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;
// ...
```

```js#new.js
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();
```

```js#old.js
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();
```

{% /codetabs %}

### 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.

## Thanks to 14 contributors!

- [@190n](https://github.com/190n)
- [@cirospaciari](https://github.com/cirospaciari)
- [@dariushalipour](https://github.com/dariushalipour)
- [@davidstevens37](https://github.com/davidstevens37)
- [@dylan-conway](https://github.com/dylan-conway)
- [@eval](https://github.com/eval)
- [@HibanaSama](https://github.com/HibanaSama)
- [@huseeiin](https://github.com/huseeiin)
- [@ippsav](https://github.com/ippsav)
- [@Jarred-Sumner](https://github.com/Jarred-Sumner)
- [@mangs](https://github.com/mangs)
- [@nektro](https://github.com/nektro)
- [@paperclover](https://github.com/paperclover)
- [@zpix1](https://github.com/zpix1)
