From 558c70aa24ee40853cc279d80bdfeda7069df62a Mon Sep 17 00:00:00 2001 From: Nessar Date: Sat, 17 Oct 2026 12:49:17 +0200 Subject: [PATCH] docs(adr): record Bun 1.4.3 verification results and decisions Stable Bun 1.4.3 runs installs, the development server, standalone type checks, the native test runner and a Bun.serve static server. Native lint/fmt are canary-only, and Angular's server build needs Node loader hooks Bun does not apply, so the spec now keeps ESLint/Prettier until a stable release, builds Angular client-only behind a Bun static server, converts line coverage from lcov, and replaces the heap cap with a container memory limit. Refs #289 Co-Authored-By: Claude Opus 5.5 --- .../0009-bun-runtime-and-native-tooling.md | 8 ++ docs/bun-adoption.md | 74 ++++++++++++++++++- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/docs/adr/0009-bun-runtime-and-native-tooling.md b/docs/adr/0009-bun-runtime-and-native-tooling.md index 2abfdf1..5aa648f 100644 --- a/docs/adr/0009-bun-runtime-and-native-tooling.md +++ b/docs/adr/0009-bun-runtime-and-native-tooling.md @@ -11,6 +11,14 @@ This deliberately accepts Bun-specific tooling and migration work while retainin Angular's standard compiler and build pipeline, including TypeScript 6 for Angular compilation alongside `bun check` for standalone TypeScript checking. +Verification on stable Bun 1.4.3 narrowed this: ESLint and Prettier stay, +run by Bun, until a stable release ships the native linter and formatter; +Angular builds a client-only bundle, because its server build relies on Node +loader hooks Bun does not apply, and a small `Bun.serve` server delivers it, +giving up `@angular/ssr`; coverage is line-level, converted from lcov to the +existing HTML and Cobertura reports; and the benchmark's heap cap becomes a +container memory limit. + Existing behavior, tests, lint rules, coverage reporting, and benchmark budgets must be preserved; incompatibilities must be fixed or reported as blockers before declaring completion, and a Node.js fallback does not meet the runtime requirement. diff --git a/docs/bun-adoption.md b/docs/bun-adoption.md index d0c0c45..ad8662f 100644 --- a/docs/bun-adoption.md +++ b/docs/bun-adoption.md @@ -1,7 +1,8 @@ # Bun Runtime and Native Tooling — Development Spec -Status: local draft synthesized from the agreed individual decisions. Integration -feasibility remains unverified; ADR 0009 remains proposed. +Status: the agreed decisions are amended by the verification results in #289, +recorded below. ADR 0009 remains proposed until the migration passes its final +gate. ## Problem Statement @@ -104,6 +105,75 @@ The following acceptance criteria define completion: native-tool documentation. Select the exact version only after verifying the required capabilities together. +## Verification Results and Decisions (#289) + +Probes ran on 2026-10-17 against the stable Bun `1.4.3` release (`1.4.3+c6da4a4d3`) +from a clean checkout. "Node unavailable" means a `PATH` holding only a `bun` +executable plus `/usr/bin:/bin`, with `command -v node` failing. + +**Verified:** + +- **Install.** `bun install --frozen-lockfile` succeeds and leaves `bun.lock` + unchanged. +- **Runtime.** Package binaries with a `#!/usr/bin/env node` shebang (`ng`, + `eslint`, `prettier`, `tsc`) run on Bun only with `--bun`. Without it they use + Node when Node is on `PATH`, so every such command must pass `--bun` + explicitly. Spawned build children are native executables (esbuild, + dart-sass); Angular's workers are threads of the Bun process. +- **Development server.** `bun --bun x ng serve` serves the application and + direct navigation to `/benchmark` with Node unavailable. +- **Type checking.** `bun check -p tsconfig.app.json` and + `-p tsconfig.spec.json` match `tsc` 6 on the clean tree and catch injected + application and test type errors. Only the Angular build catches template + errors, as expected. +- **Tests.** `bun test` runs the whole functional suite with a preload that + compiles components through Angular's JIT transform with inlined templates, + sets up jsdom, TestBed and zoneless change detection, plus a small shim for + the Vitest APIs Bun lacks (`stubGlobal`, `unstubAllGlobals`, `waitFor`, + `advanceTimersByTimeAsync`, `expect.poll`, `toHaveBeenCalledExactlyOnceWith`, + `setSystemTime` ordering and Date-only fake timers). Real-worker specs work + with Node unavailable. `--isolate` or `--parallel` is required for per-file + isolation. No spec uses snapshots. +- **Server.** `Bun.serve` passes route, asset, cache-header, missing-file and + path-traversal checks. +- **CI scripts.** The policy regression script passes under Bun. The image + regression script needs its fake `docker` shebang changed from `node` to + `bun`. + +**Decisions taken after the probes:** + +- **Linter and formatter.** Native `bun lint` and `bun fmt` exist only in canary + builds (`1.4.4-canary.1`), not in stable `1.4.3`. Pin stable `1.4.3` + everywhere and keep ESLint and Prettier, executed by Bun, until a stable + release ships them. Then replace them in one change. Until then, user story 5 + stays open. +- **Client-only Angular build and a Bun static server.** Angular's production + build fails under Bun whenever a server or SSR entry is configured: route + extraction loads the server bundle through Node `module.register` loader + hooks, which Bun accepts but never applies. Every route is client-rendered + today, so the Angular build drops its server and SSR entries and a small + `Bun.serve` server delivers static assets with the same cache behavior and + falls back to `index.html`. This replaces the agreed Angular web request + handler integration and gives up `@angular/ssr`. Adding server rendering later + requires revisiting this decision. +- **Coverage.** `bun test` reports text and lcov with line data only. HTML and + Cobertura reports are generated from the lcov with the istanbul report + libraries. Branch and function coverage are no longer reported; this + reduction is accepted. +- **Benchmark memory guard.** JavaScriptCore has no `--max-old-space-size` + equivalent. The CI benchmark job enforces the limit with a container memory + limit instead of a heap-only cap. + +**Platform constraints, not project commands:** + +- **Gitea actions.** `actions/checkout@v4` (`node20`) and + `actions/upload-artifact@v3` (`node16`) are JavaScript actions, so the job + container must still provide `node` for the runner. Project commands must not + use it. +- **Allowed hosts.** Found while probing: the current Express server answers 400 + unless `NG_ALLOWED_HOSTS` is set, and the compose files only set `PORT`. The + static Bun server removes this dependency. + ## Testing Decisions - Test observable behavior and failure outcomes, rather than configuration text