pwshub.com

Bun v1.1.31

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

This release fixes 41 bugs (addressing 595 👍). It includes node:http2 server and gRPC server support, ca and cafile support in bun install, Bun.inspect.table, bun build --drop, Promise.try, Buffer.copyBytesFrom, iterable SQLite queries, iterator helpers, and several Node.js compatibility improvements and bugfixes.

To install Bun

curl

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

powershell

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

docker

docker run --rm --init --ulimit memlock=-1:-1 oven/bun

To upgrade Bun

Support for node:http2 server and gRPC

In this release of Bun, we added support for our most upvoted feature request: HTTP2 server and gRPC support. Bun has supported the node:http2 client since v1.0.13, but we didn't support the server, until now.

In Bun, node:http2 runs 2.4x faster than in Node v23.

In the next version of Bun

node:http2 server support is implemented. For the same code:

Bun v1.1.31: 128,879 req/s (2.4x faster)
Node v23.0.0: 52,785 req/s pic.twitter.com/SIM0I0Td4T

— Bun (@bunjavascript) October 17, 2024

You can use the node:http2 API to create an HTTP2 server.

import { createSecureServer } from "node:http2";
import { readFileSync } from "node:fs";

const server = createSecureServer({
  key: readFileSync("privkey.pem"),
  cert: readFileSync("cert.pem"),
});

server.on("error", (error) => console.error(error));
server.on("stream", (stream, headers) => {
  stream.respond({
    ":status": 200,
    "content-type": "text/html; charset=utf-8",
  });
  stream.end("<h1>Hello from Bun!</h1>");
});

server.listen(3000);

With HTTP2 support, you can also use gRPC with packages like @grpc/grpc-js.

const grpc = require("@grpc/grpc-js");
const protoLoader = require("@grpc/proto-loader");
const packageDefinition = protoLoader.loadSync("benchmark.proto", {});
const proto = grpc.loadPackageDefinition(packageDefinition).benchmark;
const fs = require("fs");

function ping(call, callback) {
  callback(null, { message: "Hello, World" });
}

function main() {
  const server = new grpc.Server();
  server.addService(proto.BenchmarkService.service, { ping: ping });
  const tls =
    !!process.env.TLS &&
    (process.env.TLS === "1" || process.env.TLS === "true");
  const port = process.env.PORT || 50051;
  const host = process.env.HOST || "localhost";
  let credentials;
  if (tls) {
    const ca = fs.readFileSync("./cert.pem");
    const key = fs.readFileSync("./key.pem");
    const cert = fs.readFileSync("./cert.pem");
    credentials = grpc.ServerCredentials.createSsl(ca, [
      { private_key: key, cert_chain: cert },
    ]);
  } else {
    credentials = grpc.ServerCredentials.createInsecure();
  }
  server.bindAsync(`${host}:${port}`, credentials, () => {
    console.log(
      `Server running at ${tls ? "https" : "http"}://${host}:${port}`,
    );
  });
}

main();

CA support in bun install

You can now configure CA certificates for bun install. This is useful when you need to install packages from your company's private registry, or if you want to use self-signed certificate.

bunfig.toml

[install]
# The CA certificate as a string
ca = "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"

# A path to a CA certificate file. The file can contain multiple certificates.
cafile = "path/to/cafile"

If you don't want to change your bunfig.toml file, you can also use the --ca and --cafile flags.

bun install --cafile=/path/to/cafile

If you are using an existing .npmrc file, you can also configure CA certificates there.

.npmrc

cafile=/path/to/cafile
ca="..."

bun build --drop

You can use --drop to remove function calls from your JavaScript bundle. This is useful if you want to remove debug code from your production bundle. For example, if you pass --drop=console, all calls to console.log will be removed from your code.

JavaScript

await Bun.build({
  entrypoints: ["./index.tsx"],
  outdir: "./out",
  drop: ["console", "anyIdentifier.or.propertyAccess"],
})

CLI

bun build ./index.tsx --outdir ./out --drop=console --drop=anyIdentifier.or.propertyAccess

bun --drop

--drop also works at runtime.

index.ts

for (let i = 0; i < 3; i++) {
  console.log("hello called!");
}

Before --drop, you'd see hello called! 3 times.

hello called!
hello called!
hello called!

With --drop=console, console.log calls get dropped and you don't see hello called! anymore.

bun --drop=console ./index.ts

Bun.inspect.table()

You can now use Bun.inspect.table to format tabular data into a string. It is similar to console.table, except it returns a string rather than printing to the console.

