Bun v1.0.21 修復了 33 個錯誤(解決了 80 個 👍 表情符號的回應)。支援 console.table()
。Bun.write
、Bun.file 和 bun:sqlite 使用更少的記憶體。使用 FormData 上傳大型檔案時使用更少的記憶體。bun:sqlite 錯誤訊息更詳細。修復了來自 node:fs 的錯誤中的記憶體洩漏。改進了 Node.js 的相容性,並修復了許多崩潰問題
Bun 是一個速度驚人的 JavaScript 執行時環境、打包器、轉譯器和套件管理器 — 功能All-in-One。如果您錯過了,以下是 Bun 最近的一些變更
v1.0.18
- 修復了 27 個錯誤(解決了 28 個 👍 表情符號的回應)。已修復影響 create-vite 和 create-next 和 stdin 的卡頓問題。已修復生命週期腳本回報「node」或「node-gyp」找不到的問題。expect().rejects 現在像 Jest 一樣運作,以及更多錯誤修復v1.0.19
- 修復了 26 個錯誤(解決了 92 個 👍 表情符號的回應)。使用 @types/bun 而不是 bun-types。修復了 --frozen-lockfile 錯誤。bcrypt 和 argon2 套件現在可以運作了。setTimeout 和 setInterval 的吞吐量提高了 4 倍。bun:test 中的模組模擬解析規範。針對 Linux 上大型 stdio 優化了 spawnSync()。Bun.peek() 速度提高了 90 倍,expect(map1).toEqual(map2) 速度提高了 100 倍。修復了 NAPI、bun install 和 Node.js 相容性改進的錯誤v1.0.20
- 降低了fs.readlink
、fs.readFile
、fs.writeFile
、fs.stat
和HTMLRewriter
中的記憶體使用量。修復了 setTimeout 在 Linux 上造成 CPU 使用率偏高的回歸問題。HTMLRewriter.transform
現在支援字串和ArrayBuffer
。fs.writeFile()
和fs.readFile()
現在支援hex
和base64
編碼。Bun.spawn
顯示了程序使用的 CPU 和記憶體量。
要安裝 Bun
curl -fsSL https://bun.dev.org.tw/install | bash
npm install -g bun
brew tap oven-sh/bun
brew install bun
docker pull oven/bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun
要升級 Bun
bun upgrade
現在支援 console.table()
console.table()
是一個 Web API,可從物件或可迭代物件列印表格。它對於除錯很有用。
const { headers } = await fetch("https://example.com");
console.table(headers)
在此範例中,Bun 將列印回應標頭的 ASCII 表格。
┌────┬──────────────────┬───────────────────────────────┐
│ │ 0 │ 1 │
├────┼──────────────────┼───────────────────────────────┤
│ 0 │ age │ 519517 │
│ 1 │ cache-control │ max-age=604800 │
│ 2 │ content-encoding │ gzip │
│ 3 │ content-length │ 648 │
│ 4 │ content-type │ text/html; charset=UTF-8 │
│ 5 │ date │ Tue, 02 Jan 2024 02:37:00 GMT │
│ 6 │ etag │ "3147526947+gzip" │
│ 7 │ expires │ Tue, 09 Jan 2024 02:37:00 GMT │
│ 8 │ last-modified │ Thu, 17 Oct 2019 07:18:26 GMT │
│ 9 │ server │ ECS (sac/2508) │
│ 10 │ vary │ Accept-Encoding │
│ 11 │ x-cache │ HIT │
└────┴──────────────────┴───────────────────────────────┘
感謝 @otgerrogla 實作此功能!
您也可以將 console.table
與各種不同的物件一起使用,包括
使用 console.table() 的陣列
const foo = await fetch("https://api.github.com/repos/elyisajs/elysia/issues");
console.table(await issues.json(), ["title", "status"]);
使用 console.table() 的物件
const user = await fetch("https://api.github.com/users/ThePrimeagen");
console.table(await user.json());
這會印出類似以下的內容
┌─────────────────────┬──────────────────────────────────────────────────────────────────┐
│ │ Values │
├─────────────────────┼──────────────────────────────────────────────────────────────────┤
│ login │ ThePrimeagen │
│ id │ 4458174 │
│ node_id │ MDQ6VXNlcjQ0NTgxNzQ= │
│ avatar_url │ https://avatars.githubusercontent.com/u/4458174?v=4 │
│ gravatar_id │ │
│ url │ https://api.github.com/users/ThePrimeagen │
│ html_url │ https://github.com/ThePrimeagen │
│ followers_url │ https://api.github.com/users/ThePrimeagen/followers │
│ following_url │ https://api.github.com/users/ThePrimeagen/following{/other_user} │
│ gists_url │ https://api.github.com/users/ThePrimeagen/gists{/gist_id} │
│ starred_url │ https://api.github.com/users/ThePrimeagen/starred{/owner}{/repo} │
│ subscriptions_url │ https://api.github.com/users/ThePrimeagen/subscriptions │
│ organizations_url │ https://api.github.com/users/ThePrimeagen/orgs │
│ repos_url │ https://api.github.com/users/ThePrimeagen/repos │
│ events_url │ https://api.github.com/users/ThePrimeagen/events{/privacy} │
│ received_events_url │ https://api.github.com/users/ThePrimeagen/received_events │
│ type │ User │
│ site_admin │ false │
│ name │ ThePrimeagen │
│ company │ CEO Of TheStartup │
│ blog │ http://twitch.tv/ThePrimeagen │
│ location │ 9th Ring, Vim │
│ email │ null │
│ hireable │ null │
│ bio │ null │
│ twitter_username │ ThePrimeagen │
│ public_repos │ 199 │
│ public_gists │ 0 │
│ followers │ 23186 │
│ following │ 3 │
│ created_at │ 2013-05-17T15:05:59Z │
│ updated_at │ 2023-11-22T19:47:49Z │
└─────────────────────┴──────────────────────────────────────────────────────────────────┘
bun:sqlite 具有更詳細的錯誤訊息
先前,bun:sqlite
可能會拋出類似以下的錯誤
7 | db.exec('INSERT INTO foo VALUES ("hello")');
^
error: constraint failed
at run (bun:sqlite:185:11)
at /private/tmp/sqlite.js:7:1
但現在,您將獲得更詳細的錯誤訊息,例如
7 | db.exec('INSERT INTO foo VALUES ("hello")');
^
SQLiteError: UNIQUE constraint failed: foo.bar
errno: 2067
code: "SQLITE_CONSTRAINT_UNIQUE"
at run (bun:sqlite:185:11)
at /private/tmp/fetch.js:7:1
變更了什麼?
error.name
現在是SQLiteError
而不是Error
error.message
現在使用來自 SQLite 的擴充錯誤訊息,其中經常包含表格和欄位名稱error.errno
是來自 SQLite 的擴充錯誤代碼error.code
是來自 SQLite 的擴充錯誤代碼,以字串形式表示error.byteOffset
報告 SQL 陳述式中發生錯誤的位元組偏移量(如果可用)
+ SQLiteError: UNIQUE constraint failed: foo.bar
- error: constraint failed
+ errno: 2067
+ code: "SQLITE_CONSTRAINT_UNIQUE"
at run (bun:sqlite:185:11)
at /private/tmp/sqlite.js:7:1
bun:sqlite 使用更少的記憶體
bun:sqlite
現在會將 SQLite 的記憶體使用量報告給垃圾收集器,這會提示垃圾收集器在必要時更積極地釋放記憶體。
此範例建立 100,000 個預備陳述式,消耗的記憶體減少了 4 倍
記憶體使用量 | Bun 版本 |
---|---|
71 MB | Bun v1.0.21 |
287 MB | Bun v1.0.20 |
import { Database } from "bun:sqlite";
const db = new Database(":memory:");
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, content TEXT)");
db.exec('INSERT INTO test VALUES (NULL, "hello")');
for (let i = 0; i < 100_000; i++) {
db.prepare(`SELECT content as content${i} FROM test`);
}
console.log((process.memoryUsage.rss() / 1024 / 1024) | 0, "MB");
我們也修復了綁定實作中的一個錯誤,該錯誤導致內部 SQLStatement
物件在垃圾回收時未呼叫預備陳述式的解構函式。
已修復:bun:sqlite 在使用 latin1 字元時崩潰
當在查詢中使用 latin1 補充字元時,bun:sqlite
中存在崩潰問題,現已修復。
const db = new Database(":memory:");
db.run(
"CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, copyright© TEXT)",
);
©
符號是有效的 latin1 補充字元,先前將查詢傳送至 SQLite 的程式碼錯誤地假設 latin1 字串始終為 ASCII 字串。
Linux 上的寫入時複製檔案上傳
當您使用 FormData
上傳大型檔案時,每個檔案都會成為記憶體中的 Blob
物件(或 File
物件)。
如果您需要以 ArrayBuffer
格式取得資料,會發生什麼情況?
const blob = formData.get("image");
// This clones the data, doubling the memory usage!
const bytes = await blob.arrayBuffer();
它會複製。突然間,**如果您上傳了 128 MB 的檔案,您現在將使用 256 MB 的記憶體**,僅僅是為了以 ArrayBuffer
格式取得資料。
有人可能會認為這是 API 設計問題。.arrayBuffer()
真的 應該複製資料嗎?但那是標準 Web API,我們必須使用它。
**我們可以在不變更 API 的情況下做得更好嗎?** 可以!
這稱為寫入時複製記憶體。
透過一些虛擬記憶體技巧,我們可以讓您在呼叫 blob.arrayBuffer()
時,它實際上不會複製資料。相反地,它會建立一個指向相同資料的新虛擬記憶體對應,並且僅在您寫入時才複製資料(以 4 KB 的區塊為單位)。
此版本實作了寫入時複製 blob.arrayBuffer()
的支援,當 blob
至少為 8 MB 時(啟用 --smol
時為 1 MB)。
100 個副本後的記憶體使用量 | Bun 版本 |
---|---|
192 MB | Bun v1.0.21 |
12.9 GB | Bun v1.0.20 |
const blob = new Blob([new Uint8Array(1024 * 1024 * 128).fill(42)]);
const arrayBuffers = await Array.fromAsync({ length: 100 }, () =>
blob.arrayBuffer(),
);
console.log("RSS", (process.memoryUsage.rss() / 1024 / 1024) | 0, "MB");
如果您建立 128 MB Blob 的 100 個副本,它只會使用 192 MB 的記憶體。以前,它會使用 12,993 MB MB 的記憶體。
當然,這有額外負擔。為每個 Blob 建立唯一的記憶體對應很耗費資源。Bun 僅對大於 8 MB 的 Blob 執行此操作(或啟用 --smol
時為 1 MB,因為 --smol
優化了記憶體使用量而不是執行時間)。
使用 Bun.CryptoHasher
從 FormData
雜湊檔案上傳
Bun.CryptoHasher
現在支援 Blob
物件,這簡化了從 FormData
雜湊檔案上傳。
import { serve, CryptoHasher } from "bun";
serve({
async fetch(req) {
const formData = await req.formData();
const image = formData.get("file");
const sha1 = CryptoHasher.hash("sha1", image, "hex");
return new Response(`SHA-1: ${sha1}`);
},
});
先前,您必須先將 Blob
轉換為 ArrayBuffer
,然後才能進行雜湊。
import { serve, CryptoHasher } from "bun";
serve({
async fetch(req) {
const formData = await req.formData();
const image = formData.get("file");
const image = await formData.get("file").arrayBuffer();
const sha1 = CryptoHasher.hash("sha1", image, "hex");
return new Response(`SHA-1: ${sha1}`);
},
});
請注意,我們尚未將 Bun.file() 的支援新增至 Bun.CryptoHasher
。這很棘手,因為 Bun.file()
是非同步的,而 Bun.CryptoHasher
是同步的。我們將在未來版本中研究新增對其的支援。
expect(...).toBeObject
是 bun:test 中的新匹配器
toBeObject
匹配器現在可在 bun:test
中使用。
import { test, expect } from "bun:test";
test("object is an object", () => {
expect({}).toBeObject();
});
test("number is not an object", () => {
expect(1).not.toBeObject();
});
感謝 @coratgerl 新增此匹配器!
已修復:Bun.file() 中的記憶體洩漏
先前,以下程式碼會造成記憶體洩漏
import { file } from "bun";
for (let i = 0; i < 100_000; i++) {
file("/tmp/" + i + "/foo.txt");
}
檔案路徑字串被重複複製,但未釋放。現在已修復。
已修復:Bun.write() 中的記憶體洩漏
與上述類似,重複呼叫 Bun.write
會造成記憶體洩漏。
import { write } from "bun";
for (let i = 0; i < 100_000; i++) {
await write("/tmp/" + i + "/foo.txt", "Hello!");
}
同樣地,檔案路徑字串被重複複製,但未釋放。現在已修復。
已修復:來自 node:fs 的錯誤中的記憶體洩漏
當您呼叫 fs.readdir(path, { throwIfNoEntries: true })
且目錄不存在時,該錯誤訊息會造成記憶體洩漏。
這已修復。
此洩漏並非特定於 readdirSync
,幾乎所有來自 node:fs
的錯誤(包含檔案路徑字串或動態產生的訊息)都會發生此情況。這包括 fs.readFile
、fs.writeFile
、fs.stat
、fs.readlink
等。
在下一個 Bun 版本中
— Jarred Sumner (@jarredsumner) 2023年12月28日
已修復來自 node:fs 和 Bun.write 的錯誤中的記憶體洩漏 pic.twitter.com/FuY34GLlB9
已修復:bun build
現在支援 --public-path
bun build
現在支援 --public-path
旗標,可讓您指定要從中提供資產的公開路徑。
bun build ./entry.ts --public-path=/assets/ --outdir=dist
如果您從 CDN 提供資產並想要確保資產使用絕對 URL,這會很有用。
此功能已在 Bun.build
中實作,使用 JavaScript API,但錯誤地從 CLI 中省略。
已修復:在某些情況下,重新導向後 fetch() 崩潰
已修復有時在快速連續進行多個 fetch()
呼叫(其中一些呼叫會重新導向)時發生的崩潰問題,感謝 @cirospaciari。
已修復:當伺服器以非 101 狀態代碼回應時,WebSocket 用戶端崩潰
已修復 WebSocket
用戶端中的一個錯誤,該錯誤可能會在伺服器以非預期的 HTTP 回應標頭回應時導致崩潰。此錯誤在 macOS 上比在 Linux 上更頻繁發生。
已修復:當 TLS 交握失敗時,WebSocket 用戶端崩潰
已修復 WebSocket
用戶端中的一個錯誤,該錯誤可能會在伺服器以無效的 TLS 憑證回應時導致崩潰。此錯誤在 macOS 上比在 Linux 上更頻繁發生。
已修復:當 TLS 交握失敗時,Bun.listen 和 Bun.connect 崩潰
與上述類似,已修復 Bun.listen
和 Bun.connect
中的一個錯誤,該錯誤可能會在伺服器以無效的 TLS 憑證回應時導致崩潰。
已修復:bun test
中錯誤訊息中存在無效的代理配對時可能崩潰
當錯誤訊息在 bun test
中包含無效的代理配對(或其他無效的 UTF-8)時,可能會在將錯誤訊息轉換為 UTF-8(在錯誤訊息到達終端機之前)時,由於判斷提示失敗而導致崩潰。這已修復。
已修復:Bun.serve
中多餘的終止區塊
已修復 Bun.serve
中的一個錯誤,該錯誤可能會在串流時導致傳送額外的終止空區塊,感謝 @cirospaciari。
已修復:console.log() 輸出中缺少 "call"
屬性
已修復 console.log()
中的一個錯誤,該錯誤導致輸出中缺少 "call"
屬性。
console.log({ call: "where did i go??", a: 1 });
之前
{
a: 1,
}
之後
{
call: "where did i go??",
a: 1,
}
已修復:expect(error).toStrictEqual()
僅比較訊息和名稱
expect(error).toStrictEqual()
匹配器現在比較錯誤的 message
和 name
屬性,而不是所有屬性。
import { test, expect } from "bun:test";
test("errors match up", () => {
const err = new Error("foo");
err.stack;
expect(err).toStrictEqual(new Error("foo"));
});
此測試現在通過,這使行為與 Jest 和 node 的 assert
模組保持一致。感謝 @Kkaioduarte 修復此錯誤!
已修復:bun install
中的「重複依賴項」現在是警告而不是錯誤
當您執行 bun install
且 package.json 中定義了重複的依賴項時,現在會發出警告而不是錯誤。
bun install
bun install v1.0.21 (f721bbe3)
6 | "@types/bun": "latest",
^
warn: Duplicate dependency: "@types/bun" specified in package.json
at errory/package.json:6:5
10 | "@types/bun": "latest"
^
note: "@types/bun" originally specified here
at errory/package.json:10:21
感謝 @knightspore 修復此錯誤!