Bun

Bun v1.1.14


Chloe Caruso · 2024年6月19日

Bun v1.1.14 隆重登場!此版本修正了 63 個錯誤(解決了 519 個 👍 問題)。使用 bun patch 修補 node_modules。bun:sqlite 中無 ORM 的物件映射。將所有 fetch() 呼叫記錄為 curl 命令。Node.js 相容性改進。bun install 錯誤修正、bun:sqlite 錯誤修正、Windows 錯誤修正。以及更多內容。

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

先前版本

  • v1.1.13 修正了 97 個錯誤(解決了 211 個 👍 問題)。bun install、WebSocket 伺服器、fetch、worker_threads 的可靠性改進。worker_threads 現在支援 eval。URL.createObjectURL、堆疊追蹤錯誤位置改進、多個 bun install 修正
  • v1.1.10 修正了 20 個錯誤。Windows 上未快取的 bun install 速度提升 2 倍。fetch() 使用的記憶體最多減少 2.8 倍。bun install、sourcemaps、Windows 可靠性改進和 Node.js 相容性改進的多項錯誤修正
  • v1.1.0 Bundows。Windows 支援已推出!

安裝 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

bun patch

Bun v1.1.14 為 Bun 的套件管理器引入了一個新的子命令:bun patch

bun patch <pkg>

有時,相依性套件存在錯誤或缺少功能。您可以 fork 該套件、進行修改並發布。但對於一個小的變更來說,這需要大量工作。如果您不想維護 fork 版本怎麼辦?如果您想繼續接收來自原始套件的更新,但同時包含您的變更,又該怎麼辦?

bun patch 讓修改相依性套件變得容易,無需 fork 或 vendoring。變更會保存在 .patch 檔案中,這些檔案可以被審查、共享、版本控制,並在其他專案中重複使用。這些 .patch 檔案會在 bun install 時自動套用,結果會快取在 Bun 的 Global Cache 中。

若要開始使用,請執行 bun patch <pkg> 以修補套件。

bun patch is-even

bun patch v1.1.14

+ is-even@1.0.0

5 packages installed [3.00ms]

To patch is-even, edit the following folder:

  node_modules/is-even

Once you're done with your changes, run:

  bun patch --commit 'node_modules/is-even'

在內部,這會在 node_modules 中複製套件,並使用其自身的全新副本,與共享快取隔離。這讓您可以安全地編輯套件目錄中的檔案,而不會影響全域快取。

由於我們複製了套件,您可以在專案的 node_modules 資料夾中本機測試您的修補程式。這讓您可以輕鬆地迭代變更並驗證它們是否如預期般運作。

一旦您對變更感到滿意,請執行 bun patch --commit <pkg> 以儲存您的變更。

bun patch --commit is-even

當您執行 bun patch --commit is-even 時,Bun 會

  1. 產生包含您變更的修補程式檔案
  2. 將相依性新增至 package.json 中的 "patchedDependencies"
  3. 將修補程式檔案儲存在專案根目錄下的 patches/

修補程式檔案使用標準 diff 格式,因此您可以像其他任何程式碼一樣審查、共享和進行版本控制。

./patches/is-even@1.0.0.patch
diff --git a/index.js b/index.js
index 832d92223a9ec491364ee10dcbe3ad495446ab80..2a61f0dd2f476a4a30631c570e6c8d2d148d419a 100644
--- a/index.js
+++ b/index.js
@@ -1,14 +1 @@
-/*!
- * is-even <https://github.com/jonschlinkert/is-even>
- *
- * Copyright (c) 2015, 2017, Jon Schlinkert.
- * Released under the MIT License.
- */
-
-'use strict';
-
-var isOdd = require('is-odd');
-
-module.exports = function isEven(i) {
-  return !isOdd(i);
-};
+module.exports = (i) => (i % 2 === 0)

感謝 @zackradisic 實作 bun patch

更佳的 bun:sqlite

此版本為 Bun 的內建 sqlite 模組 bun:sqlite 新增了多項改進。

使用 query.as(Class) 進行無 ORM 的物件映射

bun:sqlite 現在支援將查詢結果映射到類別的實例。這讓您可以將方法、getter 和 setter 附加到查詢結果,而無需使用 ORM。

import { Database } from "bun:sqlite";

const db = new Database("my.db");