console.log(
  Bun.inspect.table([
    { a: 1, b: 2, c: 3 },
    { a: 4, b: 5, c: 6 },
    { a: 7, b: 8, c: 9 },
  ]),
);

// ┌───┬───┬───┬───┐
// │   │ a │ b │ c │
// ├───┼───┼───┼───┤
// │ 0 │ 1 │ 2 │ 3 │
// │ 1 │ 4 │ 5 │ 6 │
// │ 2 │ 7 │ 8 │ 9 │
// └───┴───┴───┴───┘

You can also pass an array of property names to display only a subset of properties.

console.log(
  Bun.inspect.table(
    [
      { a: 1, b: 2, c: 3 },
      { a: 4, b: 5, c: 6 },
    ],
    ["a", "c"],
  ),
);

// ┌───┬───┬───┐
// │   │ a │ c │
// ├───┼───┼───┤
// │ 0 │ 1 │ 3 │
// │ 1 │ 4 │ 6 │
// └───┴───┴───┘

If you want to enable ANSI colors, you can set the colors property on the options object.

console.log(
  Bun.inspect.table(
    [
      { a: 1, b: 2, c: 3 },
      { a: 4, b: 5, c: 6 },
    ],
    {
      colors: true,
    },
  ),
);

Iterable SQLite queries

Bun has a built-in SQLite API, that makes it easy to query SQLite databases. Instead of returning an array of rows, you can now return an iterator that yields rows as they are returned from the database.

import { Database } from "bun:sqlite";

const sst = new Database(":memory:");
sst.run("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)");

class User {
  id: number;
  name: string;
  email: string;

  get nameAndEmail() {
    return `${this.name} <${this.email}>`;
  }
}

const rows = sst
  .query("SELECT * FROM users")
  .as(User)
  .iterate() // <--- here
  // call the .nameAndEmail property on each row
  .map((row) => row.nameAndEmail);

for (const row of rows) {
  console.log(row);
}

// prints nothing because sst has no users

You can also call the .iterate() method on a query to get an iterator that yields rows as they are returned from the database.

for (const row of db.query("SELECT * FROM users").iterate()) {
  console.log(row); // { id: 1, name: "Bun" }
}

Thanks to @Skywalker13 for implementing this!

Iterator helpers

In this release of Bun, there are new APIs that make it easier to work with JavaScript iterators and generators. These APIs were introduced in a TC39 proposal, and are now available in Safari and Chrome. Thanks to the WebKit team for implementing these APIs, especially @sosukesuzuki and @shvaikalesh!

iterator.map(fn)

Returns an iterator that yields the results of the fn function applied to each value of the original iterator, similar to Array.prototype.map.

function* range(start: number, end: number): Generator<number> {
  for (let i = start; i < end; i++) {
    yield i;
  }
}

const result = range(3, 5).map((x) => x * 2);
result.next(); // { value: 6, done: false }

iterator.flatMap(fn)

Returns an iterator that yields the values of the original iterator, but flattens the results of the fn function, similar to Array.prototype.flatMap.

function* randomThoughts(): Generator<string> {
  yield "Bun is written in Zig";
  yield "Bun runs JavaScript and TypeScript";
}

const result = randomThoughts().flatMap((x) => x.split(" "));
result.next(); // { value: "Bun", done: false }
result.next(); // { value: "is", done: false }
// ...
result.next(); // { value: "TypeScript", done: false }

iterator.filter(fn)

Returns an iterator that only yields values that pass the fn predicate, similar to Array.prototype.filter.

function* range(start: number, end: number): Generator<number> {
  for (let i = start; i < end; i++) {
    yield i;
  }
}

const result = range(3, 5).filter((x) => x % 2 === 0);
result.next(); // { value: 4, done: false }

iterator.take(n)

Returns an iterator that yields the first n values of the original iterator.

function* odds(): Generator<number> {
  let i = 1;
  while (true) {
    yield i;
    i += 2;
  }
}

const result = odds().take(1);
result.next(); // { value: 1, done: false }
result.next(); // { done: true }

iterator.drop(n)

Returns an iterator that yields all values of the original iterator, except the first n values.

function* evens(): Generator<number> {
  let i = 0;
  while (true) {
    yield i;
    i += 2;
  }
}

const result = evens().drop(2);
result.next(); // { value: 4, done: false }
result.next(); // { value: 6, done: false }

iterator.reduce(fn, initialValue)

Reduces the values of an iterator with a function, similar to Array.prototype.reduce.

function* powersOfTwo(): Generator<number> {
  let i = 1;
  while (true) {
    yield i;
    i *= 2;
  }
}

