Blog

Bun v1.4.2

This release fixes two regressions impacting Elysia and AsyncLocalStorage, a hang in @discordjs/ws, CMYK JPEG decoding in Bun.Image, a rare JIT crash, and a GC crash on musl.

To install Bun

curl

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

To upgrade Bun

bun upgrade

Fixed: bun build variable name collision#

A regression introduced in Bun v1.4.1 impacting Elysia caused bun build to rename a nested var to the same name as a let in the same block. Builds importing Elysia would fail to load with SyntaxError: Cannot declare a var variable that shadows a let/const/class variable. This has been fixed and a regression test has been added.

index.js

function foo () {
  {
    let exports2 = {};
    var exports = exports2;
  }
  return exports;
}
module.exports = foo();

bun build index.js --outfile out.js would output code containing:

out.js

// inside CJS module wrapper
function foo() {
  {
    let exports2 = {};
    var exports2 = exports2; // SyntaxError
  }
  return exports2;
}
module.exports = foo();

The same bug could also give a let the name of a function parameter or catch binding, producing code that evaluates but computes incorrect values.

Fixed: AsyncLocalStorage memory leak#

A regression introduced in Bun v1.4.1 caused a timer, immediate, or pending promise created inside store.exit() or a nested store.run() to keep the outer store value alive for as long as that timer or promise existed. getStore() still returned the correct value, so only memory usage was affected. This has been fixed.

const store = new AsyncLocalStorage();

store.run(bigPerRequestContext, () => {
  store.exit(() => setTimeout(() => {}, 3_600_000));
  // kept bigPerRequestContext alive for an hour
});

Fixed: worker_threads 'online' event order#

A Worker from node:worker_threads didn't emit 'online' first, causing @discordjs/ws to hang due to missing a worker's first message. The order now matches Node.js.

import { Worker, isMainThread, parentPort } from "worker_threads";
import { once } from "events";

if (isMainThread) {
  const worker = new Worker(new URL(import.meta.url));
  await once(worker, "online");
  worker.on("message", (msg) => {
    console.log(msg); // never received in v1.4.1
  });
} else {
  parentPort.postMessage("hi");
}

Fixed: Bun.Image decodes CMYK and YCCK JPEGs#

Bun.Image now decodes 4-component CMYK and YCCK JPEGs. Previously these failed with Image: decode failed. They are converted to RGB on decode, so every transform and output format works.

await new Bun.Image("photo-cmyk.jpg").resize(400, 400).webp().bytes();

Upgraded JavaScriptCore#

Bun v1.4.2 pulls in about 350 more upstream WebKit commits. They include Intl and TypedArray correctness fixes, a fix for a Proxy crash, and cheaper Date objects.

new Intl.PluralRules("en").select(1n); // => "one" (was TypeError)
  • new Int32Array(array) (and the other TypedArray constructors) read a stale or missing element when an element's valueOf mutated array during conversion.
  • TypedArray.prototype.slice into a Symbol.species view that overlaps its source on the same buffer now copies in spec order.
  • Intl.DurationFormat with style: "digital" no longer prints a stray : when minutes is 0 and hidden.
  • Object.setPrototypeOf(handler, null) on a Proxy handler that had already served a trap could crash when the handler came from a class defined inside a function called many times.

Bugfixes#

  • Fixed: a rare crash in long-running processes after a prototype that JIT-optimized code had cached property lookups through was garbage-collected.
  • Fixed: on musl (Alpine), Array.prototype.splice, shift, or shrinking array.length on an array of objects could crash on a GC thread or hang if it ran while the GC was marking.
  • Fixed: .json() on a Response, Blob, Bun.file(), or proc.stdout rejected invalid JSON with a generic Failed to parse JSON instead of the SyntaxError message JSON.parse gives, such as JSON Parse error: Expected '}'.
  • Fixed: bun install and bun add could panic with range end index out of range when bun.lockb or a cached registry manifest stored a package-name hash that didn't match the name.
  • Fixed: on Linux, if registering a Bun.file().writer() FileSink with epoll failed (for example fs.epoll.max_user_watches exhausted), its file descriptor was closed twice, which could close an unrelated descriptor that reused the number.

Thanks to 3 contributors!#