class Tweet {
  id: number;
  text: string;
  username: string;

  get isMe() {
    return this.username === "jarredsumner";
  }
}

const tweets = db
  .query("SELECT * FROM tweets")
  .as(Tweet);

for (const tweet of tweets.all()) {
  if (!tweet.isMe) {
    console.log(`${tweet.username}: ${tweet.text}`);
  }
}

屬性不需要預先在類別上定義(這裡只是為了讓範例更容易閱讀),而且查詢中欄位的順序並不重要。

這是一種將查詢結果映射到物件的輕量方式。在底層,它的運作方式類似於 Object.create,這表示它不會呼叫建構子、執行預設初始化器,並且無法存取私有欄位。

as 方法在 Statement 類別上定義,因此您可以將其與 getall 方法一起使用。重要的是要注意,這不是 ORM。它不管理關聯性、產生 SQL 或任何類似的東西。它只是讓查詢傳回類別實例而不是純物件。

運作方式

當您呼叫 query.as(Class) 時,Bun 會建立一個以類別原型為基礎的新物件,並將查詢結果指派給它。

const query = db.query("SELECT * FROM tweets").as(Tweet);

// Get the first row as a Tweet instance
const tweet: Tweet = query.get();

// Each row is a Tweet instance
const allTweets: Tweet[] = query.all();

使用 strict: true 進行更簡化的查詢綁定

此版本為 Database 建構子新增了一個新的 strict?: boolean 選項,讓您在將值綁定到預先處理的語句時,可以省略 $@: 前綴。

import { Database } from "bun:sqlite";

const db = new Database(":memory:", {
  strict: false,
  strict: true,
});

const query = db.query(`select $message;`);

query.all({
  $message: "Hello world"
  message: "Hello world"
});

strict: true 時,如果查詢缺少任何綁定,將會拋出錯誤。

為了保持向後相容性,strict 預設為 false。建議在新專案中啟用此選項。

使用 query.run(sql) 追蹤變更

DatabaseStatement 上的 run 方法現在會傳回一個具有 changeslastInsertRowid 屬性的物件。這讓追蹤變更和取得最後插入的列 ID 變得更容易。

import { Database } from "bun:sqlite";

const db = new Database(":memory:");

db.run(`CREATE TABLE hey ( id INTEGER, data TEXT)`);
const { changes, lastInsertRowid } = db.run(
  `insert into hey (id, data) values (1, 'foo')`,
);
console.log({
  changes, // => 1
  lastInsertRowid, // => 1
});

這在驗證查詢是否已對資料庫進行變更時非常重要。先前,run 會傳回 undefined

使用 safeIntegers: true 的 BigInt

safeIntegerstrue 時,bun:sqlite 將會以 bigint 類型傳回整數。

import { Database } from "bun:sqlite";

const db = new Database(":memory:", { safeIntegers: true });
const query = db.query(
  `SELECT ${BigInt(Number.MAX_SAFE_INTEGER) + 102n} as max_int`,
);
const result = query.get();
console.log(result.max_int); // => 9007199254741093n

這些整數必須在 64 位元範圍內。如果輸入超出此範圍,bun:sqlite 將會拋出錯誤。

import { Database } from "bun:sqlite";

const db = new Database(":memory:", { safeIntegers: true, strict: true });
db.run("CREATE TABLE test (id INTEGER PRIMARY KEY, value INTEGER)");

const query = db.query("INSERT INTO test (value) VALUES ($value)");

try {
  query.run({ value: BigInt(Number.MAX_SAFE_INTEGER) ** 2n });
} catch (e) {
  console.log(e.message); // => BigInt value '81129638414606663681390495662081' is out of range
}

有時,您只希望特定查詢具有此行為。safeIntegers 方法在 Statement 上可用。

import { Database } from "bun:sqlite";

const db = new Database(":memory:", { strict: true });

const query = db.query("SELECT $value as value").safeIntegers(true);

const result = query.get({ value: BigInt(Number.MAX_SAFE_INTEGER) + 102n });
console.log(result.value); // => 9007199254741093n

已修正:query.values() 在處理重複欄位名稱時的問題

一個錯誤導致在呼叫 query.values() 時,重複的欄位名稱被忽略。此問題已修正。

import { Database } from "bun:sqlite";
const db = new Database();
const result = db.prepare("select 1 as id, 2 as id").values();
console.log(result);