const result = powersOfTwo()
  .take(5)
  .reduce((acc, x) => acc + x, 0);
console.log(result); // 15

iterator.toArray()

Returns an array that contains all the values of the original iterator. Make sure that the iterator is finite, otherwise this will cause an infinite loop.

function* range(start: number, end: number): Generator<number> {
  for (let i = start; i < end; i++) {
    yield i;
  }
}

const result = range(1, 5).toArray();
console.log(result); // [1, 2, 3, 4]

iterator.forEach(fn)

Calls the fn function on each value of the original iterator, similar to Array.prototype.forEach.

function* randomThoughts(): Generator<string> {
  yield "Bun is written in Zig";
  yield "Bun runs JavaScript and TypeScript";
}

const result = randomThoughts().forEach((x) => console.log(x));
// Bun is written in Zig
// Bun runs JavaScript and TypeScript

iterator.find(fn)

Returns the first value of the original iterator that passes the fn predicate, similar to Array.prototype.find. If no such value exists, it returns undefined.

function* range(start: number, end: number): Generator<number> {
  for (let i = start; i < end; i++) {
    yield i;
  }
}

const result = range(0, 99).find((x) => x % 100 === 0);
console.log(result); // undefined

Promise.try

The Promise.try method is similar to Promise.resolve, but it also works with synchronous functions.

Promise.try(() => {
  return 1 + 1;
}).then((result) => {
  console.log(result); // 2
});

This is a stage4 TC39 proposal (i.e. it has graduated from proposal to standardized ECMAScript). Thanks to @rkirsling for implementing this!

9x faster Error.captureStackTrace

Warn on first idle request timeout

In the next version of Bun

Added a warning when a request times out automatically in Bun.serve() without explicitly passing idleTimeout and without using AbortSignal pic.twitter.com/krMmUNi14x

— Jarred Sumner (@jarredsumner) October 11, 2024

New error: Mismatched JSX closing tag

Previously, the following code would not throw an error, even though it's invalid JSX:

const Oopsie = () => {
  return (
    <h1>
      That should be an h1! Not a div.
    </div>
  );
};

We should have always thrown an error here, but we were not. Now we do:

❯ bun build Oopsie.tsx
5 |     </div>
          ^
error: Expected closing JSX tag to match opening tag "<h1>"
    at Oopsie.tsx:5:7

3 |     <h1>
         ^
note: Opening tag here:
   at Oopsie.tsx:3:6

Thanks to @DonIsaac for fixing this!

Node.js compatibility

New: Buffer.copyBytesFrom(view[, offset[, length]])

You can now use the Buffer.copyBytesFrom method to create a new Buffer by copying bytes from a typed array or DataView.

const u16 = new Uint16Array([0, 0xffff]);
const buf = Buffer.copyBytesFrom(u16, 1, 1);
u16[1] = 0;
console.log(buf.length); // 2
console.log(buf[0]); // 255
console.log(buf[1]); // 255

Thanks to @nektro for implementing this!

Fixed: fs.open()

We fixed various issues in fs.open(), so it more closely matches Node.js's behavior. Thanks to @nektro for fixing this!

Fixed: Node-API bugs

We've been working on fixing bugs in Bun's implementation of Node-API. Thanks to @190n for fixing these bugs!

  • napi_create_empty_array does not crash when the array is too large
  • napi_create_empty_array uses empty slots, instead of undefined values
  • napi_get_value_int32 uses the same bitwise conversions as in JavaScript
  • napi_get_value_int64 now matches Node.js's behavior
  • napi_throw_error and napi_create_error are now tested and don't sometimes crash

Fixed: Setting UV_THREADPOOL_SIZE

There are some Node.js packages that set the UV_THREADPOOL_SIZE environment variable to control the number of threads used by libuv. While Bun only uses libuv on Windows, and only for some APIs, it will now respect this environment variable.

Fixed: Buffer.from JSON serialization

When you call JSON.stringify on a Buffer, it adds {type: "Buffer", value: [...buffer contents]} to the JSON. This already worked, but Buffer.from would not accept this JSON. Now it does.

const buf = Buffer.from([1, 2, 3]);
JSON.stringify(buf); // "{"type":"Buffer","data":[1,2,3]}"
Buffer.from(JSON.parse('{"type":"Buffer","data":[1,2,3]}'))[0] === 1; // works

Previously, this would throw:

TypeError: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object.
      at [file]:3:20

Thanks to @nektro for fixing this!

Bugs squashed

