bun

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one

Downloads
4.5M
Stars
70.2K
Committers
681

Bot releases are visible (Hide)

bun - Bun v0.1.13 (fixes a regression)

Published by Jarred-Sumner about 2 years ago

To upgrade:

bun upgrade

To install:

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

Run the install script (you can run it multiple times):

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

This fixes a regression introduced in Bun v0.1.12 where fetch() with a body would fail to write the body when the body was small. This also adds keepalive: false and timeout: false to fetch as options.

Please see Bun v0.1.12's release notes, as a lot more changed there.

bun - Bun v0.1.12

Published by Jarred-Sumner about 2 years ago

To upgrade:

bun upgrade

To install:

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

Run the install script (you can run it multiple times):

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

bun install gets faster & more reliable

Previously, bun install on Linux would crash or hang at least 1 out of every 80,000 cold package installs πŸ™ˆ. It was particularly bad for download speeds < 100 mbps. Sometimes there were DNS hostname resolution issues as well (which caused a separate crash). If you were on a Linux kernel earlier than v5.5 or had a low memlock limit (e.g. using Ubuntu 18.04), it just wouldn't work at all.

Bun v0.1.12 fixes that.

To end-to-end test this, I had a computer cold install 512 MB of node_modules for 2 hours in a loop (at least 800,000 cold package installs) and it installed successfully every time without crashing or hanging (unlike Bun v0.1.11 and earlier)

The first run installed 100 times and the second 1,000 times.

image

