Bun
    Bun

Bun v1.1.25


Ashcon Partovi · 2024 年 8 月 21 日

Bun v1.1.25 版本在此!此版本修正了 42 個錯誤(解決了 470 個 👍)。支援 node:cluster 和 V8 的 C++ API。Windows 上使用 OMGJIT 加快 WebAssembly 速度。使用 @aws-sdk/client-s3 上傳 S3 速度提升 5 倍、獨立可執行檔中的 Worker,以及更多 Node.js 相容性改進和錯誤修正。

我們正在舊金山招聘系統工程師,共同打造 JavaScript 的未來!

安裝 Bun

curl
npm
powershell
scoop
brew
docker
curl
curl -fsSL https://bun.dev.org.tw/install | bash
npm
npm install -g bun
powershell
powershell -c "irm bun.sh/install.ps1|iex"
scoop
scoop install 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

升級 Bun

bun upgrade

node:cluster 支援

Bun 現在支援 node:cluster API。

這讓您可以執行 Bun worker 池,這些 worker 共用同一個埠,從而在具有多核心 CPU 的機器上實現更高的輸送量和利用率。這非常適合生產環境中的負載平衡。

以下是如何運作的範例

  • 主要 worker 產生 n 個子 worker(通常等於 CPU 核心數)
  • 每個子 worker 監聽同一個埠(使用 reusePort
  • 傳入的 HTTP 請求會在子 worker 之間進行負載平衡
import cluster from "node:cluster";
import http from "node:http";
import { cpus } from "node:os";
import process from "node:process";

if (cluster.isPrimary) {
  console.log(`Primary ${process.pid} is running`);

  // Start N workers for the number of CPUs
  for (let i = 0; i < cpus().length; i++) {
    cluster.fork();
  }

  cluster.on("exit", (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} exited`);
  });
} else {
  // Incoming requests are handled by the pool of workers
  // instead of the primary worker.
  http
    .createServer((req, res) => {
      res.writeHead(200);
      res.end("hello world\n");
    })
    .listen(3000);

  console.log(`Worker ${process.pid} started`);
}

這適用於 node:http API,以及 Bun.serve() API。

import cluster from "node:cluster";
import { cpus } from "node:os";

if (cluster.isPrimary) {
  for (let i = 0; i < cpus().length; i++) {
    cluster.fork();
  }
} else {
  Bun.serve({
    port: 3000,
    fetch(request) {
      return new Response(`Hello from Worker ${process.pid}`);
    },
  });
}

請注意,目前 reusePort 僅在 Linux 上有效。在 Windows 和 macOS 上,作業系統不會像預期的那樣對 HTTP 連線進行負載平衡。

感謝 @nektro 實作此功能!

初步的 V8 C++ API 支援

Bun 現在支援 V8 的公開 C++ API,這解鎖了 cpu-features 等套件在 Bun 中運作。

這是一個值得注意的變更,因為 Bun 不是像 Node.js 那樣基於 V8 建構的。相反,Bun 是基於 JavaScriptCore 建構的,JavaScriptCore 是 Safari 瀏覽器使用的 JavaScript 引擎。

這表示我們必須在 V8 的 API 和 JavaScriptCore 之間實作我們自己的 C++ 翻譯層。

src/bun.js/bindings/v8/V8String.cpp
#include "v8.h"
#include "V8Primitive.h"
#include "V8MaybeLocal.h"
#include "V8Isolate.h"

namespace v8 {

enum class NewStringType { /* ... */ };

class String : Primitive {
public:
    enum WriteOptions { /* ... */ };

    BUN_EXPORT static MaybeLocal<String> NewFromUtf8(Isolate* isolate, char const* data, NewStringType type, int length = -1);
    BUN_EXPORT int WriteUtf8(Isolate* isolate, char* buffer, int length = -1, int* nchars_ref = nullptr, int options = NO_OPTIONS) const;
    BUN_EXPORT int Length() const;

    JSC::JSString* localToJSString()
    {
        return localToObjectPointer<JSC::JSString>();
    }
};
}

這是困難的工程工作。JavaScriptCore 和 V8 以不同的方式表示 JavaScript 值。V8 使用移動式並行垃圾收集器和顯式 handle scopes,而 JavaScriptCore 使用非移動式並行垃圾收集器,該收集器掃描堆疊記憶體(有點像隱式 handle scope)。

以前,如果您嘗試匯入使用這些 API 的套件,您會收到類似這樣的錯誤

console.log(require("cpu-features")());
dyld[94465]: missing symbol called
fish: Job 1, 'bun index.ts' terminated by signal SIGABRT (Abort)

現在,cpu-features 等套件可以匯入並在 Bun 中正常運作。

$ bun index.ts
{
  arch: "aarch64",
  flags: {
    fp: true,
    asimd: true,
    // ...
  },
}

這只是支援 V8 內部 API 的開始。如果您的套件遇到我們尚未實作的 V8 API 區域,您會收到一個錯誤,引導您在 GitHub 上為問題投票。

為什麼要支援 V8 的內部 API?

我們最初不打算支援這些 API,但在發現許多熱門套件依賴這些 API 後,我們決定支援,例如

在此版本中,上述列表中僅支援 cpu-features,但我們正在努力改進支援,以便所有這些套件以及更多套件都能在 Bun 中正常運作。

感謝 @190n 實作此功能!

使用 @aws-sdk/client-s3 將 S3 上傳速度提升 5 倍

我們修正了 node:http client 實作中的一個錯誤,該錯誤使 S3 上傳速度提高了 5 倍。

獨立可執行檔中的 Worker

Bun 的單檔案獨立可執行檔現在支援捆綁 Workernode:worker_threads

main.ts
my-worker.ts
main.ts
console.log("Hello from main thread!");

new Worker("./my-worker.ts");
my-worker.ts
console.log("Hello from another thread!");

若要編譯具有 worker 的獨立可執行檔,請將進入點傳遞給 bun build --compile

bun build --compile ./main.ts ./my-worker.ts

這會將 my-worker.tsmain.ts 作為單獨的進入點捆綁到產生的可執行檔中。

在獨立可執行檔中嵌入檔案,無需匯入

現在,如果您願意,可以在獨立可執行檔中嵌入檔案,而無需顯式的 import 語句。

bun build --compile ./main.ts ./public/**/*.png

通常,您必須匯入檔案才能使用它

import logo from "./public/icons/logo.png" with {type: "file"};

但是,有時您想要匯入大量可能需要以程式方式引用的檔案。

Bun.embeddedFiles

新的 Bun.embeddedFiles API 可讓您查看獨立可執行檔中所有嵌入檔案的清單。

import { embeddedFiles } from "bun";

for (const file of embeddedFiles) {
  console.log(file.name); // "logo.png"
  console.log(file.size); // 1234
  console.log(await file.bytes()); // Uint8Array(1234) [...]
}

這會將所有非 JavaScript 進入點和資源合併到按名稱排序的單個 Blob 物件陣列中。

獨立可執行檔中嵌入檔案的相對匯入

您現在可以使用獨立可執行檔中的相對路徑,參考嵌入檔案的執行階段已知匯入。

function getWasmFilePath(file: string) {
  return require.resolve(`./${file}.wasm`);
}

const wasmFile = getWasmFilePath("my-wasm-file");
console.log(wasmFile); // "./my-wasm-file.wasm"

以前,這僅支援在建置時已知的靜態可分析匯入。現在,您可以對任何嵌入檔案執行此操作。

Windows 上使用 OMGJIT 加快 WebAssembly 速度

Windows 上的 WebAssembly 現在支援 JavaScriptCore 的最佳化即時編譯器 (JIT),稱為 OMGJIT。

感謝 Ian Grunert 在 WebKit 中發布此功能!

Node.js 相容性改進

execa 現在可以運作了

我們修正了一個錯誤,Bun 未正確支援 EventTargetsetMaxListeners()。這影響了 execa 等套件,這些套件會因 undefined is not a function 而出錯。

import { execa } from "execa";

const { stdout } = await execa`echo "test"`;

如果您遇到看起來像這樣的錯誤,現在已修正

91 |    const controller = new AbortController();
92 |    setMaxListeners(Number.POSITIVE_INFINITY, controller.signal);
      ^
TypeError: undefined is not a function
      at node:events:101:30
      at spawnSubprocessAsync (/node_modules/execa/lib/methods/main-async.js:92:2)
      at execaCoreAsync (/node_modules/execa/lib/methods/main-async.js:26:32)
      at index.mjs:3:26

已修正:在 destroy() 之後,node:net 的連線掛起

我們修正了一個錯誤,在 TCP 連線上呼叫 destroy() 並非總是能正確退出程序,因為事件迴圈仍然處於活動狀態。這有時會導致 postgres 等套件無限期掛起。

import net from "node:net";

const server = net.createServer((socket) => {
  socket.on("connect", (data) => {
    socket.destroy();
    // This would destroy the connection,
    // but the event loop would still be active
  });
});

server.listen(3000);

已修正:node:net.Socket 的 ref 和 unref this

node:net.Socket 上的 refunref 方法未傳回 this 值。

現在,它們會傳回 this

感謝 @nektro 修正此錯誤!

錯誤修正

已修正:TextEncoderStream 產生了錯誤的類型

在 Bun v1.1.23 中,我們引入了對 TextEncoderStream API 的支援。我們修正了一個錯誤,其中來自串流的區塊是 Buffer 物件而不是 Uint8Array 物件。

const response = await fetch("https://example.com");
const body = response.body.pipeThrough(new TextEncoderStream());

for await (const chunk of body) {
  console.log(chunk);
  // Before: Buffer
  // Now: Uint8Array
}

已修正:Bun shell 中使用跳脫字元反引號時崩潰

我們修正了 Bun shell 中的一個崩潰問題,該問題無法正確處理跳脫字元反引號。

import { $ } from "bun";

console.log(await $`cd \`find -name dir\``.text());

感謝 @zackradisic 修正此錯誤!

已修正:TextEncoder 的 JIT 崩潰

我們修正了一個錯誤,如果 TextEncoder 在執行數百萬次後進行 JIT 編譯,則會崩潰。這是由於 Bun 為在函數上啟用 JIT 而產生的程式碼中的錯誤所致。

已修正:jest.fn 和 mock() 中的崩潰

已修正當使用意外的 this 值在 jest.fnmock() 物件上呼叫函數時可能發生的崩潰。

已修正:IPC 中的崩潰

當子程序在父程序收到訊息之前退出時,在某些情況下可能會發生崩潰。

這已修正,感謝 @cirospaciari

已修正:console.log 檔案的名稱

當您使用 web File 建構函式建立 Blob 時,檔案名稱未包含在 console.log 的輸出中。

輸入

console.log(new File(["hello world"], "hello.txt"));

新的

File (11 bytes) {
  name: "hello.txt",
  lastModified: 1724218333315
}

先前

Blob (11 bytes)

已修正:websocket 伺服器中罕見的崩潰

已修正一個罕見的崩潰問題,該問題可能在伺服器呼叫回呼後經過很長時間,並且 ServerWebSocket 物件已被垃圾回收時發生。

感謝 11 位貢獻者!