已修正

[
  [ 1, 2 ]
]

先前

[
  [ 2 ]
]

此錯誤最常在使用 JOIN 時發生。

已修正:bun:sqlite 在使用表格 JOIN 時崩潰的問題。

在聯結具有重複欄位的表格時,有時會發生的崩潰問題已修正。這會影響使用 Drizzle 的 bun:sqlite 驅動程式和 .leftJoin

bun install 可靠性

已修正:Tarball 下載失敗時以非零程式碼結束的問題

當 tarball 下載失敗時,bun install 會以零結束代碼退出。此問題已修正。當任何 HTTP 請求失敗時,bun install 現在將以非零結束代碼退出,以便 CI 系統停止建置。

已修正:bun install 處理過渡性資料夾相依性的問題

有時,套件會發布到 npm 註冊表,並帶有 file: 相依性。有時

感謝 @dylan-conway 修正此問題。

已修正:bun install 解壓縮錯誤修正

.tar.gz 封存檔有可能包含同一目錄的多個條目(一個帶有 ./,一個沒有)。Bun 現在在解壓縮時會處理此情況,而不是報告它要解壓縮到的目錄已存在的錯誤。

感謝 @dylan-conway 修正此問題。

已修正:在 workspace 中使用 bun install --production 時,會包含 devDependencies 的問題

--production 傳遞給 workspace 中的 bun install 時,workspace 套件的開發相依性會包含在安裝中。現在已不再如此。

同時也修正了在這種情況下的斷言失敗。

已修正:在 Windows 上未傳遞 npm_config_user_agent 的問題

為了讓專案初始化腳本(如 create-next-app)知道要使用哪個套件管理器,套件管理器應設定 npm_config_user_agent 環境變數。Windows 上的 Bun 並非總是將修改後的環境變數傳遞給子進程,因此此問題已修正。

