diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 5994169a065..555017223ea 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -72,8 +72,11 @@ pnpm build ``` For repeated local typechecks, use `pnpm tc` (or `pnpm tc:node`, `pnpm tc:web`, -and `pnpm tc:cli`). These keep TypeScript's incremental cache. CI uses -`pnpm typecheck:ci` with Bun for full checks. Shared tests are checked by the +and `pnpm tc:cli`). These run `bun check` from the pinned `bun` devDependency — +about a third of tsc's CPU and an eighth of its memory, so several agents can +typecheck at once. Pass `--compiler=native` to use tsc and its incremental +cache instead. CI uses `pnpm typecheck:ci`, the same Bun check with CI's +concurrency. Shared tests are checked by the Node project; shared production code is checked in all three environments. Run `pnpm tc:node` after editing shared tests, or `pnpm tc` to check everything. diff --git a/.github/actions/install-node-dependencies/action.yml b/.github/actions/install-node-dependencies/action.yml index f04eb5e3a72..b81dd6aed51 100644 --- a/.github/actions/install-node-dependencies/action.yml +++ b/.github/actions/install-node-dependencies/action.yml @@ -209,6 +209,14 @@ runs: git -C "$GITHUB_WORKSPACE" diff --exit-code -- package.json pnpm-lock.yaml pnpm-workspace.yaml fi + # Why: the script-free install leaves node_modules/.bin/bun as a stub that shadows setup-bun under `pnpm run`. + - name: Link pinned Bun + shell: bash + run: | + if [ -d node_modules/bun ]; then + pnpm rebuild bun + fi + # Why --if-present: release cuts run main's workflows against older release tags that predate this script. - name: Build internal packages shell: bash diff --git a/config/scripts/pinned-bun-executable.mjs b/config/scripts/pinned-bun-executable.mjs new file mode 100644 index 00000000000..58d73364629 --- /dev/null +++ b/config/scripts/pinned-bun-executable.mjs @@ -0,0 +1,35 @@ +import { readFileSync } from 'node:fs' +import { createRequire } from 'node:module' +import path from 'node:path' + +export function readPinnedBunVersion(root) { + return readFileSync(path.join(root, 'config', '.bun-version'), 'utf8').trim() +} + +/** + * Absolute path of the `bun` devDependency when it runs and is exactly the version in + * config/.bun-version, otherwise null. Its binary is only real once the postinstall ran. + */ +export function resolvePinnedBunExecutable(root, runProcessSync) { + try { + const manifestPath = createRequire(path.join(root, 'package.json')).resolve('bun/package.json') + const { bin } = JSON.parse(readFileSync(manifestPath, 'utf8')) + const executable = path.join(path.dirname(manifestPath), bin.bun) + const probe = runProcessSync({ + program: executable, + args: ['--version'], + cwd: root, + env: process.env, + timeoutMs: 2000, + maxOutputBytes: 4096 + }) + return probe.code === 0 && + !probe.signal && + !probe.timedOut && + probe.stdout.trim() === readPinnedBunVersion(root) + ? executable + : null + } catch { + return null + } +} diff --git a/config/scripts/run-typecheck-projects-in-parallel.mjs b/config/scripts/run-typecheck-projects-in-parallel.mjs index 91935273fc1..ebf6389c20b 100644 --- a/config/scripts/run-typecheck-projects-in-parallel.mjs +++ b/config/scripts/run-typecheck-projects-in-parallel.mjs @@ -1,9 +1,10 @@ -import { spawnProcess } from '@orca/process-host' +import { runProcessSync, spawnProcess } from '@orca/process-host' import { readFileSync } from 'node:fs' import { createRequire } from 'node:module' import { availableParallelism, totalmem } from 'node:os' import { dirname, join } from 'node:path' import { fileURLToPath, pathToFileURL } from 'node:url' +import { readPinnedBunVersion, resolvePinnedBunExecutable } from './pinned-bun-executable.mjs' const BYTES_PER_GIB = 1024 ** 3 @@ -70,15 +71,31 @@ export function resolveProjectCompiler(config, root = repoRoot) { return resolveProjectCompilerInfo(config, root).program } +// Bun 1.4.3 targets TypeScript 7.0.2; compiler upgrades require a new parity comparison. +export const BUN_TYPESCRIPT_VERSION = '7.0.2' + export function parseTypecheckOptions(args) { - const options = { compiler: 'native', bunConcurrency: 1, bunThreads: 4 } + const options = { + compiler: 'bun', + bunConcurrency: 2, + bunThreads: 4, + projects: TYPECHECK_PROJECTS + } + const selected = [] for (const argument of args) { - if (argument === '--compiler=bun' || argument === '--compiler=native') { + if (/^--compiler=(bun|native)$/.test(argument)) { options.compiler = argument.slice('--compiler='.length) } else if (argument.startsWith('--bun-concurrency=')) { options.bunConcurrency = Number(argument.slice('--bun-concurrency='.length)) } else if (argument.startsWith('--bun-threads=')) { options.bunThreads = Number(argument.slice('--bun-threads='.length)) + } else if (argument.startsWith('--project=')) { + const config = argument.slice('--project='.length) + const project = TYPECHECK_PROJECTS.find((candidate) => candidate.config === config) + if (!project) { + throw new Error(`Unknown typecheck project: ${config}`) + } + selected.push(project) } else { throw new Error(`Unknown typecheck option: ${argument}`) } @@ -88,9 +105,29 @@ export function parseTypecheckOptions(args) { throw new Error('Bun concurrency and threads must be integers from 1 to 4') } } + if (selected.length > 0) { + options.projects = selected + } return options } +/** + * Bun is the default: on the node project it uses ~1/3 of tsc's CPU and ~2 GB instead of + * ~16 GB, and tsc's incremental cache rarely survives an edit to an export. + */ +export function selectTypecheckCompiler(compiler, { root, resolveBunExecutable }) { + if (compiler === 'native') { + return { compiler } + } + const bunExecutable = resolveBunExecutable(root) + if (!bunExecutable) { + throw new Error( + `Bun ${readPinnedBunVersion(root)} from devDependencies is missing; run pnpm install, or pass --compiler=native.` + ) + } + return { compiler, bunExecutable } +} + export function typecheckBatches(projects, { compiler, bunConcurrency, memoryBytes, parallelism }) { return planTypecheckBatches( compiler === 'bun' ? projects.map((project) => ({ ...project, heapGib: 4 })) : projects, @@ -101,17 +138,16 @@ export function typecheckBatches(projects, { compiler, bunConcurrency, memoryByt ) } -function checkProject(config, { compiler, bunThreads, root, spawnCompiler }) { +function checkProject(config, { compiler, bunExecutable, bunThreads, root, spawnCompiler }) { return new Promise((resolve, reject) => { const installedCompiler = resolveProjectCompilerInfo(config, root) - // Bun 1.4.3 targets TypeScript 7.0.2; compiler upgrades require a new parity comparison. - if (compiler === 'bun' && installedCompiler.version !== '7.0.2') { + if (compiler === 'bun' && installedCompiler.version !== BUN_TYPESCRIPT_VERSION) { throw new Error( - `Bun CI compiler targets TypeScript 7.0.2, but ${config} resolves ${installedCompiler.version}` + `Bun CI compiler targets TypeScript ${BUN_TYPESCRIPT_VERSION}, but ${config} resolves ${installedCompiler.version}` ) } const child = spawnCompiler({ - program: compiler === 'bun' ? 'bun' : process.execPath, + program: compiler === 'bun' ? bunExecutable : process.execPath, args: compiler === 'bun' ? ['check', '-p', config, '--threads', String(bunThreads), '--no-pretty', '--all'] @@ -134,6 +170,7 @@ function checkProject(config, { compiler, bunThreads, root, spawnCompiler }) { export async function runTypecheckProjects({ compiler = 'native', + bunExecutable = 'bun', bunConcurrency = 1, bunThreads = 4, root = repoRoot, @@ -153,7 +190,13 @@ export async function runTypecheckProjects({ while (next < pending.length) { const index = next++ try { - await checkProject(pending[index].config, { compiler, bunThreads, root, spawnCompiler }) + await checkProject(pending[index].config, { + compiler, + bunExecutable, + bunThreads, + root, + spawnCompiler + }) } catch (reason) { failures.push({ index, reason }) } @@ -167,7 +210,7 @@ export async function runTypecheckProjects({ for (const batch of batches) { const results = await Promise.allSettled( batch.map((project) => - checkProject(project.config, { compiler, bunThreads, root, spawnCompiler }) + checkProject(project.config, { compiler, bunExecutable, bunThreads, root, spawnCompiler }) ) ) for (const result of results) { @@ -180,7 +223,12 @@ export async function runTypecheckProjects({ } if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { - const failures = await runTypecheckProjects(parseTypecheckOptions(process.argv.slice(2))) + const options = parseTypecheckOptions(process.argv.slice(2)) + const selection = selectTypecheckCompiler(options.compiler, { + root: repoRoot, + resolveBunExecutable: (root) => resolvePinnedBunExecutable(root, runProcessSync) + }) + const failures = await runTypecheckProjects({ ...options, ...selection }) if (failures.length > 0) { for (const failure of failures) { console.error(failure.message ?? failure) diff --git a/config/scripts/run-typecheck-projects-in-parallel.test.mjs b/config/scripts/run-typecheck-projects-in-parallel.test.mjs index ef695a7124b..5a404bb4d7b 100644 --- a/config/scripts/run-typecheck-projects-in-parallel.test.mjs +++ b/config/scripts/run-typecheck-projects-in-parallel.test.mjs @@ -18,6 +18,7 @@ import { planTypecheckBatches, resolveProjectCompiler, parseTypecheckOptions, + selectTypecheckCompiler, typecheckBatches, runTypecheckProjects } from './run-typecheck-projects-in-parallel.mjs' @@ -132,16 +133,57 @@ describe('workspace package test typecheck', () => { }) describe('CI compiler selection', () => { - it('keeps native incremental checking as the local default', () => { - expect(parseTypecheckOptions([]).compiler).toBe('native') + it('checks with Bun by default', () => { + expect(parseTypecheckOptions([]).compiler).toBe('bun') }) - it.each(['--compiler=typo', '--bun-concurrency=0', '--bun-threads=NaN', '--bun-concurrency=5'])( - 'rejects unsafe or unknown options: %s', - (argument) => { - expect(() => parseTypecheckOptions([argument])).toThrow() - } - ) + it('pins the Bun devDependency to config/.bun-version', () => { + const manifest = JSON.parse(readFileSync(join(repoRoot, 'package.json'), 'utf8')) + expect(manifest.devDependencies.bun).toBe( + readFileSync(join(repoRoot, 'config', '.bun-version'), 'utf8').trim() + ) + }) + + it('narrows to the requested projects and rejects unknown ones', () => { + expect(parseTypecheckOptions(['--project=config/tsconfig.node.json']).projects).toEqual([ + TYPECHECK_PROJECTS[0] + ]) + expect(parseTypecheckOptions([]).projects).toEqual(TYPECHECK_PROJECTS) + expect(() => parseTypecheckOptions(['--project=config/tsconfig.json'])).toThrow() + }) + + it('runs the resolved Bun and refuses to silently fall back when it is missing', () => { + expect( + selectTypecheckCompiler('bun', { + root: repoRoot, + resolveBunExecutable: () => '/repo/node_modules/bun/bin/bun.exe' + }) + ).toEqual({ compiler: 'bun', bunExecutable: '/repo/node_modules/bun/bin/bun.exe' }) + expect(() => + selectTypecheckCompiler('bun', { root: repoRoot, resolveBunExecutable: () => null }) + ).toThrow('run pnpm install') + }) + + it('never probes Bun when tsc is requested', () => { + expect( + selectTypecheckCompiler('native', { + root: repoRoot, + resolveBunExecutable: () => { + throw new Error('probed') + } + }) + ).toEqual({ compiler: 'native' }) + }) + + it.each([ + '--compiler=typo', + '--bun-concurrency=0', + '--bun-threads=NaN', + '--bun-concurrency=5', + '--project' + ])('rejects unsafe or unknown options: %s', (argument) => { + expect(() => parseTypecheckOptions([argument])).toThrow() + }) it('bounds Bun project concurrency and reserves memory for overlapping checks', () => { const options = parseTypecheckOptions([ diff --git a/config/scripts/verify-localization-extraction.mjs b/config/scripts/verify-localization-extraction.mjs index c3cc7774be6..650c3f4d576 100644 --- a/config/scripts/verify-localization-extraction.mjs +++ b/config/scripts/verify-localization-extraction.mjs @@ -79,39 +79,14 @@ async function extractToTemporaryCatalog(root, tempDir) { const outputPattern = path.join(tempDir, '{{language}}.json') // Why: extraction output is evidence for this check, not another committed // catalog that feature authors must keep synchronized. - const [{ runProcessSync }, { describeProcessFailure }] = await Promise.all([ - import('@orca/process-host'), - import('./process-failure-message.mjs') - ]) - let program = process.execPath - try { - const version = (await fs.readFile(path.join(root, 'config', '.bun-version'), 'utf8')).trim() - const probe = runProcessSync({ - program: 'bun', - args: [ - '-e', - 'console.log(JSON.stringify({ version: process.versions.bun, executable: process.execPath }))' - ], - cwd: root, - env: process.env, - timeoutMs: 2000, - maxOutputBytes: 4096 - }) - const identity = JSON.parse(probe.stdout) - if ( - probe.code === 0 && - !probe.signal && - !probe.timedOut && - !probe.outputTruncated && - identity.version === version && - typeof identity.executable === 'string' && - path.isAbsolute(identity.executable) - ) { - program = identity.executable - } - } catch { - // Missing or unusable optional Bun keeps the existing parent runtime. - } + const [{ runProcessSync }, { describeProcessFailure }, { resolvePinnedBunExecutable }] = + await Promise.all([ + import('@orca/process-host'), + import('./process-failure-message.mjs'), + import('./pinned-bun-executable.mjs') + ]) + // Missing or unusable optional Bun keeps the existing parent runtime. + const program = resolvePinnedBunExecutable(root, runProcessSync) ?? process.execPath const result = runProcessSync({ program, args: [cliPath, '--config', 'config/i18next.config.ts', 'extract', '--sync-primary', '--quiet'], diff --git a/package.json b/package.json index a7b78aa8a37..98610514deb 100644 --- a/package.json +++ b/package.json @@ -66,9 +66,9 @@ "tc:cli": "pnpm run typecheck:cli", "tc:web": "pnpm run typecheck:web", "tc": "pnpm run typecheck", - "typecheck:node": "pnpm run build:packages && node node_modules/typescript/bin/tsc --noEmit -p config/tsconfig.node.json", - "typecheck:cli": "pnpm run build:packages && node node_modules/typescript/bin/tsc --noEmit -p config/tsconfig.tc.cli.json", - "typecheck:web": "pnpm run build:packages && node node_modules/typescript/bin/tsc --noEmit -p config/tsconfig.tc.web.json", + "typecheck:node": "pnpm run build:packages && node config/scripts/run-typecheck-projects-in-parallel.mjs --project=config/tsconfig.node.json", + "typecheck:cli": "pnpm run build:packages && node config/scripts/run-typecheck-projects-in-parallel.mjs --project=config/tsconfig.tc.cli.json", + "typecheck:web": "pnpm run build:packages && node config/scripts/run-typecheck-projects-in-parallel.mjs --project=config/tsconfig.tc.web.json", "typecheck:e2e": "pnpm run build:packages && node node_modules/typescript/bin/tsc --noEmit -p config/tsconfig.e2e.json", "typecheck": "pnpm run build:packages && node config/scripts/run-typecheck-projects-in-parallel.mjs", "typecheck:ci": "pnpm run build:packages && node config/scripts/run-typecheck-projects-in-parallel.mjs --compiler=bun", @@ -268,6 +268,7 @@ "@xterm/addon-web-links": "0.13.0-beta.301", "@xterm/addon-webgl": "0.20.0-beta.300", "@xterm/xterm": "6.1.0-beta.304", + "bun": "1.4.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f291af8feb1..55ac5e183e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -435,6 +435,9 @@ importers: '@xterm/xterm': specifier: 6.1.0-beta.304 version: 6.1.0-beta.304(patch_hash=87de83f62154c0f2c31eb17d14b881e139f3368f8be1e9e2951bfeba16d86156) + bun: + specifier: 1.4.3 + version: 1.4.3 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -1529,6 +1532,70 @@ packages: resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} engines: {node: '>=8.0.0'} + '@oven/bun-darwin-aarch64@1.4.3': + resolution: {integrity: sha512-u2wAM9RzqyF4baO5oFVhcFUVWO1CCMLG6yC5/aPWCYGZPfL5ULMbeHt+0/3I0h3yaPx1m+PKSANO/61jPtDZcQ==} + cpu: [arm64] + os: [darwin] + + '@oven/bun-darwin-x64@1.4.3': + resolution: {integrity: sha512-wRGeciWOyKyG/BfTHq4CAyqoOfuzj5UJkKA8FlJETW2yhtDh+Ba3EgPnsVclFyzIw7E0HJxpsonoj17g3HPK/Q==} + cpu: [x64] + os: [darwin] + + '@oven/bun-freebsd-aarch64@1.4.3': + resolution: {integrity: sha512-z4LiDRPTJMKA8JvtP+Z+I5m1syJr8HGYFainIJoCAdkWaHMyQMaEEYr0N+FzTK4pECUHUmscCTNfC9XJwTe4dQ==} + cpu: [arm64] + os: [freebsd] + + '@oven/bun-freebsd-x64@1.4.3': + resolution: {integrity: sha512-YpwGnfvtSMRMnnViJI5wCGmS5HpCBAl0wLhBflJfmZYkgOHfplzeKUkUVDW2bNOZyrym3YN+EDaLWooaJpX8Eg==} + cpu: [x64] + os: [freebsd] + + '@oven/bun-linux-aarch64-android@1.4.3': + resolution: {integrity: sha512-KCsYD+HHawd6xht4BOffTnLZXNf+xzq8l52MsIF2yRtIfUvGTCumdnJPXxuo969iXukb7cWtz+WMkpYlKMG+sg==} + cpu: [arm64] + os: [android] + + '@oven/bun-linux-aarch64-musl@1.4.3': + resolution: {integrity: sha512-5OJIbcVspZxIN/zQtrFYwLuJgaOlciGcynmwCtwKyg0rmXHJoNG0QyspiiLnDUDoVTSGY/nrn0QEbWKhu0QbEQ==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oven/bun-linux-aarch64@1.4.3': + resolution: {integrity: sha512-ylogDFxDD+aRKO0TJauwDzCWM+rULvc85gRNZQ9CnY6rm2BU4GC2XUDi20bh67CIuB4Wt/0PL1xpsJ2hqplMww==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oven/bun-linux-x64-android@1.4.3': + resolution: {integrity: sha512-5CdcvAc+iTqf/p6S0VkoGF8harKMjTfEkMOXJHslwBzl5GcY5alth1mkhD3T7SqLA5aUal95vzfiNjoAZXkPOw==} + cpu: [x64] + os: [android] + + '@oven/bun-linux-x64-musl@1.4.3': + resolution: {integrity: sha512-L0gTTjTncLqv7VsV/2iLz7Zk4vd969xqRgbtGnskmV3MvL+IJgzTyQr5zm8GGGWSm/SO/ab2vnn4GXgPomoGbg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oven/bun-linux-x64@1.4.3': + resolution: {integrity: sha512-RArgnpjeXjaGzjp5h7Rs89cUYJutO8IyqR3itE8OBaBzEt8KNm2jp/YozxBHq4eFovwrQLryeSwTAcmiT+zcoA==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oven/bun-windows-aarch64@1.4.3': + resolution: {integrity: sha512-a3Vyu9KI0J9vfIS+/LJMmjWI/qsBpph8u0R7d6Tm+YaSOqZeN2LRULfVqz13dgQRTvqg6aWBxOv9jE4Rl0lAAQ==} + cpu: [arm64] + os: [win32] + + '@oven/bun-windows-x64@1.4.3': + resolution: {integrity: sha512-9pV16qXHTFvZDH61S8hpdpVBbWmvEqekczLF54zTnQHKP28D6yRCNLrD/7jVbXYR61fQYAx4FSBgzCxGCKb3rQ==} + cpu: [x64] + os: [win32] + '@oxc-parser/binding-android-arm-eabi@0.148.0': resolution: {integrity: sha512-pHASv9g5pASxb7akHERZNSkrEqPhFaUix98o7d9hbTpolnnFWl7UiRrcMhCsV1+iVO4/cJwKsbKRJTFNs2tdBQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4242,6 +4309,12 @@ packages: resolution: {integrity: sha512-q2hn7Mbo2nFNkVekPiHFx6Nfo3hURmES3tfBn+k5Pqxl2RkmP3QGqZUhH/q9Pch/4G05NRhPjDlVj1O8q4Txvw==} engines: {node: '>=14.0.0'} + bun@1.4.3: + resolution: {integrity: sha512-EvQQ2dUJlixjKgJjeXZvdsHUWIKnVfYufrQNDHa++0YM4XksQ4GtGofEdRnQSiyNp/yUWiX6SlqS8zugZCo+2A==} + cpu: [arm64, x64] + os: [darwin, linux, android, freebsd, win32] + hasBin: true + bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} @@ -8762,6 +8835,42 @@ snapshots: '@opentelemetry/api@1.9.1': optional: true + '@oven/bun-darwin-aarch64@1.4.3': + optional: true + + '@oven/bun-darwin-x64@1.4.3': + optional: true + + '@oven/bun-freebsd-aarch64@1.4.3': + optional: true + + '@oven/bun-freebsd-x64@1.4.3': + optional: true + + '@oven/bun-linux-aarch64-android@1.4.3': + optional: true + + '@oven/bun-linux-aarch64-musl@1.4.3': + optional: true + + '@oven/bun-linux-aarch64@1.4.3': + optional: true + + '@oven/bun-linux-x64-android@1.4.3': + optional: true + + '@oven/bun-linux-x64-musl@1.4.3': + optional: true + + '@oven/bun-linux-x64@1.4.3': + optional: true + + '@oven/bun-windows-aarch64@1.4.3': + optional: true + + '@oven/bun-windows-x64@1.4.3': + optional: true + '@oxc-parser/binding-android-arm-eabi@0.148.0': optional: true @@ -11160,6 +11269,21 @@ snapshots: transitivePeerDependencies: - supports-color + bun@1.4.3: + optionalDependencies: + '@oven/bun-darwin-aarch64': 1.4.3 + '@oven/bun-darwin-x64': 1.4.3 + '@oven/bun-freebsd-aarch64': 1.4.3 + '@oven/bun-freebsd-x64': 1.4.3 + '@oven/bun-linux-aarch64': 1.4.3 + '@oven/bun-linux-aarch64-android': 1.4.3 + '@oven/bun-linux-aarch64-musl': 1.4.3 + '@oven/bun-linux-x64': 1.4.3 + '@oven/bun-linux-x64-android': 1.4.3 + '@oven/bun-linux-x64-musl': 1.4.3 + '@oven/bun-windows-aarch64': 1.4.3 + '@oven/bun-windows-x64': 1.4.3 + bundle-name@4.1.0: dependencies: run-applescript: 7.1.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8ff7fe9896c..77236710d55 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -106,6 +106,7 @@ allowBuilds: '@swc/core': false '@vscode/windows-process-tree': false agent-browser: false + bun: true cpu-features: true electron-winstaller: false esbuild: true