diff --git a/compose.prod.yml b/compose.prod.yml index 6d64a2b..fc06b55 100644 --- a/compose.prod.yml +++ b/compose.prod.yml @@ -17,7 +17,7 @@ services: 'CMD', 'bun', '-e', - "fetch('http://localhost:4000/').then(r => process.exit(r.ok ? 0 : 1), () => process.exit(1))", + "fetch('http://localhost:' + (process.env.PORT || 4000) + '/').then(r => process.exit(r.ok ? 0 : 1), () => process.exit(1))", ] interval: 30s timeout: 10s diff --git a/compose.yml b/compose.yml index 29ebf3c..9283a33 100644 --- a/compose.yml +++ b/compose.yml @@ -17,7 +17,7 @@ services: 'CMD', 'bun', '-e', - "fetch('http://localhost:4000/').then(r => process.exit(r.ok ? 0 : 1), () => process.exit(1))", + "fetch('http://localhost:' + (process.env.PORT || 4000) + '/').then(r => process.exit(r.ok ? 0 : 1), () => process.exit(1))", ] interval: 30s timeout: 10s diff --git a/docs/bun-adoption.md b/docs/bun-adoption.md index e1ff60e..7c2ab82 100644 --- a/docs/bun-adoption.md +++ b/docs/bun-adoption.md @@ -41,10 +41,6 @@ start); it is not a result from the live Gitea workflow. the live runner. The `images` job still has Alpine's `nodejs` on `PATH` for the checkout action, which the project command does not use. These and the Gitea activation steps remain unverified until a live pull-request run. -- **Intermittent spec.** `real planning worker cancellation > preserves balances -and provenance after a valid checkpoint` failed in one of three full coverage - runs on the development laptop (it cancels from a stage's progress callback and - asserts that the stage did not complete); the cause is not yet investigated. - **ADR 0009** stays `proposed` until the final gate. ## Problem Statement @@ -276,23 +272,18 @@ remains proposed; the Status section lists what is implemented. The glossary receives no new terms because the choices concern general tooling, rather than new leave-planning concepts. -Remaining verification and unresolved requirements: +Remaining verification (everything else is recorded as verified under Status): -- Angular compilation, test-resource loading, serializers, mocking, isolation, - and the existing worker harnesses must work under the native Bun runner. -- Standalone TypeScript 7 diagnostics must be checked alongside Angular's - TypeScript 6 diagnostics; their coexistence is agreed, but project-specific - differences have not been measured. -- Current Istanbul reports include text, HTML, and Cobertura. Native coverage - equivalence and report-format support have not been established; accepting a - reduced capability or different downstream format has not been agreed. -- Third-party Gitea checkout and artifact actions may impose their own Node - requirement. Identify the actual platform constraint; no exception allowing - project commands to fall back to Node has been agreed, and CI infrastructure - changes beyond the migration require a separate decision. -- The exact Bun release and container compatibility remain to be verified. - The local installation currently lacks the documented native lint, format, - and standalone type-check commands. +- Native lint and format wait for a stable Bun release that ships `bun lint` and + `bun fmt` (#290). +- Live Gitea validation (#293): the custom job `shell` template, the Bun download + in each job, the `images` job with publisher credentials, and the benchmark's + timing margin on the runners. +- The `images` job keeps Alpine's `nodejs` on `PATH` because the checkout action + needs it (a runner requirement); project commands do not use it and no Node + fallback for them is agreed. +- `src/test-setup.ts` imports Analog's internal `jitTransform` by path; replace it + when Angular or Analog exports a JIT transform. Research checked on 2026-10-17: @@ -305,8 +296,8 @@ Research checked on 2026-10-17: - [Angular build guidance](https://angular.dev/ecosystem/custom-build-pipeline) recommends retaining its standard CLI pipeline. - [Angular's web request engine](https://angular.dev/api/ssr/AngularAppEngine) and - [Bun's HTTP server](https://bun.sh/docs/runtime/http/server) provide the intended - server integration contracts; their compatibility here remains untested. + [Bun's HTTP server](https://bun.sh/docs/runtime/http/server) describe the original + server integration plan, replaced by the `Bun.serve` static server above. - [Angular version compatibility](https://angular.dev/reference/versions) and [compiler roadmap](https://angular.dev/roadmap) establish the current Angular compiler boundary. [Microsoft's coexistence guidance](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/#typescript-and-embedded-languages) diff --git a/scripts/coverage-reports.mjs b/scripts/coverage-reports.mjs index b06f85c..398ed96 100644 --- a/scripts/coverage-reports.mjs +++ b/scripts/coverage-reports.mjs @@ -1,7 +1,8 @@ // Converts coverage/lcov.info (Bun) to HTML and Cobertura. Run: bun scripts/coverage-reports.mjs // shortcut: line coverage only, Bun's lcov has no usable branch/function data; revisit if Bun adds it. -import { readFileSync } from 'node:fs'; -import { resolve } from 'node:path'; +// Totals here sum lines (8351/8578); Bun's text table averages per-file percentages, so it reads lower. +import { readdirSync, readFileSync, writeFileSync } from 'node:fs'; +import { join, resolve } from 'node:path'; import libCoverage from 'istanbul-lib-coverage'; import libReport from 'istanbul-lib-report'; import reports from 'istanbul-reports'; @@ -29,3 +30,26 @@ for (const record of readFileSync('coverage/lcov.info', 'utf8').split('end_of_re } const context = libReport.createContext({ dir: 'coverage', coverageMap: map }); for (const name of ['html', 'cobertura']) reports.create(name).execute(context); + +// Istanbul reports 0 branches/functions as 100%; relabel them instead of claiming coverage. +const rewrite = (file, edit) => writeFileSync(file, edit(readFileSync(file, 'utf8'))); +for (const page of readdirSync('coverage', { recursive: true }).filter((f) => f.endsWith('.html'))) + rewrite(join('coverage', page), (html) => + html + .replace( + /100% <\/span>(\s*(?:Branches|Functions)<\/span>\s*)0\/0n/a $1not collected<', + ) + .replace( + /100%<\/td>(\s*)0\/0<\/td>/g, + 'n/a$1not collected', + ), + ); +rewrite('coverage/cobertura-coverage.xml', (xml) => + xml + .replaceAll('branch-rate="1"', 'branch-rate="0"') + .replace( + '\n { it.each([ null, + [undefined], [[]], { ...OPTIMIZATION_PRESETS.shortSequenceDeficit, conditions: ['internal', 'internal'] }, { ...OPTIMIZATION_PRESETS.longestWorkSequence, direction: 'up' }, diff --git a/src/testing/vitest-compat.ts b/src/testing/vitest-compat.ts index d2bacce..574a3db 100644 --- a/src/testing/vitest-compat.ts +++ b/src/testing/vitest-compat.ts @@ -52,14 +52,14 @@ export const vi = Object.assign(bt.vi, { }, runAllTicks() {}, async waitFor(fn: () => T | Promise, { timeout = 1000, interval = 50 } = {}): Promise { - const end = Date.now() + timeout; + const end = performance.now() + timeout; let last: unknown; for (;;) { try { return await fn(); } catch (e) { last = e; - if (Date.now() > end) throw last; + if (performance.now() > end) throw last; await new Promise((r) => setTimeout(r, interval)); } } @@ -78,13 +78,13 @@ export type MockInstance = bt.Mock; m === 'not' ? chain(!negate) : async (...args: unknown[]) => { - const end = Date.now() + timeout; + const end = performance.now() + timeout; for (;;) { try { const e: any = bt.expect(await fn()); return await (negate ? e.not : e)[m](...args); } catch (err) { - if (Date.now() > end) throw err; + if (performance.now() > end) throw err; await new Promise((r) => setTimeout(r, interval)); } }