Bun

Bun v1.0.10


Jarred Sumner ยท November 7, 2023

Bun v1.0.10 fixes 14 bugs (addressing 102 ๐Ÿ‘ reactions), node:http gets 14% faster, adds stability improvements to Bun for Linux ARM64, and fixes bugs in bun install and node:http.

Bun is an incredibly fast JavaScript runtime, bundler, transpiler, and package manager โ€” all in one. In case you missed it, here are some of the recent changes to Bun:

  • v1.0.0 - First stable release!
  • v1.0.1 - Named imports for .json and .toml files, fixes to bun install, node:path, and Buffer
  • v1.0.2 - Make --watch faster and lots of bug fixes
  • v1.0.3 - emitDecoratorMetadata and Nest.js support, fixes for private registries and more
  • v1.0.4 - server.requestIP, virtual modules in runtime plugins, and more
  • v1.0.5 - Fixed memory leak with fetch(), KeyObject support in node:crypto module, expect().toEqualIgnoringWhitespace in bun:test, and more
  • v1.0.6 - Fixes 3 bugs (addressing 85 ๐Ÿ‘ reactions), implements overrides & resolutions in package.json, and fixes regression impacting Docker usage with Bun
  • v1.0.7 - Fixes 59 bugs (addressing 78 ๐Ÿ‘ reactions), implements optional peer dependencies in bun install, and makes improvements to Node.js compatibility.
  • v1.0.8 - Fixes 138 bugs (addressing 257 ๐Ÿ‘ reactions), makes require() use 30% less memory, adds module mocking to bun test, fixes more bun install bugs
  • v1.0.9 - Fixes a glibc symbol version error, illegal instruction error, a Bun.spawn bug, an edgecase with peer dependency installs, and a JSX transpiler bugfix

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

node:http gets 14% faster

Bun's node:http implementation currently wraps Bun.serve(). Bun.serve() is built on web standards like Request and Response, which is incompatible with the Node.js IncomingMessage and ServerResponse classes. That means our wrapper has to do a lot of work to convert between the two.

While fixing a couple bugs, we moved some of the wrapper code which was previously implemented in TypeScript over to native code. This slightly reduced the overhead of the wrapper, making node:http 14% faster.

hello world node:http
Left: Bun v1.0.10. Right: Bun v1.0.9

I want the DETAILS

We made two changes to improve performance.

When you run request.headers in Bun, we serialize headers from Bun's web server into a web-standard Headers object. Normally, that's great. In this case, node:http only used it to convert from the web Headers object into a Node.js headers object and rawHeaders array. The Headers object does a lot of extra work behind the scenes - joining strings, normalizing whitespace, and more that the Node.js API does not do.

When you run request.url in Bun, it parses & normalizes the pathname joined with the Host header (when available), producing an absolute URL as a string you can use in fetch and with the URL constructor. In Node.js, req.url is a string containing the raw path and query string (/path?query). It's not a valid url. We were doing a lot of extra work to produce a valid URL string and then parse it back a 2nd time to convert into a pathname + query string.

Instead of doing all that work: creating these temporary objects & parsing, we now just create the Node.js headers object, rawHeaders array, and url string in native code.

As a side effect of moving the above code to native, we also fixed a bug where multiple Set-Cookie headers were not being exposed to JavaScript correctly in our node:http bindings. This lead to errors like this:

undefined is not an object (evaluating 'header.toLowerCase')

Fixed: "**" cannot be parsed as URL

In Node, it's expected that IncomingMessage can receive an invalid url string. Previously, Bun was using the URL constructor to parse, validate, and normalize the incoming url string, which caused spurious errors like this:

TypeError: "*" cannot be parsed as a URL.
      at new IncomingMessage (node:http:420:26)
      at fetch (node:http:383:26)
OPTIONS - * failed

Now, Bun just passes the url string through to node:http, which is what it expects.

Stability improvements to Bun for Linux ARM64/Docker

Most software on your computer interacts with the operating system through a layer called the "C standard library". The C standard library is a set of functions that are implemented by your operating system. For example, when you call fs.read in Bun or Node.js, it eventually calls the read function in the C standard library. That read function probably just wraps the read system call, which is a function implemented by the Linux kernel (or darwin kernel on macOS).

On Linux x64 & arm64, Bun often skips the C standard library and uses Linux system calls directly. This is a little faster, but it also makes error handling sometimes more complicated and means we need to spend more time reading/understanding internals. Our code for reading errors on Linux arm64 was assuming that libc errno was always in use, but that isn't true. TLDR: we were sometimes reading errno wrong on Linux arm64.

This lead to many strange issues, like:

docker-compose up

my-app-ui-1  | Failed to load PostCSS config: Failed to load PostCSS config (searchPath: /app): [EBADF] Bad file descriptor
my-app-ui-1  | undefined
my-app-ui-1  |    code: "EBADF"
my-app-ui-1  |  syscall: "fstat"
my-app-ui-1  |    errno: -9
my-app-ui-1  |

Or like:

#8 [4/4] RUN bun install
#8 0.077 bun install v1.0.9 (98f20170)
#8 0.077 error: Unexpected installing typescript
#8 0.077 error: Unexpected installing bun-types
#8 0.077 Failed to install 2 packages
#8 0.078 [2.00ms] done
#8 DONE 0.1s

This release fixes those issues.

Fixed: Loading extensions in bun:sqlite on macOS

A bug caused Bun to not support loading extensions in the bun:sqlite module on macOS. This has been fixed, thanks to @paulbaumgart.

Fixed: Semver parsing bug with pre-releases in bun install

Bun implements a node-semver compatible parser for bun install.

There was an edgecase when parsing invalid semver strings that contained a pre-release version with a dist-tag that would potentially cause Bun to crash. This issue has been fixed (thanks to @dylan-conway) and we've added more test coverage.

Misc

We also updated to the latest version of simdutf.

Thanks to 4 contributors

Full changelog: bun-v1.0.9...bun-v1.0.10