例如,在 Windows 上建立新的 Next.js 專案將正確地告知 Next.js 使用 bun install

 PS> bun create next-app
 ✔ What is your project named? … my-app
 ✔ Would you like to use TypeScript? … No / Yes
 ✔ Would you like to use ESLint? … No / Yes
 ✔ Would you like to use Tailwind CSS? … No / Yes
 ✔ Would you like to use `src/` directory? … No / Yes
 ✔ Would you like to use App Router? (recommended) … No / Yes
 ✔ Would you like to customize the default import alias (@/*)? … No / Yes
 Creating a new Next.js app in D:\Projects\my-app.

-Using npm.
+Using bun.

 Initializing project with template: app-tw

感謝 @SunsetTechuila 修正此問題。

偵錯 fetch()

此版本新增了一個新的環境變數,以協助偵錯使用 fetch()node:http 時的網路流量。

BUN_CONFIG_VERBOSE_FETCH=1 作為環境變數傳遞時,Bun 將記錄所有通過 fetchnode:http 用戶端的網路流量。

BUN_CONFIG_VERBOSE_FETCH=curl 設定為環境變數時,Bun 將把所有通過 fetchnode:http 用戶端的網路流量印成 curl 命令。

process.env.BUN_CONFIG_VERBOSE_FETCH = "curl";

await fetch("https://example.com", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ foo: "bar" }),
});

這會將 fetch 請求印成 curl 命令,讓您可以複製貼上到終端機中以重現請求。

[fetch] $ curl --http1.1 "https://example.com/" -X POST -H "content-type: application/json" -H "Connection: keep-alive" -H "User-Agent: Bun/1.1.14" -H "Accept: */*" -H "Host: example.com" -H "Accept-Encoding: gzip, deflate, br" --compressed -H "Content-Length: 13" --data-raw "{\"foo\":\"bar\"}"
[fetch] > HTTP/1.1 POST https://example.com/
[fetch] > content-type: application/json
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.1.14
[fetch] > Accept: */*
[fetch] > Host: example.com
[fetch] > Accept-Encoding: gzip, deflate, br
[fetch] > Content-Length: 13

[fetch] < 200 OK
[fetch] < Accept-Ranges: bytes
[fetch] < Cache-Control: max-age=604800
[fetch] < Content-Type: text/html; charset=UTF-8
[fetch] < Date: Tue, 18 Jun 2024 05:12:07 GMT
[fetch] < Etag: "3147526947"
[fetch] < Expires: Tue, 25 Jun 2024 05:12:07 GMT
[fetch] < Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
[fetch] < Server: EOS (vny/044F)
[fetch] < Content-Length: 1256

您可以使用此功能來偵錯網路請求、重現網路請求,或檢查不同的 JavaScript/TypeScript 工具在網路上傳送的內容,而無需設定代理。

Node.js 相容性改進

node:fs/promises 函式現在接受 FileHandle

使用 node:fs/promises 時,readFilewriteFileappendFile 函式接受 FileHandle 物件作為輸入。

import { open, readFile } from 'node:fs/promises';

using file = await open("example.txt", "rw");
const contents = await readFile(file);
console.log(contents);
await writeFile(file, `New data: ${Math.random()}\n`);

感謝 @nektro 實作此功能。

process.env.NODE_TLS_REJECT_UNAUTHORIZED

您現在可以在執行時設定 process.env.NODE_TLS_REJECT_UNAUTHORIZED,以停用或啟用 SSL 憑證驗證。這對於測試或偵錯很有用,但不應在生產環境中啟用。

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
await fetch("https://self-signed.badssl.com"); // no error
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "1";
await fetch("https://self-signed.badssl.com"); // throws error

先前,此環境變數只能在啟動時傳遞。現在,可以在執行時設定。

已修正:node:http / express 中的停滯問題

一個影響 node:http 伺服器的錯誤,其中 req.completed 從未設定為 true,會導致 Express 中的某些請求無限期停滯。感謝 @cirospaciari,此問題已修正

已修正:blob.type

競爭條件有時會導致來自 fetchblob.type 變成 undefined。感謝 @cirospaciari,此問題現已解決。

const blob = await fetch("https://bun.dev.org.tw/logo.svg").then((res) => res.blob());
console.log("Blob type is:", blob.type);

已修正:blob.name 可寫入

您現在可以設定 Blob 物件上的 name 屬性。

const blob = await fetch("https://bun.dev.org.tw/logo.svg").then((res) => res.blob());
blob.name = "bun!!!!!";
console.log(blob.name); // "bun!!!!!"

先前,設定 blob.name 會拋出 TypeError

1 | const blob = await fetch("https://bun.dev.org.tw/logo.svg").then((res) => res.blob());
2 | blob.name = "bun!!!!!";
    ^
TypeError: Attempted to assign to readonly property.

已修正:透過 SSL 的 WebSocket 處理大型二進位酬載的問題

先前,當透過 wss:// WebSocket 連線傳送大型二進位酬載時,訊息可能未被傳送。這是因為未從 BoringSSL 呼叫 set_retry_write。感謝 @cirospaciari 修正此問題。

已修正:Bun 不再使 Windows 終端機崩潰

在 Windows 上,ENABLE_VIRTUAL_TERMINAL_INPUT 旗標始終在主控台輸入上啟用。如果在退出前不移除此旗標,大多數 shell 會崩潰(cmd.exe 方向鍵失效、Powershell 經常崩潰、nu 無法接受新行等)。此旗標已移除,因為 Bun 不再需要設定此旗標來處理其輸入(prompt()node:readlinebun init 等)。

感謝 @paperclover 修正此問題。

已修正:WebSocket 標頭大小寫與 node 不一致的問題

先前,使用 new WebSocket 會將所有標頭轉換為小寫文字。這對於某些期望標頭具有特定大小寫的伺服器造成了一些問題,即使標頭本應不區分大小寫。現在,知名的標頭將被標準化為其標準形式,而自訂標頭將保留其精確的大小寫。

例如

const ws = new WebSocket("ws://127.0.0.1:3000", {
  headers: {
    "Origin": "https://127.0.0.1:3000",
    "authorization": "password",
    "X-some-Other-header": "bun!",
  },
});

將傳送這些標頭

> Origin: https://127.0.0.1:3000
> Authorization: password
> X-some-Other-header: bun!

感謝 @cirospaciari 修正此問題。

已修正:Bun Shell 中的記憶體洩漏

已修正影響 Bun Shell 的記憶體洩漏。這會導致 shell 物件永遠不會被垃圾回收,從而阻止記憶體被回收。

已修正:error.line 的回歸問題

一個 error.lineerror.column 會傳回 0 的錯誤已修正。此回歸問題是在 v1.1.13 中引入的。

感謝 10 位貢獻者!