Four different things caused stability issues:

  • A deadlock in the code that waits for async work to complete (extracting files and parsing JSON happen in a threadpool). This was fixed by (1) moving to a multi-producer single-consumer lockfree queue for HTTP requests and (2) using either a eventfd or a machport to notify readiness instead of a futex.
  • A bug in the HTTP client when suspending/resuming from work that occurred asynchronously. This was fixed while rewriting the HTTP client (it no longer uses Zig's async/await)
  • A crash when reading an empty JSON file. This was a trivial fix.
  • A crash when DNS resolution failed for any reason. This was fixed while rewriting the HTTP client because now it uses libc's getaddrinfo

Many of these issues applied to macOS as well (excluding the memlock limit one)

There are still important missing features in bun install, including support for git dependencies, github dependencies, npm: package aliasing, and workspaces support. Those are just not implemented yet, which is different than stability issues with existing features.

fetch() gets faster & more reliable

The eventing code for Bun's HTTP client has been rewritten to use uSockets and that, along with changes to concurrent task scheduling and HTTP keep-alive made sending HTTP requests in Bun faster & more reliable.

Bun's fetch() can send 155,000 requests per second on Linux x64:

  • 24x higher throughput than in Node v18.9.0
  • 6.2x higher throughput than in Bun v0.1.11
  • 4.5x higher throughput than in Deno v1.25.3

Code

The performance of fetch in Bun seems to be within 25% of optimized native HTTP benchmarking tools like oha and bombardier (autocannon, the more popular choice, only reaches 60k req/s)

read.u8 in bun:ffi

read in bun:ffi lets you read pointers without creating a new DataView. This helps write faster libraries.

import {read, ptr} from 'bun:ffi';
import {it, expect} from 'bun:test';

it("read", () => {
  const buffer = new BigInt64Array(16);
  const dataView = new DataView(buffer.buffer);
  const addr = ptr(buffer);

  for (let i = 0; i < buffer.length; i++) {
    buffer[i] = BigInt(i);
    expect(read.intptr(addr, i * 8)).toBe(
      Number(dataView.getBigInt64(i * 8, true))
    );
    expect(read.ptr(addr, i * 8)).toBe(
      Number(dataView.getBigUint64(i * 8, true))
    );
    expect(read.f64(addr, i + 8)).toBe(dataView.getFloat64(i + 8, true));
    expect(read.i64(addr, i * 8)).toBe(dataView.getBigInt64(i * 8, true));
    expect(read.u64(addr, i * 8)).toBe(dataView.getBigUint64(i * 8, true));
  }

  for (let i = 0; i < buffer.byteLength - 4; i++) {
    // read is intended to behave like DataView
    // but instead of doing
    //    new DataView(toArrayBuffer(myPtr)).getInt8(0, true)
    // you can do
    //    read.i8(myPtr, 0)
    expect(read.i8(addr, i)).toBe(dataView.getInt8(i, true));
    expect(read.i16(addr, i)).toBe(dataView.getInt16(i, true));
    expect(read.i32(addr, i)).toBe(dataView.getInt32(i, true));
    expect(read.u8(addr, i)).toBe(dataView.getUint8(i, true));
    expect(read.u16(addr, i)).toBe(dataView.getUint16(i, true));
    expect(read.u32(addr, i)).toBe(dataView.getUint32(i, true));
    expect(read.f32(addr, i)).toBe(dataView.getFloat32(i, true));
  }
});

Implementing this involved a change to WebKit's DOMJIT to enable 52-bit integer arguments (pointers)

This involved an unlikely but potentially breaking change to the pointer representation in bun:ffi. Previously, bun:ffi pointers stored the memory address at the end of a JavaScript double (bit-casted a double to a 64-bit signed integer) and now the value is stored in the integer part of the double. This would only be a breaking change for libraries relying on the pointer representation in napi

String.prototype.replace gets 2x faster

String.prototype.replace gets 2x faster in Safari & Bun, thanks to @Constellation. It affects code like this:

myString.replace("foo", "baz")

PRs:

This version of Bun updates to the latest WebKit as of September 17th, 2022 (which includes these PRs)

Faster crypto.getRandomValues and crypto.randomUUID

crypto.getRandomValues now uses BoringSSL's optimized random functions

Note: this screenshot was taken before bun's version was bumped to v0.1.12 which is why it shows v0.1.11 there

image

More

Full Changelog: https://github.com/oven-sh/bun/compare/bun-v0.1.11...bun-v0.1.12

bun - Bun v0.1.11

Published by Jarred-Sumner about 2 years ago

To upgrade:

bun upgrade

To install:

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

Run the install script (you can run it multiple times):

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

In Bun v0.1.11, web framework libraries built on Bun got faster

Benchmark: https://github.com/SaltyAom/bun-http-framework-benchmark
This was run on Linux
Image credit: @Kapsonfire-DE

Plugin API

Bun's runtime now supports a plugin API.

  • import and require .svelte, .vue, .yaml, .scss, .less and other file extensions that Bun doesn't implement a builtin loader for
  • Dynamically generate ESM & CJS modules

The API is loosely based on esbuild's plugin API.

This code snippet lets you import .mdx files in Bun:

import { plugin } from "bun";
import { renderToStaticMarkup } from "react-dom/server";

// Their esbuild plugin runs in Bun (without esbuild)
import mdx from "@mdx-js/esbuild";
plugin(mdx());

// Usage
import Foo from "./bar.mdx";
console.log(renderToStaticMarkup(<Foo />));

This lets you import yaml files:

import { plugin } from "bun";

plugin({
  name: "YAML",

  setup(builder) {
    const { load } = require("js-yaml");
    const { readFileSync } = require("fs");
    // Run this function on any import that ends with .yaml or .yml
    builder.onLoad({ filter: /\.(yaml|yml)$/ }, (args) => {
      // Read the YAML file from disk
      const text = readFileSync(args.path, "utf8");

      // parse the YAML file with js-yaml
      const exports = load(text);

      return {
        // Copy the keys and values from the parsed YAML file into the ESM module namespace object
        exports,

        // we're returning an object
        loader: "object",
      };
    });
  },
});

We're planning on supporting browser builds with this plugin API as well (run at transpilation time)

Reliability improvements

Node compatibility:

macOS event loop internals moved to a more reliable polling mechanism:

More:

  • [bun:ffi] Fix crash with uint64_t 296fb41e920736041c6c1dec38f1d8c355a402a1
  • [bun:ffi] Fix int16 / uin16 max 30992a8b051565ace57083b990d010316d56605d
  • Fix Request and Response in macros e0b35b3086b00fb27f950a72a082b360a3dad891
  • Fix clearTimeout on Linux e6a1209c53adb3056263b894d774b30ee70a3188

Performance improvements

Bun has a long-term commitment to performance. On macOS, React server-side rendering gets around 2x faster.

Coming up next in performance improvements: a new HTTP server implementation. Not far enough along for this release, but experiments are showing progress.

PRs

New Contributors

Full Changelog: https://github.com/oven-sh/bun/compare/bun-v0.1.10...bun-v0.1.11

bun - Bun v0.1.10

Published by Jarred-Sumner about 2 years ago

To upgrade:

bun upgrade

To install:

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

Run the install script (you can run it multiple times):

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

What's new

  • Fix a regression causing bun dev to not send HTTP bodies
  • Fix console.log printing [native code] for too many things 5eeb704f25200af7ad8819c35bcfd16b8b1bff49
  • Fix crash in bun dev when used with Next.js e45ddc086fe6b3e7a32aa45607f5e3d570998137
  • Fix bug with Buffer.compare https://github.com/oven-sh/bun/commit/d150a2f4ddc10597e4531fd3c55b62bb0ecbf02c
  • Make TextDecoder 2.5x faster e3c2a95e5ff4e6c6b63839f4773cc3f5aeadddc8
  • Fix memory leak in WebSocket bdf733973c72b8e156cd1cf1c6c8a8b4649fedbe
  • Make Request, Response and TextDecoder globals not read-only 0f45386673fbf4f33b6e61b17ea49b69697ec79a

Full Changelog: https://github.com/oven-sh/bun/compare/bun-v0.1.9...bun-v0.1.10

bun - Bun v0.1.9

Published by Jarred-Sumner about 2 years ago

To upgrade:

bun upgrade

To install:

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

Run the install script (you can run it multiple times):

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

What's new

  • Numerous crashes fixed by @zhuzilin!! Incredible work.
  • [Linux] Improved event loop reliability and reduced CPU usage for concurrent/async IO (affects bun install and fetch() mostly)
  • Bun.serve gets about 20% faster outside of "hello world" benchmarks due to optimizing how Headers are copied/read and faster generated bindings
  • require("buffer") and require("process") now point to Bun's implementation instead of a browserify polyfill (thanks @zhuzilin)
  • Fixed a bug that would cause setTimeout or setInterval to not keep the process alive (thanks @zhuzilin)
  • Updated to latest WebKit
  • 6x - 10x faster ptr() in bun:ffi

JIT-optimized TextEncoder.encodeInto can be a 1.5x perf boost up to around a 5x perf boost

The hash() function in this microbenchmark calls ptr():

Internals

Bun learns how to JIT

DOMJIT is a JavaScriptCore API that gives 3rd-party embedders low-level access to the JIT compiler/assembler to optimize native functions and getters/setters. Safari leverages DOMJIT to make commonly-accessed properties like element.parentNode faster

Bun is beginning to use DOMJIT now, starting in two places:

To better support Bun's usecase, I extended DOMJIT to support Typed Array arguments, as well as added support for specifying more kinds of side effects that enable/disable optimizations:

Faster and more reliable JSC <> Zig bindings

At Bun's compile-time, Bun now code-generates C++ binding classes for JavaScript objects implemented in Zig. Previously, Bun mostly used the JavaScriptCore C API.

Using JavaScriptCore's C++ API improves performance and is important for making the garbage collector better aware of Bun's usage. But, writing bindings manually can be very repetitive.

Given a class definition in JavaScript like this:

define({
    name: "Response",
    construct: true,
    finalize: true,
    JSType: "0b11101110",
    klass: {
      json: {
        fn: "constructJSON",
      },
     // rest of the code
    },
    proto: {
      url: {
        getter: "getURL",
        cache: true,
      },

      text: { fn: "getText" },
      json: { fn: "getJSON" },
      arrayBuffer: { fn: "getArrayBuffer" },
      blob: { fn: "getBlob" },
      clone: { fn: "doClone", length: 1 },
     // rest of the code
    },
  })

Bun generates corresponding:

This approach is inspired by WebIDL bindings which both Safari and Chromium use.

More reliable event loop on Linux

This screenshot is with a simulated bandwidth limit and no throttling of max http connections

Previously, bun had a tendency to hang in situations like this

image

What's Changed

New Contributors

Full Changelog: https://github.com/oven-sh/bun/compare/bun-v0.1.8...bun-v0.1.9

bun - Bun v0.1.8

Published by Jarred-Sumner about 2 years ago

To upgrade:

bun upgrade

To install:

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

Run the install script (you can run it multiple times):

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

What's new

A huge thank you to @zhuzilin for all their help on this release. @zhuzilin fixed 4 crashes!

bun link lets you symlink a folder to node_modules. It works like npm link.

fs.copyFileSync gets 2x to 10x faster:

require.resolve works at runtime now instead of only build-time

WebSocket is more reliable now. Previously the garbage collector would attempt to free it when the socket was still open πŸ™‰

bun:ffi's toBuffer and toArrayBuffer functions now support a function pointer to a destructor so that native code can perform cleanup without needing to go through a FinalizationRegistry.

console.log

TypedArray logs the value for the type (instead of in bytes πŸ™ˆ)

console.log(MessageEvent ) is more useful now

More:

  • setInterval wouldn't cause the process to stay alive 😒 and now that is fixed thanks to @zhuzilin
  • Log error on unhandled rejected promises by @zhuzilin in https://github.com/oven-sh/bun/pull/1010
  • Log error on uncaught exceptions in event loop
  • bun install gets a symlink backend, which you probably don't want to use in most cases. It's used internally if you do file:./ as a dependency, which some packages do
  • process.revision returns the git sha used to build bun

Bug fixes

Typos:

Misc:

Full Changelog: https://github.com/oven-sh/bun/compare/bun-v0.1.7...bun-v0.1.8

bun - bun v0.1.7

Published by Jarred-Sumner about 2 years ago

To upgrade:

bun upgrade

To install:

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

Run the install script (you can run it multiple times):

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

What's new

bun init quickly start a new, empty project that uses Bun (similar to npm init). bun init is a new subcommand in bun.

bun install now supports private npm registries & scoped (authenticated) packages

Thank you @soneymathew for your help with this.

bun install now supports lifecycle hooks for project-level package.json (not dependencies)

It runs postinstall scripts for your app's package.json, but ignores dependencies lifecycle hooks. This lets you use husky, lint-staged, and other postinstall-dependent packages tools

More new stuff:

  • express is partially supported, thanks to @zhuzilin and @evanwashere. There is a lot more work to be done - it's not fast yet and it logs a spurious error on request, but it is better than not working
  • bun create now lets you specify a start command so that you can say how to run the program in the output
  • process.revision has the git sha that bun was built with

Breaking changes

  • bun install will invalidate the lockfiles on upgrade if it exists. Unfortunately, this is necessary to support private/scoped package installs

Bug fixes

Misc:

New Contributors

Full Changelog: https://github.com/oven-sh/bun/compare/bun-v0.1.6...bun-v0.1.7

bun - bun v0.1.6

Published by Jarred-Sumner about 2 years ago

To upgrade:

bun upgrade

Run this:

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

What's new

  • No more "Illegal Instruction" error on start for those using CPUs which don't support AVX/AVX2 instructions! Thanks to bun's new baseline builds, these are separate builds of bun for Linux x64 and macOS x64 which do not use AVX/AVX2 instructions. You can install with the install script. This was one of the most common issues people ran into.
  • Add util.TextEncoder by @soneymathew in https://github.com/oven-sh/bun/pull/844
  • fix(ffi): double-free segfault with symbols object by @sno2 in https://github.com/oven-sh/bun/pull/919
  • -profile builds of bun include debug symbols
  • Update bun-framework-next for Compatibility with Next 12.2+ by @TiKevin83 in https://github.com/oven-sh/bun/pull/920

Thanks to upgrading WebKit:

  • 3.5x faster JSON.stringify (thanks @darinadler)
  • 396x faster TypedArray.from (thanks @Constellation)
  • 1.5x faster Uint8Array.slice() (thanks @Constellation)
  • Up to 2% faster JS execution on Linux due to enabling new memory allocator (libpas, thanks @Constellation)

Commits

New Contributors

Full Changelog: https://github.com/oven-sh/bun/compare/bun-v0.1.5...bun-v0.1.6

bun - bun v0.1.5

Published by Jarred-Sumner about 2 years ago

To upgrade:

bun upgrade

Run this:

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

This release is mostly just bug fixes. There is also a Linux arm64 build available (not for Android arm64 yet, but this should work for raspberry pi's)

Bug fixes:

New features

Two new flags added to bun install:

	    --no-progress              	Disable the progress bar
	    --no-verify                	Skip verifying integrity of newly downloaded packages

Misc:

Other:

New Contributors

Full Changelog: https://github.com/oven-sh/bun/compare/bun-v0.1.4...bun-v0.1.5

bun - Canary (37edd5a6e389265738e89265bcbdf2999cb81a49)

Published by github-actions[bot] about 2 years ago

This canary release of Bun corresponds to the commit [37edd5a6e389265738e89265bcbdf2999cb81a49]

bun - Bun v0.1.4

Published by Jarred-Sumner over 2 years ago

To upgrade:

bun upgrade

Run this:

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

Bug fixes

Misc:

Typos, README, examples:

New Contributors

Full Changelog: https://github.com/oven-sh/bun/compare/bun-v0.1.3...bun-v0.1.4

bun - bun v0.1.3

Published by Jarred-Sumner over 2 years ago

What's new:

  • alert(), confirm(), prompt() are new globals, thanks to @sno2
  • more comprehensive type definitions, thanks to @Snazzah
  • Fixed console.log() sometimes adding an "n" to non-BigInt numbers thanks to @FinnRG
  • Fixed subarray() console.log bug
  • TypedArray logs like in node now instead of like a regular array
  • Migrate to Zig v0.10.0 (HEAD) by @alexkuz in https://github.com/oven-sh/bun/pull/491
  • console.log(request) prints something instead of nothing
  • fix performance.now() returning nanoseconds instead of milliseconds @Pruxis
  • update wordmark in bun-error @Snazzah

All the PRs:

New Contributors

Full Changelog: https://github.com/oven-sh/bun/compare/bun-v0.1.2...bun-v0.1.3

bun - Bun v0.1.2

Published by Jarred-Sumner over 2 years ago

To upgrade:

bun upgrade

Run this:

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

What's Changed

bun install:

  • Fix error: NotSameFileSystem
  • When the Linux kernel version doesn't support io_uring, print instructions for upgrading to the latest Linux kernel on Windows Subsystem for Linux

bun.js:

bun dev:

Examples:

Landing page:

Internal:

README:

New Contributors

Full Changelog: https://github.com/Jarred-Sumner/bun/compare/bun-v0.1.1...bun-v0.1.2

bun - Bun v0.1.1

Published by Jarred-Sumner over 2 years ago

What's New

  • Web Streams - ReadableStream, WritableStream, TransformStream and more. This was a massive project. There are still some reliability things to fix, but I'm very happy with the performance, and I think you will be too.
  • WebSocket is now a global, powered by a custom WebSocket client
  • Dynamic require()
  • 50% faster TextEncoder
  • ~30% faster React rendering in production due to a JSX transpiler optimization
  • Streaming React SSR is now supported
  • Dozens of small bug fixes to the HTTP server
  • Discord interactions template by @evanwashere in https://github.com/Jarred-Sumner/bun/pull/2
  • Group zsh completion options by type by @alexkuz in https://github.com/Jarred-Sumner/bun/pull/194

New Contributors

Full Changelog: https://github.com/Jarred-Sumner/bun/compare/bun-v0.0.83...bun-v0.1.0

bun - bun v0.0.83

Published by Jarred-Sumner over 2 years ago

To upgrade:

bun upgrade

You can also try the install script.

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

Thanks to:

  • @kriszyp for tons of helpful feedback on how to improve Node-API support in bun
  • @evanwashere for the idea & name for the CFunction abstraction
  • @adaptive for replacing "wrangler@beta" with "wrangler" in the examples for bun add
  • @littledivy for adding a couple missing packages to the build instructions

bun:sqlite

bun:sqlite is a high-performance builtin SQLite module for bun.js.

It tends to be around 3x faster than the popular better-sqlite3 npm package

Note: in the benchmark I tweeted earlier, better-sqlite3 always returned arrays of arrays rather than arrays of objects, which was inconsistent with what bun:sqlite & deno's x/sqlite were doing

Usage

import { Database } from "bun:sqlite";

const db = new Database("mydb.sqlite");
db.run(
  "CREATE TABLE IF NOT EXISTS foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)"
);
db.run("INSERT INTO foo (greeting) VALUES (?)", "Welcome to bun!");
db.run("INSERT INTO foo (greeting) VALUES (?)", "Hello World!");

// get the first row
db.query("SELECT * FROM foo").get();
// { id: 1, greeting: "Welcome to bun!" }

// get all rows
db.query("SELECT * FROM foo").all();
// [
//   { id: 1, greeting: "Welcome to bun!" },
//   { id: 2, greeting: "Hello World!" },
// ]

// get all rows matching a condition
db.query("SELECT * FROM foo WHERE greeting = ?").all("Welcome to bun!");
// [
//   { id: 1, greeting: "Welcome to bun!" },
// ]

// get first row matching a named condition
db.query("SELECT * FROM foo WHERE greeting = $greeting").get({
  $greeting: "Welcome to bun!",
});
// [
//   { id: 1, greeting: "Welcome to bun!" },
// ]

There are more detailed docs in Bun's README

bun:sqlite's API is loosely based on @joshuawise's better-sqlite3

New in bun:ffi

CFunction lets you call native library functions from a function pointer.

It works like dlopen but its for cases where you already have the function pointer so you don't need to open a library. This is useful for:

  • callbacks passed from native libraries to JavaScript
  • using Node-API and bun:ffi together
 import {CFunction} from 'bun:ffi';

 const myNativeLibraryGetVersion: number | bigint = /* Somehow you got this function pointer */

 const getVersion = new CFunction({
   returns: "cstring",
   args: [],
   // ptr is required
   // this is where the function pointer goes!
   ptr: myNativeLibraryGetVersion,
 });
 getVersion();
 getVersion.close();

linkSymbols is like CFunction except for when there are multiple functions. It returns the same object as dlopen except ptr is required and there is no path

import { linkSymbols } from "bun:ffi";

const [majorPtr, minorPtr, patchPtr] = getVersionPtrs();

const lib = linkSymbols({
  // Unlike with dlopen(), the names here can be whatever you want
  getMajor: {
    returns: "cstring",
    args: [],

    // Since this doesn't use dlsym(), you have to provide a valid ptr
    // That ptr could be a number or a bigint
    // An invalid pointer will crash your program.
    ptr: majorPtr,
  },
  getMinor: {
    returns: "cstring",
    args: [],
    ptr: minorPtr,
  },
  getPatch: {
    returns: "cstring",
    args: [],
    ptr: patchPtr,
  },
});

const [major, minor, patch] = [
  lib.symbols.getMajor(),
  lib.symbols.getMinor(),
  lib.symbols.getPatch(),
];

new CString(ptr) should be a little faster due to using a more optimized function for getting the length of a string.

require.resolve()

Running require.resolve("my-module") in Bun.js will now resolve the path to the module. Previously, this was not supported.

In browsers, it becomes the absolute filepath at build-time. In node, it's left in without any changes.

Internally, Bun's JavaScript transpiler transforms it to:

// input:
require.resolve("my-module");

// output
import.meta.resolveSync("my-module");

You can see this for yourself by running bun build ./file.js --platform=bun

"node:module" module polyfill

Node's "module" module lets you create require functions from ESM modules.

Bun now has a polyfill that implements a subset of the "module" module.

Normally require() in bun transforms statically at build-time to an ESM import statement. That doesn't work as well for Node-API (napi) modules because they cannot be statically analyzed by a JavaScript parser (since they're not JavaScript).

For napi modules, bun uses a dynamic require function and the "module" module exports a way to create those using the same interface as in Node.js

import { createRequire } from "module";
// this also works:
//import {createRequire} from 'node:module';

var require = createRequire(import.meta.url);

require.resolve("my-module");

// dynamic require is supported for:
// - .json files
// - .node files (napi modules)
require("my-napi-module");

This is mostly intended for improving Node-API compatibility with modules loaded from ESM.

As an extra thing, you can also use require() this way for .json files.

Bun.Transpiler – pass objects to macros

Bun.Transpiler now supports passing objects to macros.

import { Transpiler } from "bun";
import { parseCookie } from "my-cookie-lib";
import { Database } from "bun:sqlite";

const transpiler = new Transpiler();
const db = new Database("mydb.sqlite");

export default {
  fetch(req) {
    const transpiled = transpiler.transformSync(
      `
import {getUser} from 'macro:./get-user';
export function Hello({name}) {
    return <div>Hello {name}</div>;
}

export const HelloCurrentUser = <Hello {...getUser()} />;
`,
      // passing contextual data to Bun.Transpiler
      {
        userId: parseCookie(req.headers.get("Cookie")).userId,
        db: db,
      }
    );
    return new Response(transpiled, {
      headers: { "Content-Type": "application/javascript" },
    });
  },
};

Then, in get-user.js:

// db, userId is now accessible in macros
export function getUser(expr, { db, userId }) {
  // we can use it to query the database while transpiling
  return db.query("SELECT * FROM users WHERE id = ? LIMIT 1").get(userId);
}

That inlines the returned current user into the JavaScript source code, producing output equivalent to this:

export function Hello({ name }) {
  return <div>Hello {name}</div>;
}

// notice that the current user is inlined rather than a function call
export const HelloCurrentUser = <Hello name="Jarred" />;

Bug fixes

  • Buffer.from(arrayBuffer, byteOffset, length) now works as expected (thanks to @kriszyp for reporting)

Misc

  • Receiving and sending strings to Node-API modules should be a little faster
bun - bun v0.0.81

Published by Jarred-Sumner over 2 years ago

To upgrade:

bun upgrade

Bun.js gets Node-API support

Node-API is 1.75x - 3x faster in Bun compared to Node.js 18 (in call overhead)

Getters & setters:
image

Simple function calls:

Just like in Node.js, to load a Node-API module, use require('my-npm-package') or use process.dlopen.

90% of the API is implemented, though it is certainly buggy right now.

Polyfills & new APIs in Bun.js v0.0.81

The following functions have been added:

  • import.meta.resolveSync synchronously run the module resolver for the currently-referenced file
  • import.meta.require synchronously loads .node or .json modules and works with dynamic paths. This doesn't use ESM and doesn't run the transpiler, which is why regular js files are not supported. This is mostly an implementation detail for how require works for Node-API modules, but it could also be used outside of that if you want
  • Bun.gzipSync, Bun.gunzipSync, Bun.inflateSync, and Bun.deflateSync which expose native bindings to zlib-cloudflare. On macOS aarch64, gzipSync is ~3x faster than in Node. This isn't wired up to the "zlib" polyfill in bun yet

Additionally:

  • __dirname is now supported for all targets (including browsers)
  • __filename is now supported for all targets (including browsers)
  • Buffer.byteLength is now implemented

Several packages using Node-API also use detect-libc. Bun polyfills detect-libc because bun doesn't support child_process yet and this improves performance a little.

Bug fixes

  • Fix incorrect error when new.target is referenced outside of a constructor d1ea51e9f2bfecd696224f4d715a8955a6300440
  • Fix incorrect error with negative numbers in the JSON parser 123267685f57968e3515841a8c2d6991c44dcfa8
bun - bun v0.0.79

Published by Jarred-Sumner over 2 years ago

To upgrade:

bun upgrade

Try running this:

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

Highlights

  • "bun:ffi" is a new bun.js core module that lets you use third-party native libraries written in languages that support the C ABI (Zig, Rust, C/C++ etc). It's like a foreign function interface API but faster
  • Buffer (like in Node.js) is now a global, but the implementation is incomplete - see tracking issue. If you import "buffer", it continues to use the browser polyfill so this shouldn't be a breaking change
  • 2x faster TextEncoder & TextDecoder thanks to some fixes to the vectorization (SIMD) code
  • Faster TypedArray.from. JavaScriptCore's implementation of TypedArray.from uses the code path for JS iterators when it could instead use an optimized code path for copying elements from an array, like V8 does. I have filed an upstream bug with WebKit about this, but I expect to do a more thorough fix for this in Bun and upstream that. For now, Bun reuses TypedArray.prototype.set when possible
  • 17x faster Uint8Array.fill
  • Bun.Transpiler gets an API for removing & replacing exports
  • SHA512, SHA256, SHA128, and more are now exposed in the "bun" module and the Bun global. They use BoringSSL's optimized hashing functions.
  • Fixed a reliability bug with new Response(Bun.file(path))
  • Bun's HTTP server now has a stop() function. Before, there was no way to stop it without terminating the process πŸ˜†
  • @evanwashere expose mmap size and offset option
  • @jameslahm [node] Add more fs constants

The next large project for bun is a production bundler Tracking issue

New Contributors

bun:ffi

The "bun:ffi" core module lets you efficiently call native libraries from JavaScript. It works with languages that support the C ABI (Zig, Rust, C/C++, C#, Nim, Kotlin, etc).

Get the locally-installed SQLite version number:

import { dlopen, CString, ptr, suffix, FFIType } from "bun:ffi";

const sqlite3Path = process.env.SQLITE3_PATH || `libsqlite3.${suffix}`;
const {
  symbols: { sqlite3_libversion },
} = dlopen(sqlite3Path, {
  sqlite3_libversion: {
    returns: "cstring",
  },
});
console.log("SQLite version", sqlite3_libversion());

FFI is really exciting because there is no runtime-specific code. You don't have to write a Bun FFI module (that isn't a thing). Use JavaScript to write bindings to native libraries installed with homebrew, with your linux distro's package manager or elsewhere. You can also write bindings to your own native code.

FFI has a reputation of being slower than runtime-specific APIs like napi – but that's not true for bun:ffi.

image

Bun embeds a small C compiler that generates code on-demand and converts types between JavaScript & native code inline. A lot of overhead in native libraries comes from function calls that validate & convert types, so moving that to just-in-time compiled C using engine-specific implementation details makes that faster. Those C functions are called directly – there is no extra wrapper in the native code side of things.

Some bun:ffi usecases:

  • SQLite client
  • Skia bindings so you can use Canvas API in bun.js
  • Clipboard api
  • Fast ffmpeg recording/streaming
  • Postgres client (possibly)
  • Use Pythons "ndarray" package from JavaScript (ideally via ndarray's C API and not just embedding Python in bun)

Later (not yet), bun:ffi will be integrated with bun's bundler and that will enable things like:

  • Use hermes to strip Flow types for code transpiled in bun
  • .sass support

Buffer

A lot of Node.js' Buffer module is now implemented natively in Bun.js, but it's not complete yet.

Here is a comparison of how long various functions take.

Replace & eliminate exports with Bun.Transpiler

For code transpiled with Bun.Transpiler, you can now remove and/or replace exports with a different value.

const transpiler = new Bun.Transpiler({
  exports: {
    replace: {
      // Next.js does this
      getStaticProps: ["__N_SSG", true],
    },
    eliminate: ["localVarToRemove"],
  },
  treeShaking: true,
  trimUnusedImports: true,
});

const code = `
import fs from "fs";
export var localVarToRemove = fs.readFileSync("/etc/passwd");
import * as CSV from "my-csv-parser";

export function getStaticProps() {
  return {
    props: { rows: CSV.parse(fs.readFileSync("./users-list.csv", "utf8")) },
  };
}

export function Page({ rows }) {
  return (
    <div>
      <h1>My page</h1>
      <p>
        <a href="/about">About</a>
      </p>
      <p>
        <a href="/users">Users</a>
      </p>
      <div>
        {rows.map((columns, index) => (
          <span key={index}>{columns.join(" | ")} </span>
        ))}
      </div>
    </div>
  );
}
`;

console.log(transpiler.transformSync(code));

Which outputs (this is the automatic react transform)

export var __N_SSG = true;

export function Page({ rows }) {
  return jsxDEV("div", {
    children: [
      jsxDEV("h1", {
        children: "My page"
      }, undefined, false, undefined, this),
      jsxDEV("p", {
        children: jsxDEV("a", {
          href: "/about",
          children: "About"
        }, undefined, false, undefined, this)
      }, undefined, false, undefined, this),
      jsxDEV("p", {
        children: jsxDEV("a", {
          href: "/users",
          children: "Users"
        }, undefined, false, undefined, this)
      }, undefined, false, undefined, this),
      jsxDEV("div", {
        children: rows.map((columns, index) => jsxDEV("span", {
          children: [
            columns.join(" | "),
            " "
          ]
        }, index, true, undefined, this))
      }, undefined, false, undefined, this)
    ]
  }, undefined, true, undefined, this);
}

More new stuff

Hashing functions powered by BoringSSL:

import {
  SHA1,
  MD5,
  MD4,
  SHA224,
  SHA512,
  SHA384,
  SHA256,
  SHA512_256,
} from "bun";

// hash the string and return as a Uint8Array
SHA1.hash("123456");
MD5.hash("123456");
MD4.hash("123456");
SHA224.hash("123456");
SHA512.hash("123456");
SHA384.hash("123456");
SHA256.hash("123456");
SHA512_256.hash("123456");

// output as a hex string
SHA1.hash(new Uint8Array(42), "hex");
MD5.hash(new Uint8Array(42), "hex");
MD4.hash(new Uint8Array(42), "hex");
SHA224.hash(new Uint8Array(42), "hex");
SHA512.hash(new Uint8Array(42), "hex");
SHA384.hash(new Uint8Array(42), "hex");
SHA256.hash(new Uint8Array(42), "hex");
SHA512_256.hash(new Uint8Array(42), "hex");

// incrementally update the hashing function value and convert it at the end to a hex string
// similar to node's API in require('crypto')
// this is not wired up yet to bun's "crypto" polyfill, but it really should be
new SHA1().update(new Uint8Array(42)).digest("hex");
new MD5().update(new Uint8Array(42)).digest("hex");
new MD4().update(new Uint8Array(42)).digest("hex");
new SHA224().update(new Uint8Array(42)).digest("hex");
new SHA512().update(new Uint8Array(42)).digest("hex");
new SHA384().update(new Uint8Array(42)).digest("hex");
new SHA256().update(new Uint8Array(42)).digest("hex");
new SHA512_256().update(new Uint8Array(42)).digest("hex");

Reliability improvements

bun - bun v0.0.78

Published by Jarred-Sumner over 2 years ago

To upgrade:

bun upgrade

What's new:

You can now import from "bun" in bun.js. You can still use the global Bun.

Before:

await Bun.write("output.txt", Bun.file("input.txt"))

After:

import {write, file} from 'bun';

await write("output.txt", file("input.txt"))

This isn't a breaking change – you can still use Bun as a global same as before.

Bun's JavaScript printer replaces the "bun" import specifier with globalThis.Bun.

var {write, file} = globalThis.Bun;

await write("output.txt", file("input.txt"))

You'll probably want to update types too:

bun add bun-types

Bug fixes

  • [fs] Add missing isFile and isDirectory functions to fs.stat()
    7cd3d1301128c1f9cdcfe5825b1fd34319ff0bdc
  • [js parser] Fix a code simplification bug that could happen when using ! and comma operator - 4de7978b2763d95ddcded6ccda9b7b80cca7e8f1
  • [bun bun] Fix using bun bun with --platform=bun set - 43b18663fdc763c24ae8fa0940019f2aee37c6aa

Bug fixes from v0.0.77

  • [bun dev] Fix race condition in file watcher
  • [bun install] Fix falling back to copyfile when hardlink fails on linux due to differing filesystems - 74309a1f9e52a11f2809485f4fc58ef317a7203b
  • [Bun.serve] When a Bun.file is not found and the error handler is not run, the default status code is now 404
  • [Bun.serve] Fix hanging when Bun.file sent with sendfile and the request aborts or errors
  • [Bun.serve] Decrement the reference count for FileBlob sent via sendfile after the callback completes instead of before
  • [Bun.serve] Serve .ts and .tsx files with text/javascript mime type by default instead of MPEG2 Video
bun - bun v0.0.76

Published by Jarred-Sumner over 2 years ago

To upgrade:

bun upgrade

What's new

  • Type definitions for better editor integration and TypeScript LSP! There are types for the runtime APIs now in the bun-types npm package. A PR for @types/bun is waiting review (feel free to nudge them).
  • Bun.serve() is a fast HTTP & HTTPS server that supports the Request and Response Web APIs. The server is a fork of uWebSockets
  • Bun.write() – one API for writing files, pipes, and copying files leveraging the fastest system calls available for the input & platform. It uses the Blob Web API.
  • Bun.mmap(path) lets you read files as a live-updating Uint8Array via the mmap(2) syscall. Thank you @evanwashere!!
  • Bun.hash(bufferOrString) exposes fast non-cryptographic hashing functions. Useful for things like ETag, not for passwords.
  • Bun.allocUnsafe(length) creates a new Uint8Array ~3.5x faster than new Uint8Array, but it is not zero-initialized. This is similar to Node's Buffer.allocUnsafe, though without the memory pool currently
  • Several web APIs have been added, including URL
  • A few more examples have been added to the examples folder
  • Bug fixes to fs.read() and fs.write() and some more tests for fs
  • Response.redirect(), Response.json(), and Response.error() have been added
  • import.meta.url is now a file:// url string
  • Bun.resolve and Bun.resolveSync let you resolve the same as import does. It throws a ResolveError on failure (same as import)
  • Bun.stderr and Bun.stdout now return a Blob
  • SharedArrayBuffer is now enabled (thanks @evanwashere!)
  • Updated Next.js version

New Web APIs in bun.js

Going forward, Bun will first try to rely on WebKit/Safari's implementations of Web APIs rather than writing new ones. This will improve Web API compatibility while reducing bun's scope, without compromising performance

These Web APIs are now available in bun.js and powered by Safari's implementation:

Also added:

Additionally, all the builtin constructors in bun now have a .prototype property (this was missing before)

Bun.serve - fast HTTP server

For a hello world HTTP server that writes "bun!", Bun.serve serves about 2.5x more requests per second than node.js on Linux:

Requests per second Runtime
~64,000 Node 16
~160,000 Bun

Bigger is better

Bun:

Bun.serve({
  fetch(req: Request) {
    return new Response(`bun!`);
  },
  port: 3000,
});

Node:

require("http")
  .createServer((req, res) => res.end("bun!"))
  .listen(8080);

Usage

Two ways to start an HTTP server with bun.js:

  1. export default an object with a fetch function

If the file used to start bun has a default export with a fetch function, it will start the http server.

// hi.js
export default {
  fetch(req) {
    return new Response("HI!");
  },
};

// bun ./hi.js

fetch receives a Request object and must return either a Response or a Promise<Response>. In a future version, it might have an additional arguments for things like cookies.

  1. Bun.serve starts the http server explicitly
Bun.serve({
  fetch(req) {
    return new Response("HI!");
  },
});

Error handling

For error handling, you get an error function.

If development: true and error is not defined or doesn't return a Response, you will get an exception page with a stack trace:

It will hopefully make it easier to debug issues with bun until bun gets debugger support. This error page is based on what bun dev does.

If the error function returns a Response, it will be served instead

Bun.serve({
  fetch(req) {
    throw new Error("woops!");
  },
  error(error: Error) {
    return new Response("Uh oh!!\n" + error.toString(), { status: 500 });
  },
});

If the error function itself throws and development is false, a generic 500 page will be shown

Currently, there is no way to stop the HTTP server once started πŸ˜…, but that will be added in a future version.

The interface for Bun.serve is based on what Cloudflare Workers does.

Bun.write() – optimizing I/O

Bun.write lets you write, copy or pipe files automatically using the fastest system calls compatible with the input and platform.

interface Bun {
  write(
    destination: string | number | FileBlob,
    input: string | FileBlob | Blob | ArrayBufferView
  ): Promise<number>;
}
Output Input System Call Platform
file file copy_file_range Linux
file pipe sendfile Linux
pipe pipe splice Linux
terminal file sendfile Linux
terminal terminal sendfile Linux
socket file or pipe sendfile (if http, not https) Linux
file (path, doesn't exist) file (path) clonefile macOS
file file fcopyfile macOS
file Blob or string write macOS
file Blob or string write Linux

All this complexity is handled by a single function.

// Write "Hello World" to output.txt
await Bun.write("output.txt", "Hello World");
// log a file to stdout
await Bun.write(Bun.stdout, Bun.file("input.txt"));
// write the HTTP response body to disk
await Bun.write("index.html", await fetch("http://example.com"));
// this does the same thing
await Bun.write(Bun.file("index.html"), await fetch("http://example.com"));
// copy input.txt to output.txt
await Bun.write("output.txt", Bun.file("input.txt"));

Bug fixes

  • Fixed a bug on Linux where sometimes the HTTP thread would incorrectly go to sleep, causing requests to hang forever 😒 . Previously, it relied on data in the queues to determine whether it should idle and now it increments a counter.
  • Fixed a bug that sometimes caused require to produce incorrect output depending on how the module was used
  • Fixed a number of crashes in bun dev related to HMR & websockets - https://github.com/Jarred-Sumner/bun/commit/daeede28dbb3c6b5bb43250ca4f3524cd728ca4c
  • fs.openSync now supports mode and flags - https://github.com/Jarred-Sumner/bun/commit/c73fcb073109405e1ccc30299bd9f8bef2791435
  • fs.read and fs.write were incorrectly returning the output of fs/promises versions, this is fixed
  • Fixed a crash that could occur during garbage collection when an fs function received a TypedArray as input. - https://github.com/Jarred-Sumner/bun/commit/614f64ba82947f7c2d3bf2dcc4e4993f6446e9b9. This also improves performance of sending array buffers to native a little
  • Response's constructor previously read statusCode instead of status. This was incorrect and has been fixed.
  • Fixed a bug where fs.stat reported incorrect information on macOS x64
  • Slight improvement to HMR reliability if reading large files or slow filesystems over websockets - 89cd35f07fdb2e965e3518f3417e4c39f0163522
  • Fixed a potential infinite loop when generating a sourcemap - fe973a5ab0d112ea6d80c8bcf7f693da879bb90d
    improve performance of accessing Bun.Transpiler and Bun.unsafe 29a759a65512278f1c20d1089ba05dbae268ef24
bun - bun v0.0.72

Published by Jarred-Sumner over 2 years ago

To upgrade:

bun upgrade

What's new

This release adds source maps for JS output, several web APIs to bun.js (and HTMLRewriter), parser support for ES2022 syntax, improves parsing speed for long strings, makes bun dev 10% faster on macOS and fixes a lot of crashes.

Note: the WASM build is not quite ready yet, but I'm working on it! The main thing I'm uncertain of is whether to release it with Bun.Transpiler as the API, or with a partially-implemented esbuild-compatible API. So if you would like to use it and have opinions there, do say

bun.js

Thanks to source maps, errors in bun.js show the source code instead of the transpiled code now:

Note the incorrect line numbers and the missing if (true) branch – bun's transpiler removed the dead code, but that can make it harder to read the code

New APIs:

Misc:

  • console.log support for JSX!
  • ResolveError.prototype.toString() and BuildError.prototype.toString() work as expected
  • console.log support for Request, Response, Headers
  • async node fs functions now have a compatibility wrapper that runs the sync version in the next tick. non-blocking support will come eventually
  • Unlike the Web API, FetchEvent.respondWith will automatically await any promises now
  • Fixed UTF-8 bug with console.{time, count, timeEnd, profile, profileEnd, count, countReset}
  • Fix Program terminates with pending promises #122
  • Fix a memory leak with microtasks - d0a9b404c39842755b452b93547ee85fe2b368ad
  • Fix bug causing async wasm compilation to never run - 7b8113c7c70bb3bf6916404123e1c30c233a2d6c

bun dev

  • Source Maps for non-bundled code
  • 10% faster http requests on macOS
  • Reduce HMR latency by using better syscalls (send multiple messages in one syscall with sendmsg)
  • Improve HMR performance when multiple tabs are open (pooling websocket threads)
  • Send β€œDate” header
  • Fix crash on macOS that sometimes occurred when closing the tab
  • Fixed several memory leaks and cases where bun would sometimes segfault

bun install

  • bun pm cache prints the cache dir path
  • Fix bug when parsing package.json that has non-ascii characters

bun run

  • Set $npm_config_user_agent
  • Set $npm_package_json
  • Set $NODE and $npm_node_exec path based on what’s available in $PATH

bun create

  • Fix a crash due to allocation failure - 2d1538e698654150a00a1b2187dfc811a0d15395

JavaScript Transpiler

  • Source Maps support for unbundled code (app code)
  • 69% faster at parsing long strings
  • [microbenchmark] 10% faster at parsing HTML entity codes in JSX
  • Support for static initialization blocks (ES2022)
  • Support for private static class fields and methods (ES2022)
  • Support for private static class brand checks (ES2022)
  • Support for private identifiers (ES2019)
  • Fix printing bug in strings that have certain escape sequences and non-ascii characters
  • Always print escaped Unicode codepoints instead of the original. This more closely matches what Babel & esbuild do
  • For TypeScript classes, match the semantics of useDefineForClassFields for fields and constructor arguments

Bun.Transpiler:

  • autoImportJSX flag lets you enable or disable importing auto-importing the jsxImportSource.
  • allowBunRuntime flag lets you disable importing bun's runtime code. This is useful if you want to use bun as just a transpiler. It is disabled by default, as I would expect people to use Bun.Transpiler mostly for other environments than bun itself

Infrastructural changes

I started using the debug build of mimalloc, bun's memory allocator for the debug builds of bun and that uncovered a few otherwise difficult-to-reproduce crashes

  • fix crash when upgrade checker ends – 00977c75ba090587140f3e110c51e30016c8efb3
  • fix crash when http thread goes to sleep - 1316dd1a4fcd25b0a81621b57c5f25c8674cd2db