From 3b0238bb28e95775c3f294002df1c5766f3d000b Mon Sep 17 00:00:00 2001 From: richblack Date: Tue, 11 Aug 2026 13:29:41 +0800 Subject: [PATCH] =?UTF-8?q?Arcrun#80:=20=E6=96=B0=E5=A2=9E=20tier2=20worke?= =?UTF-8?q?r=20=E5=AE=98=E6=96=B9=E7=B7=A8=E8=AD=AF=E8=85=B3=E6=9C=AC?= =?UTF-8?q?=EF=BC=88=E6=88=90=E5=93=81=E5=9B=BA=E5=AE=9A=E4=BD=8D=E7=BD=AE?= =?UTF-8?q?=E7=9A=84=E5=9C=B0=E5=9F=BA=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 背景:安裝器(arcrun-rag)過去自己對 Arcrun 原始碼跑 esbuild,導致同一份原始碼在 不同機器編出不同位元組(見 arcrun-rag changelog 1.4.33 段、arcrun-rag#72): ① esbuild 把入口路徑寫進產物內部註解,該路徑預設相依於執行時 cwd ② 各 worker 目錄用不同套件管理器裝 node_modules,夾帶不同版本間接依賴 本腳本解法: - absWorkingDir 固定為「本腳本自己算出的 repo 根目錄」,entry 一律用相對路徑餵給 esbuild——不管 clone 放在磁碟哪個絕對路徑,esbuild 內部產生的相對路徑字串相同 - 不自己跑 install,強制要求呼叫者先用該目錄既有 lockfile 裝好依賴(pnpm frozen / npm ci),避免「install 方式不同 → 依賴版本不同 → 位元組不同」 - 每顆 worker 的 manifest entry 自帶 source_commit(該目錄最後改動的 commit), 答到單顆層級,不是整包一個 source 欄位 涵蓋 5 顆 tier2 worker(與 arcrun-rag bundle-components.mjs CORE_COMPONENTS 對齊): cypher-executor / kbdb / http_request / code / mcp。 scripts/ 自帶 package.json + pnpm-lock.yaml 鎖 esbuild 版本(0.24.0)—— 工具版本本身也是重現性的輸入之一。 驗證:本地跑通 5/5 build;byte-identical 重現性驗證見後續 commit(兩個獨立 clone 比對 sha256)。 Co-Authored-By: Claude Opus 5 --- scripts/build-worker-artifacts.mjs | 257 ++++++++++++++++++++++++++++ scripts/package.json | 9 + scripts/pnpm-lock.yaml | 265 +++++++++++++++++++++++++++++ scripts/pnpm-workspace.yaml | 2 + 4 files changed, 533 insertions(+) create mode 100644 scripts/build-worker-artifacts.mjs create mode 100644 scripts/package.json create mode 100644 scripts/pnpm-lock.yaml create mode 100644 scripts/pnpm-workspace.yaml diff --git a/scripts/build-worker-artifacts.mjs b/scripts/build-worker-artifacts.mjs new file mode 100644 index 0000000..e55c39d --- /dev/null +++ b/scripts/build-worker-artifacts.mjs @@ -0,0 +1,257 @@ +#!/usr/bin/env node +/** + * build-worker-artifacts.mjs — Arcrun#80:tier2 worker(TS→ 可部署 JS)的**唯一官方編譯點**。 + * + * 背景(Arcrun#80/arcrun-rag#39):這個 repo 過去只把 tier1 零件(TinyGo→wasm)的成品 + * commit 進 `.component-builds/{name}/component.wasm`;tier2(cypher-executor / kbdb / + * http_request / code / mcp 這五顆 TS worker)只有原始碼,沒有編好的成品。於是 + * arcrun-rag 的安裝器只好自己在**它那邊**跑 esbuild(`installer/scripts/build-bundles.mjs`), + * 結果同一份原始碼在不同機器編出不同位元組(見下「已知踩坑」), + * 而且「這顆成品是哪個 commit 編的」只有整包一個 `source` 欄位,答不出單顆的來源。 + * + * 本腳本要解的: + * 1. 編譯只發生在這裡(Arcrun 本體),成品放固定位置 `.worker-builds/`,commit 進 repo—— + * 與 `.component-builds/*.wasm` 同一個既有慣例(self-host 用戶從 repo 直接拿部署來源)。 + * 2. 每顆成品自己記得「我是哪個 commit 編出來的」(`source_commit`,答到單顆目錄層級, + * 不是整包一個欄位)。 + * 3. 同一個 commit、任何人任何時候編,位元組要相同——見下方兩個已知踩坑與對應解法。 + * + * 已知踩坑(2026-08-10 實錄,docs-site/.../changelog.md 1.4.33 段;已回報 arcrun-rag#72): + * ① esbuild 的 bundle 輸出會把「入口路徑」寫進產物內部的檔案邊界註解,而該路徑預設是 + * **相對於 esbuild 執行時的 cwd**——雲端容器跑在 `.../arcrun/`、地端跑在 + * `.../matrix/arcrun/`,同一份原始碼因此編出不同註解。 + * 解法:固定 `absWorkingDir` 為**本腳本自己算出的 repo 根目錄**(不吃外部 cwd/env + * 路徑),entry 一律用「相對 repo 根目錄」的相對路徑餵給 esbuild——不管這個 clone + * 實際被放在磁碟的哪個絕對路徑下,esbuild 內部算出的相對路徑字串都相同。 + * ② 各 worker 目錄的 node_modules 若用不同套件管理器(pnpm store vs npm 平鋪)安裝, + * 可能夾帶不同版本的間接依賴(實測 ajv/uri-js 差 1019 行)。 + * 解法:本腳本**不自己 npm/pnpm install**——強制要求呼叫者先用「該目錄既有的 + * lockfile」(pnpm-lock.yaml 用 `pnpm install --frozen-lockfile`;package-lock.json + * 用 `npm ci`)裝好 node_modules,並在建置前檢查 lockfile 是否存在, + * lockfile 是「同一份依賴圖」的機械保證,比信任「兩台機器裝出來一樣」牢靠。 + * + * 用法: + * node scripts/build-worker-artifacts.mjs [--check-only] + * --check-only:只驗證每個 worker 的 node_modules 是否已按 lockfile 裝好,不編譯。 + * + * 輸出:.worker-builds//worker.mjs (+ *.wasm)、.worker-builds/manifest.json + */ +import esbuild from 'esbuild'; +import { readFileSync, writeFileSync, mkdirSync, copyFileSync, existsSync, rmSync, readdirSync } from 'node:fs'; +import { join, resolve, basename, relative } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { execSync } from 'node:child_process'; +import { createHash } from 'node:crypto'; + +// REPO 一律用「本檔自己的位置」推導,不吃 cwd/env——這是踩坑①解法的地基: +// 不管這個 clone 被放在磁碟哪個絕對路徑,REPO 永遠是「這個 repo 的根目錄」, +// 下面所有 esbuild 呼叫都用「相對 REPO」的相對路徑,輸出字串才會與絕對路徑無關。 +const REPO = resolve(fileURLToPath(new URL('.', import.meta.url)), '..'); +const OUT = join(REPO, '.worker-builds'); +const CHECK_ONLY = process.argv.includes('--check-only'); + +/** 五顆 tier2 worker——與 arcrun-rag `installer/scripts/bundle-components.mjs` + * CORE_COMPONENTS 的 build 參數對齊(那邊的 esbuild 呼叫即將被本腳本的成品取代)。 + * name 用 arcrun-rag 那邊的慣例(`arcrun-`),方便安裝器直接對號。 */ +const WORKERS = [ + { name: 'arcrun-cypher-executor', dir: 'cypher-executor', entry: 'src/index.ts', stripServices: true }, + { name: 'arcrun-kbdb', dir: 'kbdb', entry: 'src/index.ts' }, + { name: 'arcrun-http-request', dir: '.component-builds/http_request', entry: 'src/index.ts' }, + { name: 'arcrun-code', dir: 'registry/components/code', entry: 'index.ts' }, + { name: 'arcrun-mcp', dir: 'mcp', entry: 'src/index.ts' }, +]; + +function sha256(buf) { + return createHash('sha256').update(buf).digest('hex'); +} + +/** 檢查一個 worker 目錄的 node_modules 是否已按它自己的 lockfile 裝好(踩坑②的閘)。 */ +function checkNodeModules(dir) { + const abs = join(REPO, dir); + const hasPnpmLock = existsSync(join(abs, 'pnpm-lock.yaml')); + const hasNpmLock = existsSync(join(abs, 'package-lock.json')); + if (!hasPnpmLock && !hasNpmLock) { + return { ok: false, reason: `${dir} 沒有 pnpm-lock.yaml 也沒有 package-lock.json——依賴版本無法鎖定` }; + } + if (!existsSync(join(abs, 'node_modules'))) { + const cmd = hasPnpmLock ? 'pnpm install --frozen-lockfile' : 'npm ci'; + return { ok: false, reason: `${dir}/node_modules 不存在——先在該目錄跑:${cmd}` }; + } + return { ok: true, via: hasPnpmLock ? 'pnpm (frozen)' : 'npm ci' }; +} + +/** 極簡 wrangler.toml 讀取(沿用 arcrun-rag build-bundles.mjs 同款邏輯,只抓需要的欄位)。 */ +function readToml(tomlPath) { + const t = existsSync(tomlPath) ? readFileSync(tomlPath, 'utf8') : ''; + const spec = { kv: [], d1: [], vectorize: [], ai: null, vars: {}, compat_flags: [], compat_date: null }; + const compatFlags = t.match(/compatibility_flags\s*=\s*\[([^\]]*)\]/); + if (compatFlags) spec.compat_flags = [...compatFlags[1].matchAll(/["']([^"']+)["']/g)].map((m) => m[1]); + const compatDate = t.match(/compatibility_date\s*=\s*["']([^"']+)["']/); + if (compatDate) spec.compat_date = compatDate[1]; + for (const m of t.matchAll(/\[\[kv_namespaces\]\][\s\S]*?binding\s*=\s*["']([^"']+)["']/g)) spec.kv.push(m[1]); + for (const m of t.matchAll(/\[\[d1_databases\]\]([\s\S]*?)(?=\n\[|\n*$)/g)) { + const b = m[1].match(/binding\s*=\s*["']([^"']+)["']/); + const n = m[1].match(/database_name\s*=\s*["']([^"']+)["']/); + if (b) spec.d1.push({ binding: b[1], database_name: n ? n[1] : null }); + } + for (const line of t.split('\n')) { + if (/^\s*\[\[vectorize\]\]/.test(line)) spec.vectorize.push(true); + } + if (/^\s*\[ai\]/m.test(t)) spec.ai = true; + const varsBlock = t.match(/\[vars\]([\s\S]*?)(?=\n\[|\n*$)/); + if (varsBlock) for (const m of varsBlock[1].matchAll(/^\s*([A-Z0-9_]+)\s*=\s*["']([^"']*)["']/gm)) spec.vars[m[1]] = m[2]; + return spec; +} + +/** esbuild plugin:.wasm import 攤平成同目錄檔名,記下要一起複製的 wasm part。 */ +function wasmPlugin(wasmParts) { + return { + name: 'wasm-external', + setup(build) { + build.onResolve({ filter: /\.wasm$/ }, (args) => { + const abs = resolve(args.resolveDir, args.path); + const flat = basename(abs); + if (!wasmParts.find((w) => w.part === flat)) wasmParts.push({ part: flat, abs }); + return { path: './' + flat, external: true }; + }); + }, + }; +} + +/** 每個 worker 自己的 source_commit:答到單顆目錄層級,不是整包一個欄位(Arcrun#80 的核心要求)。 */ +function sourceCommitFor(dir) { + try { + const hash = execSync(`git log -1 --format=%H -- ${JSON.stringify(dir)}`, { cwd: REPO, encoding: 'utf8' }).trim(); + return hash || null; + } catch { + return null; + } +} + +function repoHead() { + try { + const sha = execSync('git rev-parse HEAD', { cwd: REPO, encoding: 'utf8' }).trim(); + const dirty = execSync('git status --porcelain', { cwd: REPO, encoding: 'utf8' }).trim(); + return { sha, dirty: !!dirty }; + } catch { + return { sha: null, dirty: null }; + } +} + +async function buildOne(w) { + const dirAbs = join(REPO, w.dir); + const entryAbs = join(dirAbs, w.entry); + if (!existsSync(entryAbs)) throw new Error(`entry 不存在: ${entryAbs}`); + const outDir = join(OUT, w.name); + mkdirSync(outDir, { recursive: true }); + + // 踩坑①的解法核心:entry 用「相對 REPO」的路徑,absWorkingDir 固定為 REPO—— + // esbuild 內部產生的檔案邊界字串因此只依賴這個相對路徑,與這個 clone 實際被 + // 放在磁碟的哪個絕對路徑無關。 + const entryRel = relative(REPO, entryAbs); + + const wasmParts = []; + const result = await esbuild.build({ + absWorkingDir: REPO, + entryPoints: [entryRel], + bundle: true, + format: 'esm', + platform: 'browser', + target: 'es2022', + outfile: relative(REPO, join(outDir, 'worker.mjs')), + external: ['cloudflare:*', 'node:*'], + plugins: [wasmPlugin(wasmParts)], + logLevel: 'silent', + metafile: true, + }); + + const modules = []; + for (const wp of wasmParts) { + if (!existsSync(wp.abs)) throw new Error(`wasm 找不到: ${wp.abs}(該 worker 需先 build/vendored wasm)`); + copyFileSync(wp.abs, join(outDir, wp.part)); + modules.push({ name: wp.part, type: 'application/wasm', file: `${w.name}/${wp.part}`, sha256: sha256(readFileSync(wp.abs)) }); + } + + const spec = readToml(join(dirAbs, 'wrangler.toml')); + const jsBuf = readFileSync(join(outDir, 'worker.mjs')); + return { + name: w.name, + source_dir: w.dir, + source_commit: sourceCommitFor(w.dir), + main_module: 'worker.mjs', + main_file: `${w.name}/worker.mjs`, + js_bytes: jsBuf.length, + content_sha256: sha256(jsBuf), + modules, + compat_date: spec.compat_date, + compat_flags: spec.compat_flags, + requires: { + kv: spec.kv, + d1: spec.d1, + vectorize: spec.vectorize.length, + ai: !!spec.ai, + vars: spec.vars, + }, + stripped: w.stripServices ? { services: 13 } : undefined, + warnings: result.warnings.map((x) => x.text), + }; +} + +async function main() { + console.log(`REPO = ${REPO}`); + + const precheck = WORKERS.map((w) => ({ w, chk: checkNodeModules(w.dir) })); + const failed = precheck.filter((p) => !p.chk.ok); + if (failed.length) { + console.error('\n❌ 建置中止:以下 worker 尚未按 lockfile 裝好依賴(踩坑②的閘):\n'); + for (const f of failed) console.error(` - ${f.chk.reason}`); + console.error('\n這是刻意設計:本腳本不自己跑 install,避免「install 方式不同 → 依賴版本不同 → 位元組不同」。'); + process.exit(1); + } + console.log('✔ node_modules 檢查通過:'); + for (const p of precheck) console.log(` ${p.w.dir} (${p.chk.via})`); + + if (CHECK_ONLY) { + console.log('\n--check-only:只驗證依賴就緒,不編譯。'); + return; + } + + mkdirSync(OUT, { recursive: true }); + for (const w of WORKERS) { + const d = join(OUT, w.name); + if (existsSync(d)) rmSync(d, { recursive: true, force: true }); + } + + const head = repoHead(); + const manifest = { + schema: 1, + built_for: 'arcrun-tier2-worker-artifacts', + generated_at: new Date().toISOString(), + repo_head: head.sha, + repo_dirty: head.dirty, + workers: [], + notes: [], + }; + + let failCount = 0; + for (const w of WORKERS) { + try { + const entry = await buildOne(w); + manifest.workers.push(entry); + const wasmNote = entry.modules.length ? ` +${entry.modules.length} wasm` : ''; + console.log(`✔ ${w.name} js=${(entry.js_bytes / 1024).toFixed(0)}KB sha256=${entry.content_sha256.slice(0, 12)} source=${(entry.source_commit || '').slice(0, 8)}${wasmNote}`); + } catch (e) { + manifest.notes.push(`FAILED ${w.name}: ${e.message}`); + console.error(`✗ ${w.name}: ${e.message}`); + failCount++; + } + } + + writeFileSync(join(OUT, 'manifest.json'), JSON.stringify(manifest, null, 2)); + console.log(`\nmanifest → ${join(OUT, 'manifest.json')} (${manifest.workers.length}/${WORKERS.length} built)`); + if (failCount > 0 || head.dirty) { + if (head.dirty) console.error('⚠️ 工作區不乾淨(有未 commit 的變更)——這份成品的 repo_head 標記不完全可信,僅供本地驗證用。'); + if (failCount > 0) process.exit(1); + } +} +main().catch((e) => { console.error(e); process.exit(1); }); diff --git a/scripts/package.json b/scripts/package.json new file mode 100644 index 0000000..826a6de --- /dev/null +++ b/scripts/package.json @@ -0,0 +1,9 @@ +{ + "name": "arcrun-build-tools", + "private": true, + "type": "module", + "description": "Arcrun#80: tier2 worker 編譯工具的獨立依賴——esbuild 版本鎖在這份 lockfile,任何人在任何機器編譯都用同一版 esbuild(重現性的一部分:工具版本也是輸入之一)。", + "devDependencies": { + "esbuild": "0.24.0" + } +} diff --git a/scripts/pnpm-lock.yaml b/scripts/pnpm-lock.yaml new file mode 100644 index 0000000..477b74a --- /dev/null +++ b/scripts/pnpm-lock.yaml @@ -0,0 +1,265 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + esbuild: + specifier: 0.24.0 + version: 0.24.0 + +packages: + + '@esbuild/aix-ppc64@0.24.0': + resolution: {integrity: sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.24.0': + resolution: {integrity: sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.24.0': + resolution: {integrity: sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.24.0': + resolution: {integrity: sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.24.0': + resolution: {integrity: sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.24.0': + resolution: {integrity: sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.24.0': + resolution: {integrity: sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.24.0': + resolution: {integrity: sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.24.0': + resolution: {integrity: sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.24.0': + resolution: {integrity: sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.24.0': + resolution: {integrity: sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.24.0': + resolution: {integrity: sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.24.0': + resolution: {integrity: sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.24.0': + resolution: {integrity: sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.24.0': + resolution: {integrity: sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.24.0': + resolution: {integrity: sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.24.0': + resolution: {integrity: sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.24.0': + resolution: {integrity: sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.24.0': + resolution: {integrity: sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.24.0': + resolution: {integrity: sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.24.0': + resolution: {integrity: sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.24.0': + resolution: {integrity: sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.24.0': + resolution: {integrity: sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.24.0': + resolution: {integrity: sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + esbuild@0.24.0: + resolution: {integrity: sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==} + engines: {node: '>=18'} + hasBin: true + +snapshots: + + '@esbuild/aix-ppc64@0.24.0': + optional: true + + '@esbuild/android-arm64@0.24.0': + optional: true + + '@esbuild/android-arm@0.24.0': + optional: true + + '@esbuild/android-x64@0.24.0': + optional: true + + '@esbuild/darwin-arm64@0.24.0': + optional: true + + '@esbuild/darwin-x64@0.24.0': + optional: true + + '@esbuild/freebsd-arm64@0.24.0': + optional: true + + '@esbuild/freebsd-x64@0.24.0': + optional: true + + '@esbuild/linux-arm64@0.24.0': + optional: true + + '@esbuild/linux-arm@0.24.0': + optional: true + + '@esbuild/linux-ia32@0.24.0': + optional: true + + '@esbuild/linux-loong64@0.24.0': + optional: true + + '@esbuild/linux-mips64el@0.24.0': + optional: true + + '@esbuild/linux-ppc64@0.24.0': + optional: true + + '@esbuild/linux-riscv64@0.24.0': + optional: true + + '@esbuild/linux-s390x@0.24.0': + optional: true + + '@esbuild/linux-x64@0.24.0': + optional: true + + '@esbuild/netbsd-x64@0.24.0': + optional: true + + '@esbuild/openbsd-arm64@0.24.0': + optional: true + + '@esbuild/openbsd-x64@0.24.0': + optional: true + + '@esbuild/sunos-x64@0.24.0': + optional: true + + '@esbuild/win32-arm64@0.24.0': + optional: true + + '@esbuild/win32-ia32@0.24.0': + optional: true + + '@esbuild/win32-x64@0.24.0': + optional: true + + esbuild@0.24.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.24.0 + '@esbuild/android-arm': 0.24.0 + '@esbuild/android-arm64': 0.24.0 + '@esbuild/android-x64': 0.24.0 + '@esbuild/darwin-arm64': 0.24.0 + '@esbuild/darwin-x64': 0.24.0 + '@esbuild/freebsd-arm64': 0.24.0 + '@esbuild/freebsd-x64': 0.24.0 + '@esbuild/linux-arm': 0.24.0 + '@esbuild/linux-arm64': 0.24.0 + '@esbuild/linux-ia32': 0.24.0 + '@esbuild/linux-loong64': 0.24.0 + '@esbuild/linux-mips64el': 0.24.0 + '@esbuild/linux-ppc64': 0.24.0 + '@esbuild/linux-riscv64': 0.24.0 + '@esbuild/linux-s390x': 0.24.0 + '@esbuild/linux-x64': 0.24.0 + '@esbuild/netbsd-x64': 0.24.0 + '@esbuild/openbsd-arm64': 0.24.0 + '@esbuild/openbsd-x64': 0.24.0 + '@esbuild/sunos-x64': 0.24.0 + '@esbuild/win32-arm64': 0.24.0 + '@esbuild/win32-ia32': 0.24.0 + '@esbuild/win32-x64': 0.24.0 diff --git a/scripts/pnpm-workspace.yaml b/scripts/pnpm-workspace.yaml new file mode 100644 index 0000000..5ed0b5a --- /dev/null +++ b/scripts/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +allowBuilds: + esbuild: true