Fixed: Format of console.table with numeric keys

We fixed a bug where console.table would format with incorrect spacing when the keys were numeric. Thanks to @pfgithub for fixing this!

console.table({test: {"10": 123, "100": 154}});

// Before
┌──────┬─────┬─────┐
│      │ 10100
├──────┼─────┼─────┤
│ test │     │     │
└──────┴─────┴─────┘

// After
┌─────────┬─────┬─────┐
│ (index) │ 10100
├─────────┼─────┼─────┤
│ test    │ 123154
└─────────┴─────┴─────┘

Fixed: Crash using Bun.color

In a recent release, we introduced Bun.color, an API that allows you to parse and transform colors in different formats, including css, ansi, hex, rgb, hsl, and more.

We fixed a bug where Bun.color would crash if you did not pass it a format string.

Fixed: Regression with instanceof

An instanceof regression introduced in Bun v1.1.30 through a WebKit upgrade has been fixed. The bug could be caused by JavaScript code storing the output value of instanceof in the same variable as the input. The error message would look like:

error: [BUG] Unreachable
      at ... (input.js:2247:47)

This bug was most often seen when using sass with source maps enabled.

Fixed thanks to @hyjorc1!

Fixed: Transpiler bug with using and generators

We fixed a bug where a variable declaration before a using statement would cause the transpiler to hoist a generator outside the scope of the using declaration. This would cause a ReferenceError, but has now been fixed thanks to @snoglobe.

Fixed: File permissions with bun install

Some npm packages are published with incorrect file permissions, we added a check in Bun to ensure that files extracted from an npm package are at-least readable. This matches the behavior of npm install.

Fixed: Missing metadata publishing with bun publish

A bug was fixed where bun publish was sending publish requests without important package metadata, including dependencies and binaries from bin or directories.bin. This would cause published packages to install incorrectly due to missing transitive dependencies and binaries in node_modules/.bin.

Fixed: empty string argument for fs.mkdir

An integer overflow caused by passing an empty string to fs.mkdir has been fixed. This would only happen when the recursive option was set to true.

const fs = require("fs");
fs.mkdirSync("", { recursive: true }); // throws ENOENT

Fixed: fetch keepalive option

The keepalive option for fetch was previously ignored. Now it disables the "Connection: keep-alive" header from being sent by default.

const res = await fetch("https://example.com", { keepalive: false });
console.log(res.headers.get("connection")); // "close"

Thanks to 19 contributors!

Source: bun.sh

Related stories
1 month ago - 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`...
1 month ago - This release fixes 40 bugs (addressing 51 👍). Compile & run C from JavaScript. 30x faster path.resolve. Named pipes on Windows. Several Node.js compatibility improvements and bugfixes.
3 weeks ago - Fixes 21 bugs (addressing 22 👍). Fixes issue with proxying requests with axios. N-API (napi) support in compiled C. Faster typed arrays in `bun:ffi` with 'buffer' type. Fixes issue with bundling imported .wasm files in `bun build`. Fixes...
1 week ago - Fixes 57 bugs (addressing 150 👍) Bun's CSS bundler is here (and very experimental). `bun publish` is a drop-in replacement for `npm publish`. `bun build --bytecode` compiles to bytecode leading to 2x faster start time. Bun.color()...
1 month ago - Fixes 9 bugs (addressing 652 👍). `bun oudated` shows outdated dependencies. Bun.serve() gets a new `idleTimeout` option and `subscriberCount(topic: string)` for a count of websocket clients subscribed to a topic. `test.only` in bun:test...
Other stories
7 hours ago - This guide provides a foundational understanding of Redux and why you should use it for state management in a React app. The post Understanding Redux: A tutorial with examples appeared first on LogRocket Blog.
9 hours ago - Discover some of the best Node.js web scraping libraries, including Axios and Superagent, and techniques for how to use them. The post The best Node.js web scrapers for your use case appeared first on LogRocket Blog.
12 hours ago - Infinite runner games have been a favorite for gamers and developers alike due to their fast-paced action and replayability. These games often feature engaging mechanics like endless levels, smooth character movement, and dynamic...
14 hours ago - Yesterday, Elizabeth Siegle, a developer advocate for CLoudflare, showed off a really freaking cool demo making use of Cloudflare's Workers AI support. Her demo made use of WNBA stats to create a beautiful dashboard that's then enhanced...
14 hours ago - User interviews are great — only if you don't rush them. In this piece, I share how using debrief questions in research can help capture better insights and improve your interview process. The post Using debrief questions to get the most...