@burdenoff/toolchain 0.0.0-stage → 2026.1107.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js ADDED
@@ -0,0 +1,3014 @@
1
+ #!/usr/bin/env bun
2
+ // @bun
3
+
4
+ // src/coverage-gate.ts
5
+ import { existsSync as existsSync2, readFileSync as readFileSync2, rmSync } from "fs";
6
+ import { isAbsolute, join as join2, relative } from "path";
7
+
8
+ // src/lib/args.ts
9
+ function parseArgs(argv, valueFlags = []) {
10
+ const flags = new Map;
11
+ const positionals = [];
12
+ let rest = [];
13
+ for (let i = 0;i < argv.length; i++) {
14
+ const a = argv[i] ?? "";
15
+ if (a === "--") {
16
+ rest = argv.slice(i + 1);
17
+ break;
18
+ }
19
+ if (a.startsWith("--")) {
20
+ const eq = a.indexOf("=");
21
+ if (eq > 0) {
22
+ flags.set(a.slice(2, eq), a.slice(eq + 1));
23
+ continue;
24
+ }
25
+ const name = a.slice(2);
26
+ const next = argv[i + 1];
27
+ if (valueFlags.includes(name) && next !== undefined && !next.startsWith("--")) {
28
+ flags.set(name, next);
29
+ i++;
30
+ continue;
31
+ }
32
+ flags.set(name, true);
33
+ continue;
34
+ }
35
+ positionals.push(a);
36
+ }
37
+ return { flags, positionals, rest };
38
+ }
39
+ function flagString(args, name) {
40
+ const v = args.flags.get(name);
41
+ return typeof v === "string" ? v : undefined;
42
+ }
43
+ function flagBool(args, name) {
44
+ return args.flags.has(name);
45
+ }
46
+
47
+ // src/lib/proc.ts
48
+ function capture(cmd, opts = {}) {
49
+ try {
50
+ const res = Bun.spawnSync([...cmd], {
51
+ cwd: opts.cwd,
52
+ env: opts.env,
53
+ stdin: opts.stdin === undefined ? "ignore" : new TextEncoder().encode(opts.stdin),
54
+ stdout: "pipe",
55
+ stderr: "pipe"
56
+ });
57
+ return {
58
+ code: res.exitCode ?? 1,
59
+ stdout: res.stdout.toString(),
60
+ stderr: res.stderr.toString()
61
+ };
62
+ } catch (error) {
63
+ return { code: 127, stdout: "", stderr: error instanceof Error ? error.message : String(error) };
64
+ }
65
+ }
66
+
67
+ // src/lib/git.ts
68
+ function isGitRepo(cwd) {
69
+ return capture(["git", "rev-parse", "--is-inside-work-tree"], { cwd }).stdout.trim() === "true";
70
+ }
71
+ function listRepoFiles(cwd) {
72
+ const res = capture(["git", "ls-files", "-z", "-c", "-o", "--exclude-standard"], { cwd });
73
+ if (res.code !== 0)
74
+ return null;
75
+ const seen = new Set;
76
+ for (const f of res.stdout.split("\x00")) {
77
+ if (f !== "")
78
+ seen.add(f);
79
+ }
80
+ return [...seen].sort();
81
+ }
82
+ function showHead(cwd, path) {
83
+ const res = capture(["git", "show", `HEAD:${path}`], { cwd });
84
+ return res.code === 0 ? res.stdout : null;
85
+ }
86
+ function refExists(cwd, ref) {
87
+ return capture(["git", "rev-parse", "--verify", "--quiet", `${ref}^{commit}`], { cwd }).code === 0;
88
+ }
89
+ function differsFromHead(cwd, path) {
90
+ const tracked = capture(["git", "ls-files", "--error-unmatch", "--", path], { cwd }).code === 0;
91
+ if (!tracked)
92
+ return true;
93
+ return capture(["git", "diff", "--quiet", "HEAD", "--", path], { cwd }).code !== 0;
94
+ }
95
+ function diffAgainst(cwd, base) {
96
+ const mergeBase = capture(["git", "merge-base", base, "HEAD"], { cwd });
97
+ const from = mergeBase.code === 0 ? mergeBase.stdout.trim() : base;
98
+ const res = capture(["git", "diff", "-U0", "--no-color", "--no-ext-diff", from, "--"], { cwd });
99
+ return res.code === 0 ? res.stdout : null;
100
+ }
101
+
102
+ // src/lib/log.ts
103
+ var consoleWriter = {
104
+ out: (line) => {
105
+ process.stdout.write(`${line}
106
+ `);
107
+ },
108
+ err: (line) => {
109
+ process.stderr.write(`${line}
110
+ `);
111
+ }
112
+ };
113
+ function isGithubActions(env = process.env) {
114
+ return env.GITHUB_ACTIONS === "true";
115
+ }
116
+ function isCi(env = process.env) {
117
+ const v = env.CI;
118
+ return v !== undefined && v !== "" && v !== "0" && v.toLowerCase() !== "false";
119
+ }
120
+ function renderTable(header, rows) {
121
+ const widths = header.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length)));
122
+ const fmt = (cells) => cells.map((c, i) => c.padEnd(widths[i] ?? 0)).join(" ").trimEnd();
123
+ return [fmt(header), widths.map((w) => "-".repeat(w)).join(" "), ...rows.map(fmt)];
124
+ }
125
+
126
+ // src/lib/repo.ts
127
+ import { existsSync, readFileSync } from "fs";
128
+ import { join } from "path";
129
+ var REPO_TYPES = [
130
+ "svc",
131
+ "gateway",
132
+ "be-sdk",
133
+ "mfe",
134
+ "fe-libs",
135
+ "app",
136
+ "website",
137
+ "docs",
138
+ "cli",
139
+ "mcp",
140
+ "sdk-node",
141
+ "browser-ext",
142
+ "vscode-ext",
143
+ "plugin",
144
+ "doctor",
145
+ "lib"
146
+ ];
147
+ function isRepoType(value) {
148
+ return typeof value === "string" && REPO_TYPES.includes(value);
149
+ }
150
+ function isRecord(v) {
151
+ return typeof v === "object" && v !== null && !Array.isArray(v);
152
+ }
153
+ function stringArray(v, where) {
154
+ if (v === undefined)
155
+ return;
156
+ if (Array.isArray(v) && v.every((x) => typeof x === "string"))
157
+ return v;
158
+ throw new Error(`package.json ${where} must be an array of strings`);
159
+ }
160
+ function optionalNumber(v, where) {
161
+ if (v === undefined)
162
+ return;
163
+ if (typeof v === "number" && Number.isFinite(v) && v >= 0 && v <= 100)
164
+ return v;
165
+ throw new Error(`package.json ${where} must be a number between 0 and 100`);
166
+ }
167
+ function parseBoffConfig(raw) {
168
+ if (raw === undefined)
169
+ return {};
170
+ if (!isRecord(raw))
171
+ throw new Error('package.json "boff" must be an object');
172
+ const allowed = new Set(["type", "sanity", "coverage", "scripts", "depsMode"]);
173
+ for (const key of Object.keys(raw)) {
174
+ if (!allowed.has(key))
175
+ throw new Error(`package.json "boff.${key}" is not a known key`);
176
+ }
177
+ const cfg = {};
178
+ if (raw.type !== undefined) {
179
+ if (!isRepoType(raw.type)) {
180
+ throw new Error(`package.json "boff.type" must be one of: ${REPO_TYPES.join(", ")}`);
181
+ }
182
+ cfg.type = raw.type;
183
+ }
184
+ if (raw.sanity !== undefined) {
185
+ if (!isRecord(raw.sanity))
186
+ throw new Error('package.json "boff.sanity" must be an object');
187
+ const s = raw.sanity;
188
+ for (const key of Object.keys(s)) {
189
+ if (!["steps", "extra", "formatPaths", "codegenCommitted"].includes(key)) {
190
+ throw new Error(`package.json "boff.sanity.${key}" is not a known key`);
191
+ }
192
+ }
193
+ cfg.sanity = {
194
+ steps: stringArray(s.steps, '"boff.sanity.steps"'),
195
+ extra: stringArray(s.extra, '"boff.sanity.extra"'),
196
+ formatPaths: stringArray(s.formatPaths, '"boff.sanity.formatPaths"'),
197
+ codegenCommitted: stringArray(s.codegenCommitted, '"boff.sanity.codegenCommitted"')
198
+ };
199
+ }
200
+ if (raw.coverage !== undefined) {
201
+ if (!isRecord(raw.coverage))
202
+ throw new Error('package.json "boff.coverage" must be an object');
203
+ const c = raw.coverage;
204
+ cfg.coverage = {
205
+ global: optionalNumber(c.global, '"boff.coverage.global"'),
206
+ diff: optionalNumber(c.diff, '"boff.coverage.diff"'),
207
+ exclude: stringArray(c.exclude, '"boff.coverage.exclude"')
208
+ };
209
+ }
210
+ if (raw.scripts !== undefined) {
211
+ if (!isRecord(raw.scripts))
212
+ throw new Error('package.json "boff.scripts" must be an object');
213
+ cfg.scripts = { unmanaged: stringArray(raw.scripts.unmanaged, '"boff.scripts.unmanaged"') };
214
+ }
215
+ if (raw.depsMode !== undefined) {
216
+ if (!isRecord(raw.depsMode))
217
+ throw new Error('package.json "boff.depsMode" must be an object');
218
+ const d = raw.depsMode;
219
+ const paths = {};
220
+ if (d.paths !== undefined) {
221
+ if (!isRecord(d.paths))
222
+ throw new Error('package.json "boff.depsMode.paths" must be an object');
223
+ for (const [k, v] of Object.entries(d.paths)) {
224
+ if (typeof v !== "string")
225
+ throw new Error(`package.json "boff.depsMode.paths.${k}" must be a string`);
226
+ paths[k] = v;
227
+ }
228
+ }
229
+ if (d.stateFile !== undefined && typeof d.stateFile !== "string") {
230
+ throw new Error('package.json "boff.depsMode.stateFile" must be a string');
231
+ }
232
+ cfg.depsMode = {
233
+ paths: d.paths === undefined ? undefined : paths,
234
+ stateFile: typeof d.stateFile === "string" ? d.stateFile : undefined
235
+ };
236
+ }
237
+ return cfg;
238
+ }
239
+ function readPackageJson(root) {
240
+ const path = join(root, "package.json");
241
+ if (!existsSync(path))
242
+ return null;
243
+ const raw = JSON.parse(readFileSync(path, "utf8"));
244
+ if (!isRecord(raw))
245
+ throw new Error(`${path} is not a JSON object`);
246
+ return raw;
247
+ }
248
+ function inferRepoType(root, pkg) {
249
+ const name = (pkg?.name ?? "").replace(/^@[^/]+\//, "");
250
+ const has = (p) => existsSync(join(root, p));
251
+ if (/^(wspace|global)-.+-svc$/.test(name) || has("prisma") && has("src/modules"))
252
+ return "svc";
253
+ if (/-gateway$/.test(name))
254
+ return "gateway";
255
+ if (name === "be-sdk")
256
+ return "be-sdk";
257
+ if (name === "fe-libs")
258
+ return "fe-libs";
259
+ if (/^microfe-/.test(name))
260
+ return "mfe";
261
+ if (/-browser-extension$/.test(name) || has("manifest.json"))
262
+ return "browser-ext";
263
+ if (/-code-extension$/.test(name) || pkg?.engines?.vscode !== undefined)
264
+ return "vscode-ext";
265
+ if (/^vibe-plugin-/.test(name))
266
+ return "plugin";
267
+ if (/-sdk-node$|-sdk$/.test(name))
268
+ return "sdk-node";
269
+ if (/-mcp$|-mcp-server$/.test(name))
270
+ return "mcp";
271
+ if (/-cli$/.test(name))
272
+ return "cli";
273
+ if (/-docs$/.test(name) || has("docusaurus.config.ts") || has("docusaurus.config.js"))
274
+ return "docs";
275
+ if (/-website$/.test(name))
276
+ return "website";
277
+ if (/-app$/.test(name))
278
+ return "app";
279
+ if (/-doctor$/.test(name))
280
+ return "doctor";
281
+ return "lib";
282
+ }
283
+ function loadRepo(root, typeOverride) {
284
+ const pkg = readPackageJson(root);
285
+ const config = parseBoffConfig(pkg?.boff);
286
+ let type;
287
+ if (typeOverride !== undefined) {
288
+ if (!isRepoType(typeOverride)) {
289
+ throw new Error(`unknown repo type "${typeOverride}" (expected one of: ${REPO_TYPES.join(", ")})`);
290
+ }
291
+ type = typeOverride;
292
+ } else {
293
+ type = config.type ?? inferRepoType(root, pkg);
294
+ }
295
+ return { root, pkg, config, type };
296
+ }
297
+ function hasScript(ctx, name) {
298
+ return typeof ctx.pkg?.scripts?.[name] === "string";
299
+ }
300
+
301
+ // src/coverage-gate.ts
302
+ var DEFAULT_GLOBAL_THRESHOLD = 80;
303
+ var DEFAULT_DIFF_THRESHOLD = 80;
304
+ var DEFAULT_COMPARE_BRANCH = "origin/main";
305
+ var DEFAULT_EXCLUDES = [
306
+ "**/generated/**",
307
+ "**/*.generated.ts",
308
+ "**/__tests__/**",
309
+ "**/*.test.*",
310
+ "**/*.spec.*",
311
+ "src/test/**",
312
+ "test/**",
313
+ "tests/**",
314
+ "**/node_modules/**"
315
+ ];
316
+ function counter(value, line) {
317
+ const n = Number(value);
318
+ if (!Number.isInteger(n) || n < 0)
319
+ throw new Error(`malformed lcov line: "${line}"`);
320
+ return n;
321
+ }
322
+ function parseLcov(text) {
323
+ const files = [];
324
+ let cur = null;
325
+ for (const raw of text.split(/\r?\n/)) {
326
+ const line = raw.trim();
327
+ if (line === "")
328
+ continue;
329
+ if (line.startsWith("SF:")) {
330
+ cur = { path: line.slice(3), linesFound: 0, linesHit: 0, functionsFound: 0, functionsHit: 0, lines: new Map };
331
+ continue;
332
+ }
333
+ if (line === "end_of_record") {
334
+ if (cur !== null)
335
+ files.push(cur);
336
+ cur = null;
337
+ continue;
338
+ }
339
+ if (cur === null)
340
+ continue;
341
+ const sep = line.indexOf(":");
342
+ if (sep <= 0)
343
+ continue;
344
+ const tag = line.slice(0, sep);
345
+ const value = line.slice(sep + 1);
346
+ switch (tag) {
347
+ case "LF":
348
+ cur.linesFound = counter(value, line);
349
+ break;
350
+ case "LH":
351
+ cur.linesHit = counter(value, line);
352
+ break;
353
+ case "FNF":
354
+ cur.functionsFound = counter(value, line);
355
+ break;
356
+ case "FNH":
357
+ cur.functionsHit = counter(value, line);
358
+ break;
359
+ case "DA": {
360
+ const [ln, hits] = value.split(",");
361
+ const lineNo = counter(ln ?? "", line);
362
+ const count = counter(hits ?? "", line);
363
+ cur.lines.set(lineNo, (cur.lines.get(lineNo) ?? 0) + count);
364
+ break;
365
+ }
366
+ default:
367
+ break;
368
+ }
369
+ }
370
+ if (cur !== null)
371
+ files.push(cur);
372
+ return files;
373
+ }
374
+ function normalisePath(root, sf) {
375
+ const rel = isAbsolute(sf) ? relative(root, sf) : sf;
376
+ return rel.replace(/\\/g, "/").replace(/^\.\//, "");
377
+ }
378
+ function filterFiles(files, root, excludes) {
379
+ const globs = excludes.map((p) => new Bun.Glob(p));
380
+ return files.map((f) => ({ ...f, path: normalisePath(root, f.path) })).filter((f) => !globs.some((g) => g.match(f.path)));
381
+ }
382
+ function totals(files) {
383
+ const t = { files: files.length, linesFound: 0, linesHit: 0, functionsFound: 0, functionsHit: 0 };
384
+ for (const f of files) {
385
+ t.linesFound += f.linesFound;
386
+ t.linesHit += f.linesHit;
387
+ t.functionsFound += f.functionsFound;
388
+ t.functionsHit += f.functionsHit;
389
+ }
390
+ return t;
391
+ }
392
+ function pct(hit, found) {
393
+ return found === 0 ? 100 : hit / found * 100;
394
+ }
395
+ function parseDiff(diff) {
396
+ const out = new Map;
397
+ let current = null;
398
+ for (const line of diff.split(`
399
+ `)) {
400
+ if (line.startsWith("+++ ")) {
401
+ const target = line.slice(4).trim();
402
+ if (target === "/dev/null") {
403
+ current = null;
404
+ continue;
405
+ }
406
+ const path = target.replace(/^b\//, "");
407
+ current = out.get(path) ?? new Set;
408
+ out.set(path, current);
409
+ continue;
410
+ }
411
+ const m = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/.exec(line);
412
+ if (m !== null && current !== null) {
413
+ const start = Number(m[1]);
414
+ const count = m[2] === undefined ? 1 : Number(m[2]);
415
+ for (let i = 0;i < count; i++)
416
+ current.add(start + i);
417
+ }
418
+ }
419
+ return out;
420
+ }
421
+ function diffCoverage(files, changed) {
422
+ const byPath = new Map(files.map((f) => [f.path, f]));
423
+ let instrumented = 0;
424
+ let covered = 0;
425
+ const uncovered = new Map;
426
+ for (const [path, lines] of changed) {
427
+ const cov = byPath.get(path);
428
+ if (cov === undefined)
429
+ continue;
430
+ for (const ln of [...lines].sort((a, b) => a - b)) {
431
+ const hits = cov.lines.get(ln);
432
+ if (hits === undefined)
433
+ continue;
434
+ instrumented++;
435
+ if (hits > 0)
436
+ covered++;
437
+ else
438
+ uncovered.set(path, [...uncovered.get(path) ?? [], ln]);
439
+ }
440
+ }
441
+ return { instrumented, covered, uncovered };
442
+ }
443
+ function formatRanges(lines) {
444
+ const parts = [];
445
+ let start = lines[0];
446
+ let prev = lines[0];
447
+ for (const n of lines.slice(1)) {
448
+ if (prev !== undefined && n === prev + 1) {
449
+ prev = n;
450
+ continue;
451
+ }
452
+ if (start !== undefined && prev !== undefined)
453
+ parts.push(start === prev ? `${start}` : `${start}-${prev}`);
454
+ start = n;
455
+ prev = n;
456
+ }
457
+ if (start !== undefined && prev !== undefined)
458
+ parts.push(start === prev ? `${start}` : `${start}-${prev}`);
459
+ return parts.join(", ");
460
+ }
461
+ function envNumber(env, name) {
462
+ const v = env[name];
463
+ if (v === undefined || v.trim() === "")
464
+ return;
465
+ const n = Number(v);
466
+ if (!Number.isFinite(n) || n < 0 || n > 100)
467
+ throw new Error(`${name} must be a number between 0 and 100`);
468
+ return n;
469
+ }
470
+ var HELP = `usage: boff coverage-gate [options]
471
+
472
+ Reads coverage/lcov.info and enforces:
473
+ aggregate lines and functions >= global threshold
474
+ coverage of changed lines vs the compare branch >= diff threshold
475
+
476
+ Options:
477
+ --lcov <path> lcov file (default coverage/lcov.info)
478
+ --global <pct> global threshold (env GLOBAL_THRESHOLD, package.json boff.coverage.global, default 80)
479
+ --diff <pct> diff threshold (env DIFF_THRESHOLD, package.json boff.coverage.diff, default 80)
480
+ --compare-branch <ref> base ref (env COMPARE_BRANCH, default origin/main)
481
+ --no-diff skip the diff check
482
+ --run-tests run bun test --coverage first (drop-in for scripts/check-coverage.sh)`;
483
+ async function coverageGate(opts) {
484
+ const env = opts.env ?? process.env;
485
+ const w = opts.writer ?? consoleWriter;
486
+ const args = parseArgs(opts.argv ?? [], ["lcov", "global", "diff", "compare-branch"]);
487
+ if (flagBool(args, "help")) {
488
+ w.out(HELP);
489
+ return 0;
490
+ }
491
+ const repo = loadRepo(opts.root);
492
+ const cfg = repo.config.coverage ?? {};
493
+ const cliNumber = (name) => {
494
+ const v = flagString(args, name);
495
+ if (v === undefined)
496
+ return;
497
+ const n = Number(v);
498
+ if (!Number.isFinite(n) || n < 0 || n > 100)
499
+ throw new Error(`--${name} must be a number between 0 and 100`);
500
+ return n;
501
+ };
502
+ const globalThreshold = cliNumber("global") ?? envNumber(env, "GLOBAL_THRESHOLD") ?? cfg.global ?? DEFAULT_GLOBAL_THRESHOLD;
503
+ const diffThreshold = cliNumber("diff") ?? envNumber(env, "DIFF_THRESHOLD") ?? cfg.diff ?? DEFAULT_DIFF_THRESHOLD;
504
+ const compareBranch = flagString(args, "compare-branch") ?? env.COMPARE_BRANCH ?? DEFAULT_COMPARE_BRANCH;
505
+ const lcovPath = join2(opts.root, flagString(args, "lcov") ?? "coverage/lcov.info");
506
+ const annotate = isGithubActions(env);
507
+ const fail = (msg) => {
508
+ w.err(annotate ? `::error title=coverage gate::${msg}` : `coverage gate: ${msg}`);
509
+ };
510
+ if (flagBool(args, "run-tests")) {
511
+ rmSync(join2(opts.root, "coverage"), { recursive: true, force: true });
512
+ const envFile = existsSync2(join2(opts.root, ".env.local")) ? ["--env-file=.env.local"] : [];
513
+ const proc = Bun.spawn(["bun", ...envFile, "test", "--coverage", "--coverage-reporter=text", "--coverage-reporter=lcov"], { cwd: opts.root, env, stdin: "ignore", stdout: "inherit", stderr: "inherit" });
514
+ const rc = await proc.exited;
515
+ if (rc !== 0) {
516
+ fail(`bun test exited with ${rc}`);
517
+ return rc;
518
+ }
519
+ }
520
+ if (!existsSync2(lcovPath)) {
521
+ fail(`no coverage report at ${relative(opts.root, lcovPath)} (run bun test --coverage --coverage-reporter=lcov)`);
522
+ return 1;
523
+ }
524
+ const excludes = [...DEFAULT_EXCLUDES, ...cfg.exclude ?? []];
525
+ const files = filterFiles(parseLcov(readFileSync2(lcovPath, "utf8")), opts.root, excludes);
526
+ const t = totals(files);
527
+ const failures = [];
528
+ const rows = [];
529
+ const linePct = pct(t.linesHit, t.linesFound);
530
+ const fnPct = pct(t.functionsHit, t.functionsFound);
531
+ const verdict = (ok) => ok ? "pass" : "FAIL";
532
+ if (t.files === 0 || t.linesFound === 0)
533
+ failures.push("coverage report contains no source files after excludes");
534
+ rows.push([
535
+ "lines",
536
+ `${t.linesHit}/${t.linesFound}`,
537
+ `${linePct.toFixed(2)}%`,
538
+ `${globalThreshold}%`,
539
+ verdict(linePct >= globalThreshold)
540
+ ]);
541
+ rows.push([
542
+ "functions",
543
+ `${t.functionsHit}/${t.functionsFound}`,
544
+ `${fnPct.toFixed(2)}%`,
545
+ `${globalThreshold}%`,
546
+ verdict(fnPct >= globalThreshold)
547
+ ]);
548
+ if (linePct < globalThreshold) {
549
+ failures.push(`line coverage ${linePct.toFixed(2)}% is below the ${globalThreshold}% threshold`);
550
+ }
551
+ if (fnPct < globalThreshold) {
552
+ failures.push(`function coverage ${fnPct.toFixed(2)}% is below the ${globalThreshold}% threshold`);
553
+ }
554
+ let uncoveredReport = [];
555
+ if (flagBool(args, "no-diff")) {
556
+ rows.push(["diff", "-", "-", `${diffThreshold}%`, "skipped (--no-diff)"]);
557
+ } else if (!isGitRepo(opts.root) || !refExists(opts.root, compareBranch)) {
558
+ if (isCi(env)) {
559
+ failures.push(`compare branch ${compareBranch} is not available; fetch it (actions/checkout fetch-depth: 0) or set COMPARE_BRANCH`);
560
+ rows.push(["diff", "-", "-", `${diffThreshold}%`, "FAIL (no base ref)"]);
561
+ } else {
562
+ rows.push(["diff", "-", "-", `${diffThreshold}%`, `skipped (${compareBranch} not found locally)`]);
563
+ }
564
+ } else {
565
+ const diff = diffAgainst(opts.root, compareBranch);
566
+ if (diff === null) {
567
+ failures.push(`git diff against ${compareBranch} failed`);
568
+ } else {
569
+ const d = diffCoverage(files, parseDiff(diff));
570
+ const dPct = pct(d.covered, d.instrumented);
571
+ const ok = dPct >= diffThreshold;
572
+ rows.push([
573
+ `diff vs ${compareBranch}`,
574
+ `${d.covered}/${d.instrumented}`,
575
+ d.instrumented === 0 ? "n/a" : `${dPct.toFixed(2)}%`,
576
+ `${diffThreshold}%`,
577
+ d.instrumented === 0 ? "pass (no coverable changed lines)" : verdict(ok)
578
+ ]);
579
+ if (!ok)
580
+ failures.push(`diff coverage ${dPct.toFixed(2)}% is below the ${diffThreshold}% threshold`);
581
+ uncoveredReport = [...d.uncovered].map(([p, lns]) => ` ${p}: ${formatRanges(lns)}`);
582
+ }
583
+ }
584
+ w.out(`Coverage (${t.files} files, excludes: ${excludes.length} globs)`);
585
+ for (const line of renderTable(["metric", "covered", "percent", "threshold", "result"], rows))
586
+ w.out(line);
587
+ if (uncoveredReport.length > 0) {
588
+ w.out("Changed lines without coverage:");
589
+ for (const line of uncoveredReport)
590
+ w.out(line);
591
+ }
592
+ if (failures.length > 0) {
593
+ for (const f of failures)
594
+ fail(f);
595
+ return 1;
596
+ }
597
+ w.out("coverage gate: passed");
598
+ return 0;
599
+ }
600
+
601
+ // src/deps-mode.ts
602
+ import { copyFileSync, existsSync as existsSync4, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync } from "fs";
603
+ import { dirname as dirname2, join as join4, relative as relative2, resolve } from "path";
604
+
605
+ // src/lib/meta.ts
606
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
607
+ import { dirname, join as join3 } from "path";
608
+ var SUPPORTED_BUN = "1.4.4";
609
+ var MANAGED_START = "# >>> boff managed (do not edit)";
610
+ var MANAGED_END = "# <<< boff managed";
611
+ function findPackageRoot(start) {
612
+ let dir = start;
613
+ for (;; ) {
614
+ const candidate = join3(dir, "package.json");
615
+ if (existsSync3(candidate) && readFileSync3(candidate, "utf8").includes('"@burdenoff/toolchain"'))
616
+ return dir;
617
+ const parent = dirname(dir);
618
+ if (parent === dir)
619
+ throw new Error(`cannot locate the @burdenoff/toolchain package root above ${start}`);
620
+ dir = parent;
621
+ }
622
+ }
623
+ var PACKAGE_ROOT = findPackageRoot(import.meta.dir);
624
+ function readOwnPackage() {
625
+ const raw = JSON.parse(readFileSync3(join3(PACKAGE_ROOT, "package.json"), "utf8"));
626
+ if (typeof raw === "object" && raw !== null && "name" in raw && "version" in raw && typeof raw.name === "string" && typeof raw.version === "string") {
627
+ return { name: raw.name, version: raw.version };
628
+ }
629
+ throw new Error(`toolchain package.json at ${PACKAGE_ROOT} is missing name or version`);
630
+ }
631
+ var own = readOwnPackage();
632
+ var TOOLCHAIN_NAME = own.name;
633
+ var TOOLCHAIN_VERSION = own.version;
634
+
635
+ // src/deps-mode.ts
636
+ var INTERNAL_PREFIX = "@burdenoff/";
637
+ var DEP_SECTIONS = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
638
+ var REWRITE_SECTIONS = ["dependencies", "devDependencies"];
639
+ var LOCAL_REF = /^(workspace:|file:|link:|\.\.\/|\.\/|\/home\/|\/Users\/|\/root\/)/;
640
+ function conventionalDirs(pkgName) {
641
+ const short = pkgName.replace(INTERNAL_PREFIX, "");
642
+ const known = {
643
+ "be-sdk": ["burdenoff-be-sdk-node"],
644
+ "fe-libs": ["fe-libs"],
645
+ "fluidgrids-fe-sdk": ["fluidgrids/fluidgrids-fe-sdk"]
646
+ };
647
+ if (known[short] !== undefined)
648
+ return known[short];
649
+ if (short.startsWith("microfe-"))
650
+ return [`microfe/${short}`];
651
+ return [short, `burdenoff-${short}`];
652
+ }
653
+
654
+ class DepsMode {
655
+ root;
656
+ ctx;
657
+ w;
658
+ silent;
659
+ fetchImpl;
660
+ constructor(root, opts = {}) {
661
+ this.root = root;
662
+ this.ctx = loadRepo(root);
663
+ this.w = opts.writer ?? consoleWriter;
664
+ this.silent = opts.silent === true;
665
+ this.fetchImpl = opts.fetchImpl ?? fetch;
666
+ }
667
+ log(msg) {
668
+ if (!this.silent)
669
+ this.w.out(msg);
670
+ }
671
+ error(msg) {
672
+ this.w.err(msg);
673
+ }
674
+ get pkgPath() {
675
+ return join4(this.root, "package.json");
676
+ }
677
+ get lockPath() {
678
+ return join4(this.root, "bun.lock");
679
+ }
680
+ get lockWorkspacePath() {
681
+ return join4(this.root, "bun.lock.workspace");
682
+ }
683
+ get statePath() {
684
+ const configured = this.ctx.config.depsMode?.stateFile;
685
+ if (configured !== undefined)
686
+ return join4(this.root, configured);
687
+ if (existsSync4(join4(this.root, ".boiler-env.json")))
688
+ return join4(this.root, ".boiler-env.json");
689
+ return join4(this.root, ".vibe-env.json");
690
+ }
691
+ readPkg() {
692
+ return JSON.parse(readFileSync4(this.pkgPath, "utf8"));
693
+ }
694
+ writePkg(pkg) {
695
+ writeFileSync(this.pkgPath, `${JSON.stringify(pkg, null, 2)}
696
+ `);
697
+ }
698
+ readState() {
699
+ if (!existsSync4(this.statePath))
700
+ return {};
701
+ try {
702
+ const raw = JSON.parse(readFileSync4(this.statePath, "utf8"));
703
+ if (typeof raw !== "object" || raw === null)
704
+ return {};
705
+ return Object.fromEntries(Object.entries(raw).filter((e) => typeof e[1] === "string"));
706
+ } catch {
707
+ return {};
708
+ }
709
+ }
710
+ writeState(state) {
711
+ writeFileSync(this.statePath, `${JSON.stringify(state, null, 2)}
712
+ `);
713
+ }
714
+ mode() {
715
+ return this.readState().depsMode === "workspace" ? "workspace" : "npm";
716
+ }
717
+ setMode(mode) {
718
+ const { _restore_workspace: restore } = this.readState();
719
+ this.writeState(restore === undefined ? { depsMode: mode } : { depsMode: mode, _restore_workspace: restore });
720
+ }
721
+ internalPackages() {
722
+ const pkg = this.readPkg();
723
+ const found = new Set;
724
+ for (const section of REWRITE_SECTIONS) {
725
+ for (const name of Object.keys(pkg[section] ?? {}))
726
+ if (name.startsWith(INTERNAL_PREFIX))
727
+ found.add(name);
728
+ }
729
+ return [...found];
730
+ }
731
+ hasLocalRefs() {
732
+ const pkg = this.readPkg();
733
+ return DEP_SECTIONS.some((s) => Object.values(pkg[s] ?? {}).some((v) => LOCAL_REF.test(v)));
734
+ }
735
+ inAncestorWorkspace() {
736
+ let dir = dirname2(this.root);
737
+ for (let i = 0;i < 5; i++) {
738
+ const pj = join4(dir, "package.json");
739
+ if (existsSync4(pj)) {
740
+ try {
741
+ const parent = JSON.parse(readFileSync4(pj, "utf8"));
742
+ const ws = Array.isArray(parent.workspaces) ? parent.workspaces : parent.workspaces?.packages ?? [];
743
+ if (ws.length > 0) {
744
+ const rel = relative2(dir, this.root).replace(/\\/g, "/");
745
+ return ws.some((g) => matchSimpleGlob(g, rel));
746
+ }
747
+ } catch {}
748
+ }
749
+ const parent = dirname2(dir);
750
+ if (parent === dir)
751
+ break;
752
+ dir = parent;
753
+ }
754
+ return false;
755
+ }
756
+ kind() {
757
+ const t = this.ctx.config.type;
758
+ const vite = existsSync4(join4(this.root, "vite.config.ts")) || existsSync4(join4(this.root, "vite.config.js"));
759
+ if (t === "app" || t === "website")
760
+ return "app-shell";
761
+ if (t === "mfe" || t === "fe-libs")
762
+ return this.inAncestorWorkspace() ? "mfe" : "app-shell";
763
+ if (t !== undefined)
764
+ return "backend";
765
+ if (vite)
766
+ return "app-shell";
767
+ return this.inAncestorWorkspace() ? "mfe" : "backend";
768
+ }
769
+ resolveLocalPath(name) {
770
+ const configured = this.ctx.config.depsMode?.paths?.[name] ?? this.legacyPathMap()[name];
771
+ if (configured !== undefined)
772
+ return configured;
773
+ let dir = dirname2(this.root);
774
+ for (let i = 0;i < 5; i++) {
775
+ for (const rel of conventionalDirs(name)) {
776
+ const candidate = join4(dir, rel);
777
+ const pj = join4(candidate, "package.json");
778
+ if (!existsSync4(pj))
779
+ continue;
780
+ try {
781
+ if (JSON.parse(readFileSync4(pj, "utf8")).name === name)
782
+ return relative2(this.root, candidate);
783
+ } catch {}
784
+ }
785
+ const parent = dirname2(dir);
786
+ if (parent === dir)
787
+ break;
788
+ dir = parent;
789
+ }
790
+ return null;
791
+ }
792
+ legacyPathMap() {
793
+ const p = join4(this.root, "deps-local-paths.json");
794
+ if (!existsSync4(p))
795
+ return {};
796
+ try {
797
+ const raw = JSON.parse(readFileSync4(p, "utf8"));
798
+ if (typeof raw !== "object" || raw === null)
799
+ return {};
800
+ return Object.fromEntries(Object.entries(raw).filter((e) => typeof e[1] === "string"));
801
+ } catch {
802
+ this.log(" Warning: Could not read deps-local-paths.json");
803
+ return {};
804
+ }
805
+ }
806
+ restoreDepsFromGit() {
807
+ const committedText = showHead(this.root, "package.json");
808
+ if (committedText === null)
809
+ return -1;
810
+ const committed = JSON.parse(committedText);
811
+ const pkg = this.readPkg();
812
+ let count = 0;
813
+ for (const section of DEP_SECTIONS) {
814
+ const deps = pkg[section];
815
+ if (deps === undefined)
816
+ continue;
817
+ for (const name of Object.keys(deps)) {
818
+ if (!name.startsWith(INTERNAL_PREFIX))
819
+ continue;
820
+ const value = committed[section]?.[name];
821
+ if (value !== undefined && deps[name] !== value) {
822
+ deps[name] = value;
823
+ this.log(` ${section}.${name} \u2192 ${value}`);
824
+ count++;
825
+ }
826
+ }
827
+ }
828
+ if (count > 0)
829
+ this.writePkg(pkg);
830
+ return count;
831
+ }
832
+ async restoreDepsFromPublished(fetchImpl = this.fetchImpl) {
833
+ const pkg = this.readPkg();
834
+ let count = 0;
835
+ for (const section of DEP_SECTIONS) {
836
+ const deps = pkg[section];
837
+ if (deps === undefined)
838
+ continue;
839
+ for (const name of Object.keys(deps)) {
840
+ if (!name.startsWith(INTERNAL_PREFIX) || !LOCAL_REF.test(deps[name] ?? ""))
841
+ continue;
842
+ let version;
843
+ const installed = join4(this.root, "node_modules", ...name.split("/"), "package.json");
844
+ if (existsSync4(installed)) {
845
+ try {
846
+ const v = JSON.parse(readFileSync4(installed, "utf8")).version;
847
+ if (v !== undefined && v !== "0.0.0-dev")
848
+ version = v;
849
+ } catch {}
850
+ }
851
+ if (version === undefined) {
852
+ try {
853
+ const token = process.env.NPM_TOKEN;
854
+ const res = await fetchImpl(`https://registry.npmjs.org/${name.replace("/", "%2f")}/latest`, {
855
+ headers: token === undefined || token === "" ? {} : { authorization: `Bearer ${token}` }
856
+ });
857
+ if (res.ok) {
858
+ const body = await res.json();
859
+ const v = typeof body === "object" && body !== null ? Reflect.get(body, "version") : undefined;
860
+ if (typeof v === "string")
861
+ version = v;
862
+ }
863
+ } catch {}
864
+ }
865
+ if (version === undefined)
866
+ continue;
867
+ deps[name] = `^${version}`;
868
+ this.log(` ${section}.${name} \u2192 ^${version}`);
869
+ count++;
870
+ }
871
+ }
872
+ if (count > 0)
873
+ this.writePkg(pkg);
874
+ return count;
875
+ }
876
+ restoreLockFromGit() {
877
+ return capture(["git", "checkout", "--", "bun.lock"], { cwd: this.root }).code === 0;
878
+ }
879
+ cleanViteCache() {
880
+ const p = join4(this.root, "node_modules", ".vite");
881
+ if (existsSync4(p)) {
882
+ rmSync2(p, { recursive: true, force: true });
883
+ this.log(" Cleaned Vite cache (node_modules/.vite).");
884
+ }
885
+ }
886
+ status() {
887
+ const mode = this.mode();
888
+ const refs = this.hasLocalRefs();
889
+ this.log("");
890
+ this.log(` Dependency mode: ${mode === "workspace" ? "local (workspace/file refs)" : "remote (registry packages)"}`);
891
+ this.log(` Repo type: ${this.kind()}`);
892
+ this.log(` Internal deps: ${this.internalPackages().join(", ") || "none"}`);
893
+ this.log(` package.json: ${refs ? "has local refs" : "registry-only"}`);
894
+ if (mode === "workspace" && !refs)
895
+ this.log(`
896
+ Mode is 'local' but package.json has registry versions.`);
897
+ if (mode === "npm" && refs)
898
+ this.log(`
899
+ Mode is 'remote' but package.json has local references! Run 'boff deps-mode remote'.`);
900
+ this.log("");
901
+ return 0;
902
+ }
903
+ async remote() {
904
+ this.log(`
905
+ Switching to remote mode...
906
+ `);
907
+ const restored = this.restoreDepsFromGit();
908
+ if (restored < 0 || this.hasLocalRefs()) {
909
+ const published = await this.restoreDepsFromPublished();
910
+ if (this.hasLocalRefs()) {
911
+ this.error(" Error: could not restore registry versions from git or the registry. Fix package.json manually.");
912
+ return 1;
913
+ }
914
+ if (published > 0)
915
+ this.log(` Restored ${published} @burdenoff/* dep(s) to published versions.`);
916
+ } else if (restored > 0) {
917
+ this.log(` Restored ${restored} @burdenoff/* dep(s) to registry versions.`);
918
+ } else {
919
+ this.log(" package.json already has registry-only deps.");
920
+ }
921
+ if (existsSync4(this.lockPath) && this.restoreLockFromGit())
922
+ this.log(" Restored committed bun.lock.");
923
+ if (this.kind() === "app-shell")
924
+ this.cleanViteCache();
925
+ this.setMode("npm");
926
+ this.log(` Mode set to: remote
927
+ `);
928
+ return 0;
929
+ }
930
+ async local(skipInstall) {
931
+ this.log(`
932
+ Switching to local mode...
933
+ `);
934
+ const kind = this.kind();
935
+ const internal = this.internalPackages();
936
+ if (internal.length === 0) {
937
+ this.error(" Error: No @burdenoff/* packages found in dependencies.");
938
+ return 1;
939
+ }
940
+ if (this.hasLocalRefs() && this.restoreDepsFromGit() > 0)
941
+ this.log(" Restored canonical dep values from git first.");
942
+ const pkg = this.readPkg();
943
+ let count = 0;
944
+ for (const section of REWRITE_SECTIONS) {
945
+ const deps = pkg[section];
946
+ if (deps === undefined)
947
+ continue;
948
+ for (const name of Object.keys(deps)) {
949
+ if (!name.startsWith(INTERNAL_PREFIX))
950
+ continue;
951
+ let next;
952
+ if (kind === "mfe") {
953
+ next = "workspace:*";
954
+ } else {
955
+ const path = this.resolveLocalPath(name);
956
+ if (path === null || !existsSync4(join4(resolve(this.root, path), "package.json"))) {
957
+ this.log(` Warning: Could not find local path for ${name}. Skipping.`);
958
+ continue;
959
+ }
960
+ next = kind === "app-shell" ? `file:${path}` : path;
961
+ }
962
+ if (deps[name] !== next) {
963
+ this.log(` ${section}.${name}: ${deps[name] ?? ""} \u2192 ${next}`);
964
+ deps[name] = next;
965
+ count++;
966
+ }
967
+ }
968
+ }
969
+ if (count === 0) {
970
+ this.log(" No packages to rewrite. Nothing changed.");
971
+ return 0;
972
+ }
973
+ this.writePkg(pkg);
974
+ if (existsSync4(this.lockWorkspacePath)) {
975
+ copyFileSync(this.lockWorkspacePath, this.lockPath);
976
+ this.log(" Restored cached workspace lockfile.");
977
+ }
978
+ if (kind === "app-shell")
979
+ this.cleanViteCache();
980
+ this.setMode("workspace");
981
+ if (skipInstall) {
982
+ this.log(" Skipped bun install (--skip-install flag).");
983
+ return 0;
984
+ }
985
+ this.log(`
986
+ Running bun install...
987
+ `);
988
+ const res = Bun.spawnSync(["bun", "install"], {
989
+ cwd: this.root,
990
+ stdout: this.silent ? "ignore" : "inherit",
991
+ stderr: "inherit"
992
+ });
993
+ if (res.exitCode !== 0) {
994
+ this.error(`
995
+ bun install failed. Restoring remote mode...`);
996
+ await this.remote();
997
+ return 1;
998
+ }
999
+ if (existsSync4(this.lockPath))
1000
+ copyFileSync(this.lockPath, this.lockWorkspacePath);
1001
+ if (kind !== "app-shell" && this.restoreLockFromGit())
1002
+ this.log(" Restored committed bun.lock (git stays clean).");
1003
+ this.log(`
1004
+ Mode set to: local
1005
+ `);
1006
+ return 0;
1007
+ }
1008
+ check() {
1009
+ let errors = 0;
1010
+ const pkg = this.readPkg();
1011
+ for (const section of DEP_SECTIONS) {
1012
+ for (const [name, value] of Object.entries(pkg[section] ?? {})) {
1013
+ if (LOCAL_REF.test(value)) {
1014
+ this.error(` \u2717 ${section}.${name} = "${value}" (local ref)`);
1015
+ errors++;
1016
+ }
1017
+ }
1018
+ }
1019
+ if (existsSync4(this.lockPath)) {
1020
+ const lock = readFileSync4(this.lockPath, "utf8");
1021
+ if (/"workspace:/.test(lock)) {
1022
+ this.error(" \u2717 bun.lock contains workspace: references");
1023
+ errors++;
1024
+ }
1025
+ if (/"file:/.test(lock)) {
1026
+ this.error(" \u2717 bun.lock contains file: references");
1027
+ errors++;
1028
+ }
1029
+ }
1030
+ if (errors > 0) {
1031
+ this.error(`
1032
+ \u2717 Found ${errors} dependency validation error(s). Run 'boff deps-mode remote'.
1033
+ `);
1034
+ return 1;
1035
+ }
1036
+ this.log(` \u2713 All dependency references are registry-only.
1037
+ `);
1038
+ return 0;
1039
+ }
1040
+ async guardPreCommit() {
1041
+ if (!this.hasLocalRefs())
1042
+ return 0;
1043
+ this.w.out("deps-guard: package.json has local refs \u2014 restoring registry deps for this commit");
1044
+ this.writeState({ ...this.readState(), _restore_workspace: "true" });
1045
+ const quiet = new DepsMode(this.root, { writer: this.w, silent: true, fetchImpl: this.fetchImpl });
1046
+ const rc = await quiet.remote();
1047
+ if (rc !== 0)
1048
+ return rc;
1049
+ return capture(["git", "add", "package.json"], { cwd: this.root }).code === 0 ? 0 : 1;
1050
+ }
1051
+ guardPostCommit(cliPath, extraArgs = []) {
1052
+ const state = this.readState();
1053
+ if (state._restore_workspace !== "true")
1054
+ return 0;
1055
+ const rest = { ...state };
1056
+ delete rest._restore_workspace;
1057
+ this.writeState(rest);
1058
+ this.w.out("deps-guard: restoring local mode in the background");
1059
+ const child = Bun.spawn([
1060
+ "sh",
1061
+ "-c",
1062
+ 'nohup "$@" >/dev/null 2>&1 &',
1063
+ "sh",
1064
+ process.execPath,
1065
+ cliPath,
1066
+ "deps-mode",
1067
+ "local",
1068
+ "--silent",
1069
+ ...extraArgs
1070
+ ], {
1071
+ cwd: this.root,
1072
+ stdin: "ignore",
1073
+ stdout: "ignore",
1074
+ stderr: "ignore"
1075
+ });
1076
+ child.unref();
1077
+ return 0;
1078
+ }
1079
+ }
1080
+ function matchSimpleGlob(glob, path) {
1081
+ const g = glob.replace(/\\/g, "/");
1082
+ const p = path.replace(/\\/g, "/");
1083
+ if (g === p)
1084
+ return true;
1085
+ if (g.endsWith("/*")) {
1086
+ const prefix = g.slice(0, -2);
1087
+ return p.startsWith(`${prefix}/`) && !p.slice(prefix.length + 1).includes("/");
1088
+ }
1089
+ if (g.endsWith("/**"))
1090
+ return p.startsWith(`${g.slice(0, -3)}/`);
1091
+ return false;
1092
+ }
1093
+ function cliPath() {
1094
+ const src = join4(PACKAGE_ROOT, "src", "cli.ts");
1095
+ return existsSync4(src) ? src : join4(PACKAGE_ROOT, "dist", "cli.js");
1096
+ }
1097
+ var HELP2 = `usage: boff deps-mode <status|remote|local|check|guard pre-commit|guard post-commit> [--silent] [--skip-install]`;
1098
+ async function depsModeCommand(root, argv, w = consoleWriter) {
1099
+ const args = parseArgs(argv);
1100
+ const [command, sub] = args.positionals;
1101
+ if (!existsSync4(join4(root, "package.json"))) {
1102
+ w.err("boff deps-mode: no package.json in this directory");
1103
+ return 2;
1104
+ }
1105
+ const dm = new DepsMode(root, { writer: w, silent: flagBool(args, "silent") });
1106
+ switch (command) {
1107
+ case "status":
1108
+ return dm.status();
1109
+ case "remote":
1110
+ case "npm":
1111
+ return dm.remote();
1112
+ case "local":
1113
+ case "workspace":
1114
+ return dm.local(flagBool(args, "skip-install"));
1115
+ case "check":
1116
+ return dm.check();
1117
+ case "guard":
1118
+ if (sub === "pre-commit")
1119
+ return dm.guardPreCommit();
1120
+ if (sub === "post-commit")
1121
+ return dm.guardPostCommit(cliPath(), flagBool(args, "skip-install") ? ["--skip-install"] : []);
1122
+ w.err(HELP2);
1123
+ return 2;
1124
+ case "help":
1125
+ w.out(HELP2);
1126
+ return 0;
1127
+ default:
1128
+ w.err(HELP2);
1129
+ return 2;
1130
+ }
1131
+ }
1132
+
1133
+ // src/doctor.ts
1134
+ import { existsSync as existsSync8, readFileSync as readFileSync10 } from "fs";
1135
+ import { join as join11 } from "path";
1136
+
1137
+ // src/lib/files.ts
1138
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
1139
+ import { join as join5 } from "path";
1140
+ function scan(root, pattern) {
1141
+ if (!existsSync5(root))
1142
+ return [];
1143
+ return [...new Bun.Glob(pattern).scanSync({ cwd: root, dot: true, onlyFiles: true })].filter((p) => !p.split("/").includes("node_modules")).sort();
1144
+ }
1145
+ function read(root, rel) {
1146
+ return { path: rel, text: readFileSync5(join5(root, rel), "utf8") };
1147
+ }
1148
+ function workflowFiles(root) {
1149
+ return scan(root, ".github/workflows/*.{yml,yaml}").map((p) => read(root, p));
1150
+ }
1151
+ function actionFiles(root) {
1152
+ return scan(root, ".github/actions/**/action.{yml,yaml}").map((p) => read(root, p));
1153
+ }
1154
+ function dockerFiles(root) {
1155
+ const found = new Set([
1156
+ ...scan(root, "**/Dockerfile"),
1157
+ ...scan(root, "**/Dockerfile.*"),
1158
+ ...scan(root, "**/*.Dockerfile")
1159
+ ]);
1160
+ return [...found].filter((p) => !p.endsWith(".dockerignore")).sort().map((p) => read(root, p));
1161
+ }
1162
+
1163
+ // src/lib/hash.ts
1164
+ import { readFileSync as readFileSync6 } from "fs";
1165
+ import { join as join6 } from "path";
1166
+ var HASH_EXCLUDES = [
1167
+ "node_modules/**",
1168
+ "**/node_modules/**",
1169
+ ".git/**",
1170
+ "coverage/**",
1171
+ "dist/**",
1172
+ "sanity.log",
1173
+ "junit.xml",
1174
+ ".eslintcache"
1175
+ ];
1176
+ function sha256(...parts) {
1177
+ const h = new Bun.CryptoHasher("sha256");
1178
+ for (const p of parts) {
1179
+ h.update(p);
1180
+ h.update("\x00");
1181
+ }
1182
+ return h.digest("hex");
1183
+ }
1184
+ function repoFiles(root) {
1185
+ const fromGit = listRepoFiles(root);
1186
+ const files = fromGit ?? [...new Bun.Glob("**/*").scanSync({ cwd: root, dot: true, onlyFiles: true })].map((f) => f.replace(/\\/g, "/"));
1187
+ const excluded = HASH_EXCLUDES.map((g) => new Bun.Glob(g));
1188
+ return files.filter((f) => !excluded.some((g) => g.match(f))).sort();
1189
+ }
1190
+
1191
+ class FileHasher {
1192
+ root;
1193
+ cache = new Map;
1194
+ constructor(root) {
1195
+ this.root = root;
1196
+ }
1197
+ fileHash(rel) {
1198
+ const hit = this.cache.get(rel);
1199
+ if (hit !== undefined)
1200
+ return hit;
1201
+ let digest;
1202
+ try {
1203
+ const h = new Bun.CryptoHasher("sha256");
1204
+ h.update(readFileSync6(join6(this.root, rel)));
1205
+ digest = h.digest("hex");
1206
+ } catch {
1207
+ digest = "missing";
1208
+ }
1209
+ this.cache.set(rel, digest);
1210
+ return digest;
1211
+ }
1212
+ combined(files) {
1213
+ const h = new Bun.CryptoHasher("sha256");
1214
+ for (const f of files) {
1215
+ h.update(f);
1216
+ h.update("\x00");
1217
+ h.update(this.fileHash(f));
1218
+ h.update(`
1219
+ `);
1220
+ }
1221
+ return h.digest("hex");
1222
+ }
1223
+ }
1224
+ function matchGlobs(files, globs) {
1225
+ const compiled = globs.map((g) => new Bun.Glob(g));
1226
+ return files.filter((f) => compiled.some((g) => g.match(f)));
1227
+ }
1228
+
1229
+ // src/lib/jsonc.ts
1230
+ function stripJsonComments(text) {
1231
+ let out = "";
1232
+ let inString = false;
1233
+ for (let i = 0;i < text.length; i++) {
1234
+ const ch = text[i] ?? "";
1235
+ const next = text[i + 1] ?? "";
1236
+ if (inString) {
1237
+ out += ch;
1238
+ if (ch === "\\") {
1239
+ out += next;
1240
+ i++;
1241
+ } else if (ch === '"') {
1242
+ inString = false;
1243
+ }
1244
+ continue;
1245
+ }
1246
+ if (ch === '"') {
1247
+ inString = true;
1248
+ out += ch;
1249
+ continue;
1250
+ }
1251
+ if (ch === "/" && next === "/") {
1252
+ while (i < text.length && text[i] !== `
1253
+ `)
1254
+ i++;
1255
+ out += `
1256
+ `;
1257
+ continue;
1258
+ }
1259
+ if (ch === "/" && next === "*") {
1260
+ i += 2;
1261
+ while (i < text.length && !(text[i] === "*" && text[i + 1] === "/"))
1262
+ i++;
1263
+ i++;
1264
+ continue;
1265
+ }
1266
+ if (ch === ",") {
1267
+ let j = i + 1;
1268
+ while (j < text.length && /\s/.test(text[j] ?? ""))
1269
+ j++;
1270
+ if (text[j] === "}" || text[j] === "]")
1271
+ continue;
1272
+ }
1273
+ out += ch;
1274
+ }
1275
+ return out;
1276
+ }
1277
+ function parseJsonc(text) {
1278
+ return JSON.parse(stripJsonComments(text));
1279
+ }
1280
+
1281
+ // src/sync.ts
1282
+ import { existsSync as existsSync7 } from "fs";
1283
+ import { join as join10 } from "path";
1284
+
1285
+ // src/lib/bunfig.ts
1286
+ import { readFileSync as readFileSync8 } from "fs";
1287
+ import { join as join8 } from "path";
1288
+
1289
+ // src/lib/managed.ts
1290
+ function findRegions(lines) {
1291
+ const regions = [];
1292
+ let open = -1;
1293
+ lines.forEach((line, i) => {
1294
+ const t = line.trim();
1295
+ if (t === MANAGED_START) {
1296
+ if (open >= 0)
1297
+ throw new Error(`nested "${MANAGED_START}" at line ${i + 1}`);
1298
+ open = i;
1299
+ } else if (t === MANAGED_END) {
1300
+ if (open < 0)
1301
+ throw new Error(`"${MANAGED_END}" without a start marker at line ${i + 1}`);
1302
+ regions.push({ start: open, end: i });
1303
+ open = -1;
1304
+ }
1305
+ });
1306
+ if (open >= 0)
1307
+ throw new Error(`"${MANAGED_START}" at line ${open + 1} has no "${MANAGED_END}"`);
1308
+ return regions;
1309
+ }
1310
+ var TABLE_HEADER = /^\s*\[\s*([^[\]]+?)\s*\]\s*(?:#.*)?$/;
1311
+ function tableHeader(line) {
1312
+ if (/^\s*\[\[/.test(line))
1313
+ return null;
1314
+ const m = TABLE_HEADER.exec(line);
1315
+ if (m === null)
1316
+ return null;
1317
+ return (m[1] ?? "").split(".").map((part) => part.trim().replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1")).join(".");
1318
+ }
1319
+ function stripRegions(lines) {
1320
+ const regions = findRegions(lines);
1321
+ if (regions.length === 0)
1322
+ return [...lines];
1323
+ const drop = new Set;
1324
+ for (const r of regions) {
1325
+ let hasHeader = false;
1326
+ for (let i = r.start;i <= r.end; i++) {
1327
+ drop.add(i);
1328
+ if (tableHeader(lines[i] ?? "") !== null || /^[A-Za-z0-9_-]+:\s*(#.*)?$/.test(lines[i] ?? ""))
1329
+ hasHeader = true;
1330
+ }
1331
+ if (hasHeader && r.end + 1 < lines.length && (lines[r.end + 1] ?? "").trim() === "")
1332
+ drop.add(r.end + 1);
1333
+ }
1334
+ return lines.filter((_, i) => !drop.has(i));
1335
+ }
1336
+ function trimEnd(lines) {
1337
+ const out = [...lines];
1338
+ while (out.length > 0 && (out[out.length - 1] ?? "").trim() === "")
1339
+ out.pop();
1340
+ return out;
1341
+ }
1342
+ function splitLines(text) {
1343
+ const lines = text.replace(/\r\n/g, `
1344
+ `).split(`
1345
+ `);
1346
+ if (lines.length > 0 && lines[lines.length - 1] === "")
1347
+ lines.pop();
1348
+ return lines;
1349
+ }
1350
+ function joinLines(lines) {
1351
+ return lines.length === 0 ? "" : `${lines.join(`
1352
+ `)}
1353
+ `;
1354
+ }
1355
+ function wrapRegion(body) {
1356
+ return [MANAGED_START, ...body, MANAGED_END];
1357
+ }
1358
+
1359
+ // src/lib/excludes.ts
1360
+ import { readFileSync as readFileSync7 } from "fs";
1361
+ import { join as join7 } from "path";
1362
+ var FLEET_SCOPES = ["@burdenoff", "@fluidgrids", "@vibecontrols"];
1363
+ var REGISTRY = "https://registry.npmjs.org";
1364
+ var NAME_RE = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/;
1365
+ function isPackageName(v) {
1366
+ return typeof v === "string" && NAME_RE.test(v);
1367
+ }
1368
+ function sortedUnique(names) {
1369
+ return [...new Set(names)].sort();
1370
+ }
1371
+ function shippedExcludesPath() {
1372
+ return join7(PACKAGE_ROOT, "bunfig", "release-age-excludes.json");
1373
+ }
1374
+ function readExcludesFile(path = shippedExcludesPath()) {
1375
+ const raw = JSON.parse(readFileSync7(path, "utf8"));
1376
+ if (typeof raw !== "object" || raw === null || !("packages" in raw) || !Array.isArray(raw.packages)) {
1377
+ throw new Error(`${path}: expected an object with a "packages" array`);
1378
+ }
1379
+ const packages = raw.packages.filter(isPackageName);
1380
+ if (packages.length !== raw.packages.length)
1381
+ throw new Error(`${path}: "packages" holds an invalid package name`);
1382
+ const generatedAt = "generatedAt" in raw && typeof raw.generatedAt === "string" ? raw.generatedAt : "";
1383
+ const scopes = "scopes" in raw && Array.isArray(raw.scopes) ? raw.scopes.filter((s) => typeof s === "string") : [];
1384
+ return { generatedAt, scopes, packages: sortedUnique(packages) };
1385
+ }
1386
+ function shippedExcludes() {
1387
+ return sortedUnique([...readExcludesFile().packages, TOOLCHAIN_NAME]);
1388
+ }
1389
+ async function fetchScopePackages(scope, token, fetchImpl = fetch) {
1390
+ const org = scope.replace(/^@/, "");
1391
+ const headers = { accept: "application/json" };
1392
+ if (token !== undefined && token !== "")
1393
+ headers.authorization = `Bearer ${token}`;
1394
+ const res = await fetchImpl(`${REGISTRY}/-/org/${encodeURIComponent(org)}/package`, { headers });
1395
+ if (!res.ok)
1396
+ throw new Error(`registry returned ${res.status} for ${scope} packages`);
1397
+ const body = await res.json();
1398
+ if (typeof body !== "object" || body === null)
1399
+ throw new Error(`unexpected registry response for ${scope}`);
1400
+ return Object.keys(body).filter((name) => isPackageName(name) && name.startsWith(`${scope}/`));
1401
+ }
1402
+ async function fetchFleetExcludes(token, fetchImpl = fetch, base = shippedExcludes()) {
1403
+ const all = new Set(base);
1404
+ for (const scope of FLEET_SCOPES) {
1405
+ for (const name of await fetchScopePackages(scope, token, fetchImpl))
1406
+ all.add(name);
1407
+ }
1408
+ all.add(TOOLCHAIN_NAME);
1409
+ return sortedUnique(all);
1410
+ }
1411
+
1412
+ // src/lib/bunfig.ts
1413
+ var EXCLUDES_PLACEHOLDER = "{{RELEASE_AGE_EXCLUDES}}";
1414
+ function parseTemplate(text) {
1415
+ const tables = [];
1416
+ let current = null;
1417
+ for (const raw of splitLines(text)) {
1418
+ const line = raw.trim();
1419
+ if (line === "" || line.startsWith("#"))
1420
+ continue;
1421
+ const header = tableHeader(line);
1422
+ if (header !== null) {
1423
+ current = { table: header, keys: [] };
1424
+ tables.push(current);
1425
+ continue;
1426
+ }
1427
+ const m = /^([A-Za-z0-9_-]+)\s*=\s*(.+)$/.exec(line);
1428
+ if (m === null || current === null)
1429
+ throw new Error(`fragment template: cannot parse line "${raw}"`);
1430
+ current.keys.push({ name: m[1] ?? "", value: m[2] ?? "" });
1431
+ }
1432
+ return tables;
1433
+ }
1434
+ function readTemplate() {
1435
+ return parseTemplate(readFileSync8(join8(PACKAGE_ROOT, "bunfig", "fragment.toml.tmpl"), "utf8"));
1436
+ }
1437
+ function renderExcludes(names) {
1438
+ if (names.length === 0)
1439
+ return ["[]"];
1440
+ return ["[", ...names.map((n) => ` ${JSON.stringify(n)},`), "]"];
1441
+ }
1442
+ function bracketDelta(text) {
1443
+ let depth = 0;
1444
+ let quote = null;
1445
+ for (let i = 0;i < text.length; i++) {
1446
+ const ch = text[i];
1447
+ if (quote !== null) {
1448
+ if (ch === "\\" && quote === '"')
1449
+ i++;
1450
+ else if (ch === quote)
1451
+ quote = null;
1452
+ continue;
1453
+ }
1454
+ if (ch === "#")
1455
+ break;
1456
+ if (ch === '"' || ch === "'")
1457
+ quote = ch;
1458
+ else if (ch === "[" || ch === "{")
1459
+ depth++;
1460
+ else if (ch === "]" || ch === "}")
1461
+ depth--;
1462
+ }
1463
+ return depth;
1464
+ }
1465
+ function headerIndex(lines, table) {
1466
+ return lines.findIndex((l) => tableHeader(l) === table);
1467
+ }
1468
+ function removeKey(lines, table, name) {
1469
+ const h = headerIndex(lines, table);
1470
+ if (h < 0)
1471
+ return false;
1472
+ const keyRe = new RegExp(`^\\s*(?:${name}|"${name}"|'${name}')\\s*=(.*)$`);
1473
+ for (let i = h + 1;i < lines.length && tableHeader(lines[i] ?? "") === null; i++) {
1474
+ const m = keyRe.exec(lines[i] ?? "");
1475
+ if (m === null)
1476
+ continue;
1477
+ let depth = bracketDelta(m[1] ?? "");
1478
+ let end = i;
1479
+ while (depth > 0 && end + 1 < lines.length) {
1480
+ end++;
1481
+ depth += bracketDelta(lines[end] ?? "");
1482
+ }
1483
+ lines.splice(i, end - i + 1);
1484
+ return true;
1485
+ }
1486
+ return false;
1487
+ }
1488
+ function asTable(v) {
1489
+ return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
1490
+ }
1491
+ function existingExcludes(parsed) {
1492
+ const install = asTable(asTable(parsed)?.install);
1493
+ const list = install?.minimumReleaseAgeExcludes;
1494
+ return Array.isArray(list) ? list.filter(isPackageName) : [];
1495
+ }
1496
+ function renderBunfig(original, template, excludes) {
1497
+ const parsedOriginal = original.trim() === "" ? {} : Bun.TOML.parse(original);
1498
+ const allExcludes = sortedUnique([...excludes, ...existingExcludes(parsedOriginal)]);
1499
+ let lines = stripRegions(splitLines(original));
1500
+ const replacedKeys = [];
1501
+ for (const t of template) {
1502
+ for (const k of t.keys) {
1503
+ while (removeKey(lines, t.table, k.name))
1504
+ replacedKeys.push(`[${t.table}] ${k.name}`);
1505
+ }
1506
+ }
1507
+ for (const t of template) {
1508
+ const body = [];
1509
+ for (const k of t.keys) {
1510
+ if (k.value === EXCLUDES_PLACEHOLDER) {
1511
+ const rendered = renderExcludes(allExcludes);
1512
+ body.push(`${k.name} = ${rendered[0] ?? "[]"}`, ...rendered.slice(1));
1513
+ } else {
1514
+ body.push(`${k.name} = ${k.value}`);
1515
+ }
1516
+ }
1517
+ const h = headerIndex(lines, t.table);
1518
+ if (h >= 0) {
1519
+ lines.splice(h + 1, 0, ...wrapRegion(body));
1520
+ continue;
1521
+ }
1522
+ const sub = lines.findIndex((l) => (tableHeader(l) ?? "").startsWith(`${t.table}.`));
1523
+ const region = wrapRegion([`[${t.table}]`, ...body]);
1524
+ if (sub >= 0) {
1525
+ lines.splice(sub, 0, ...region, "");
1526
+ } else {
1527
+ lines = trimEnd(lines);
1528
+ if (lines.length > 0)
1529
+ lines.push("");
1530
+ lines.push(...region);
1531
+ }
1532
+ }
1533
+ const text = joinLines(lines);
1534
+ validateBunfig(text, template, allExcludes);
1535
+ return { text, replacedKeys };
1536
+ }
1537
+ function validateBunfig(text, template, excludes) {
1538
+ const parsed = asTable(Bun.TOML.parse(text));
1539
+ for (const t of template) {
1540
+ let node = parsed;
1541
+ for (const part of t.table.split("."))
1542
+ node = asTable(node?.[part]);
1543
+ if (node === undefined)
1544
+ throw new Error(`rendered bunfig.toml has no [${t.table}] table`);
1545
+ for (const k of t.keys) {
1546
+ const actual = node[k.name];
1547
+ const expected = k.value === EXCLUDES_PLACEHOLDER ? [...excludes] : asTable(Bun.TOML.parse(`v = ${k.value}`))?.v;
1548
+ if (JSON.stringify(actual) !== JSON.stringify(expected)) {
1549
+ throw new Error(`rendered bunfig.toml: [${t.table}] ${k.name} does not read back as written`);
1550
+ }
1551
+ }
1552
+ }
1553
+ }
1554
+
1555
+ // src/lib/lefthook.ts
1556
+ var PRE_PUSH_COMMAND = "./node_modules/.bin/boff sanity --pre-push";
1557
+ var PRE_PUSH_BODY = ["pre-push:", " commands:", " sanity:", ` run: ${PRE_PUSH_COMMAND}`];
1558
+ function removeTopLevelKey(lines, key) {
1559
+ const start = lines.findIndex((l) => new RegExp(`^${key}:\\s*(#.*)?$`).test(l));
1560
+ if (start < 0)
1561
+ return [];
1562
+ let end = start + 1;
1563
+ while (end < lines.length && !/^[^\s#]/.test(lines[end] ?? ""))
1564
+ end++;
1565
+ while (end - 1 > start && /^(#.*)?\s*$/.test(lines[end - 1] ?? ""))
1566
+ end--;
1567
+ return lines.splice(start, end - start);
1568
+ }
1569
+ function renderLefthook(original) {
1570
+ let lines = stripRegions(splitLines(original));
1571
+ const replaced = removeTopLevelKey(lines, "pre-push");
1572
+ lines = trimEnd(lines);
1573
+ if (lines.length === 0) {
1574
+ lines.push("# Git hooks (https://lefthook.dev). The pre-push block is managed by `boff sync`.");
1575
+ }
1576
+ lines.push("", ...wrapRegion(PRE_PUSH_BODY));
1577
+ const text = joinLines(lines);
1578
+ validateLefthook(text);
1579
+ return { text, replaced };
1580
+ }
1581
+ function yamlApi() {
1582
+ const candidate = Reflect.get(Bun, "YAML");
1583
+ if (typeof candidate === "object" && candidate !== null && typeof Reflect.get(candidate, "parse") === "function") {
1584
+ return { parse: (text) => Reflect.apply(Reflect.get(candidate, "parse"), candidate, [text]) };
1585
+ }
1586
+ return;
1587
+ }
1588
+ function validateLefthook(text) {
1589
+ const yaml = yamlApi();
1590
+ if (yaml === undefined)
1591
+ return;
1592
+ const doc = yaml.parse(text);
1593
+ const run = Reflect.get(Reflect.get(Reflect.get(Reflect.get(Object(doc), "pre-push") ?? {}, "commands") ?? {}, "sanity") ?? {}, "run");
1594
+ if (run !== PRE_PUSH_COMMAND)
1595
+ throw new Error("rendered lefthook.yml does not run boff sanity on pre-push");
1596
+ }
1597
+
1598
+ // src/lib/scripts.ts
1599
+ import { existsSync as existsSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync2 } from "fs";
1600
+ import { join as join9 } from "path";
1601
+ var BUILD_REFERENCES = ["mfe", "fe-libs", "website"];
1602
+ var FOREIGN_TEST_RUNNER = ["vscode-ext", "docs"];
1603
+ function shellQuote(arg) {
1604
+ return /^[A-Za-z0-9_./*@:=-]+$/.test(arg) ? arg : `'${arg.replace(/'/g, `'\\''`)}'`;
1605
+ }
1606
+ function typeCheckCommand(ctx) {
1607
+ const parts = ["boff gate -- bun check --no-pretty"];
1608
+ if (BUILD_REFERENCES.includes(ctx.type))
1609
+ parts.push("-b");
1610
+ if (ctx.type === "app" && existsSync6(join9(ctx.root, "tsconfig.typecheck.json"))) {
1611
+ parts.push("-p tsconfig.typecheck.json");
1612
+ }
1613
+ return parts.join(" ");
1614
+ }
1615
+ function managedScripts(ctx) {
1616
+ const formatPaths = (ctx.config.sanity?.formatPaths ?? []).map(shellQuote).join(" ");
1617
+ const fmt = (check) => ["bun fmt", check ? "--check" : "", formatPaths].filter((p) => p !== "").join(" ");
1618
+ const scripts = {};
1619
+ if (ctx.type !== "docs") {
1620
+ scripts["type:check"] = typeCheckCommand(ctx);
1621
+ scripts.format = fmt(false);
1622
+ scripts["format:check"] = fmt(true);
1623
+ scripts.lint = "eslint .";
1624
+ }
1625
+ if (!FOREIGN_TEST_RUNNER.includes(ctx.type))
1626
+ scripts.test = "bun test";
1627
+ scripts.sanity = "boff sanity";
1628
+ scripts["sanity:force"] = "boff sanity --force";
1629
+ scripts.audit = "bun audit --audit-level=high";
1630
+ for (const key of ctx.config.scripts?.unmanaged ?? [])
1631
+ delete scripts[key];
1632
+ return scripts;
1633
+ }
1634
+ function detectIndent(text) {
1635
+ const m = /\n([ \t]+)"/.exec(text);
1636
+ if (m?.[1] === undefined)
1637
+ return 2;
1638
+ return m[1].startsWith("\t") ? "\t" : m[1].length;
1639
+ }
1640
+ function renderPackageJson(original, scripts, type) {
1641
+ const parsed = JSON.parse(original);
1642
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1643
+ throw new Error("package.json is not a JSON object");
1644
+ }
1645
+ const pkg = parsed;
1646
+ const current = typeof pkg.scripts === "object" && pkg.scripts !== null && !Array.isArray(pkg.scripts) ? pkg.scripts : {};
1647
+ const changed = [];
1648
+ const next = { ...current };
1649
+ for (const [key, value] of Object.entries(scripts)) {
1650
+ if (current[key] !== value)
1651
+ changed.push({ key, from: current[key], to: value });
1652
+ next[key] = value;
1653
+ }
1654
+ pkg.scripts = next;
1655
+ if (type !== undefined) {
1656
+ const boff = typeof pkg.boff === "object" && pkg.boff !== null && !Array.isArray(pkg.boff) ? pkg.boff : {};
1657
+ if (boff.type !== type) {
1658
+ changed.push({ key: "boff.type", from: typeof boff.type === "string" ? boff.type : undefined, to: type });
1659
+ pkg.boff = { ...boff, type };
1660
+ }
1661
+ }
1662
+ const trailing = original.endsWith(`
1663
+ `) ? `
1664
+ ` : "";
1665
+ return { text: `${JSON.stringify(pkg, null, detectIndent(original))}${trailing}`, changed };
1666
+ }
1667
+ function readText(path) {
1668
+ return existsSync6(path) ? readFileSync9(path, "utf8") : null;
1669
+ }
1670
+ function writeText(path, text) {
1671
+ writeFileSync2(path, text);
1672
+ }
1673
+
1674
+ // src/sync.ts
1675
+ function usesLefthook(ctx) {
1676
+ if (existsSync7(join10(ctx.root, "lefthook.yml")))
1677
+ return true;
1678
+ const deps = { ...ctx.pkg?.dependencies, ...ctx.pkg?.devDependencies };
1679
+ return deps.lefthook !== undefined;
1680
+ }
1681
+ function apply(root, file, original, rendered, check, notes) {
1682
+ if (original === rendered)
1683
+ return { file, status: "unchanged", notes };
1684
+ if (check)
1685
+ return { file, status: original === null ? "missing" : "drift", notes };
1686
+ writeText(join10(root, file), rendered);
1687
+ return { file, status: original === null ? "created" : "updated", notes };
1688
+ }
1689
+ function bunTooOldForBlock(root) {
1690
+ const pin = readText(join10(root, ".bun-version"))?.trim();
1691
+ if (pin === undefined || pin === "")
1692
+ return true;
1693
+ const [major = 0, minor = 0] = pin.split(".").map((n) => Number.parseInt(n, 10));
1694
+ return major < 1 || major === 1 && minor < 4;
1695
+ }
1696
+ function syncBunfig(ctx, check, excludes) {
1697
+ const original = readText(join10(ctx.root, "bunfig.toml"));
1698
+ const r = renderBunfig(original ?? "", readTemplate(), excludes);
1699
+ const notes = r.replacedKeys.map((k) => `replaced an unmanaged ${k}`);
1700
+ if (bunTooOldForBlock(ctx.root)) {
1701
+ notes.push(`.bun-version is not ${SUPPORTED_BUN}: Bun 1.3.x rejects minimumReleaseAge = "3d", so pin Bun ${SUPPORTED_BUN} in the same commit`);
1702
+ }
1703
+ return apply(ctx.root, "bunfig.toml", original, r.text, check, notes);
1704
+ }
1705
+ function syncLefthook(ctx, check) {
1706
+ if (!usesLefthook(ctx))
1707
+ return { file: "lefthook.yml", status: "skipped", notes: ["repo does not use lefthook"] };
1708
+ const original = readText(join10(ctx.root, "lefthook.yml"));
1709
+ const r = renderLefthook(original ?? "");
1710
+ const notes = r.replaced.some((l) => l.includes("run:") && !l.includes("sanity")) ? [`replaced an unmanaged pre-push block: ${r.replaced.map((l) => l.trim()).join(" | ")}`] : [];
1711
+ return apply(ctx.root, "lefthook.yml", original, r.text, check, notes);
1712
+ }
1713
+ function syncScripts(ctx, check, writeType) {
1714
+ const original = readText(join10(ctx.root, "package.json"));
1715
+ if (original === null)
1716
+ return { file: "package.json", status: "skipped", notes: ["no package.json"] };
1717
+ const r = renderPackageJson(original, managedScripts(ctx), writeType ? ctx.type : undefined);
1718
+ const notes = r.changed.map((c) => `${c.key}: ${c.from ?? "(absent)"} -> ${c.to}`);
1719
+ return apply(ctx.root, "package.json", original, r.text, check, notes);
1720
+ }
1721
+ function runSync(root, opts) {
1722
+ const ctx = loadRepo(root, opts.type);
1723
+ const excludes = opts.excludes ?? shippedExcludes();
1724
+ const items = [syncBunfig(ctx, opts.check, excludes), syncLefthook(ctx, opts.check)];
1725
+ if (opts.scripts || opts.type !== undefined) {
1726
+ if (opts.scripts)
1727
+ items.push(syncScripts(ctx, opts.check, opts.type !== undefined));
1728
+ else
1729
+ items.push(syncTypeOnly(ctx, opts.check));
1730
+ }
1731
+ return items;
1732
+ }
1733
+ function syncTypeOnly(ctx, check) {
1734
+ const original = readText(join10(ctx.root, "package.json"));
1735
+ if (original === null)
1736
+ return { file: "package.json", status: "skipped", notes: ["no package.json"] };
1737
+ const r = renderPackageJson(original, {}, ctx.type);
1738
+ return apply(ctx.root, "package.json", original, r.text, check, r.changed.map((c) => `${c.key} -> ${c.to}`));
1739
+ }
1740
+ var HELP3 = `usage: boff sync [--check] [--type <type>] [--scripts] [--refresh-excludes]
1741
+
1742
+ --check verify only; exit 1 when any managed block is missing or differs
1743
+ --type <type> repo type (also recorded as package.json boff.type)
1744
+ --scripts also manage the package.json scripts for the repo type
1745
+ --refresh-excludes merge the live @burdenoff/@fluidgrids/@vibecontrols package list
1746
+ from the npm registry (NPM_TOKEN adds private packages)`;
1747
+ async function syncCommand(root, argv, w = consoleWriter) {
1748
+ const args = parseArgs(argv, ["type"]);
1749
+ if (flagBool(args, "help")) {
1750
+ w.out(HELP3);
1751
+ return 0;
1752
+ }
1753
+ const check = flagBool(args, "check");
1754
+ let excludes;
1755
+ if (flagBool(args, "refresh-excludes")) {
1756
+ excludes = await fetchFleetExcludes(process.env.NPM_TOKEN);
1757
+ w.out(`boff sync: ${excludes.length} fleet packages from the registry`);
1758
+ }
1759
+ const items = runSync(root, { check, scripts: flagBool(args, "scripts"), type: flagString(args, "type"), excludes });
1760
+ let drift = false;
1761
+ for (const item of items) {
1762
+ w.out(`${item.status.padEnd(9)} ${item.file}`);
1763
+ for (const note of item.notes)
1764
+ w.out(` ${note}`);
1765
+ if (item.status === "drift" || item.status === "missing")
1766
+ drift = true;
1767
+ }
1768
+ if (drift) {
1769
+ w.err("boff sync --check: managed blocks are missing or modified; run `boff sync` and commit the result");
1770
+ return 1;
1771
+ }
1772
+ return 0;
1773
+ }
1774
+
1775
+ // src/doctor.ts
1776
+ var ok = (id, detail = "") => ({ id, status: "ok", detail });
1777
+ var fail = (id, detail) => ({ id, status: "fail", detail });
1778
+ var skip = (id, detail) => ({ id, status: "skip", detail });
1779
+ function linesMatching(files, re) {
1780
+ const hits = [];
1781
+ for (const f of files) {
1782
+ f.text.split(`
1783
+ `).forEach((line, i) => {
1784
+ if (/^\s*#/.test(line))
1785
+ return;
1786
+ if (re.test(line))
1787
+ hits.push(`${f.path}:${i + 1}`);
1788
+ });
1789
+ }
1790
+ return hits;
1791
+ }
1792
+ function checkBunVersion(root) {
1793
+ const path = join11(root, ".bun-version");
1794
+ if (!existsSync8(path))
1795
+ return fail("bun-version", `.bun-version is missing (expected ${SUPPORTED_BUN})`);
1796
+ const v = readFileSync10(path, "utf8").trim();
1797
+ return v === SUPPORTED_BUN ? ok("bun-version", v) : fail("bun-version", `.bun-version is ${v}, expected ${SUPPORTED_BUN}`);
1798
+ }
1799
+ function checkPackageManager(root) {
1800
+ const { pkg } = loadRepo(root);
1801
+ if (pkg === null)
1802
+ return skip("package-manager", "no package.json");
1803
+ const problems = [];
1804
+ if (pkg.packageManager !== `bun@${SUPPORTED_BUN}`) {
1805
+ problems.push(`packageManager is ${pkg.packageManager ?? "(absent)"}, expected bun@${SUPPORTED_BUN}`);
1806
+ }
1807
+ if (pkg.engines?.bun !== `>=${SUPPORTED_BUN}`) {
1808
+ problems.push(`engines.bun is ${pkg.engines?.bun ?? "(absent)"}, expected >=${SUPPORTED_BUN}`);
1809
+ }
1810
+ return problems.length === 0 ? ok("package-manager", `bun@${SUPPORTED_BUN}`) : fail("package-manager", problems.join("; "));
1811
+ }
1812
+ function checkNoBaseUrl(root) {
1813
+ const configs = repoFiles(root).filter((f) => /(^|\/)[tj]sconfig[^/]*\.json$/.test(f));
1814
+ const offenders = [];
1815
+ for (const f of configs) {
1816
+ try {
1817
+ const parsed = parseJsonc(readFileSync10(join11(root, f), "utf8"));
1818
+ const co = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "compilerOptions") : undefined;
1819
+ if (typeof co === "object" && co !== null && Reflect.has(co, "baseUrl"))
1820
+ offenders.push(f);
1821
+ } catch {
1822
+ if (/"baseUrl"\s*:/.test(readFileSync10(join11(root, f), "utf8")))
1823
+ offenders.push(f);
1824
+ }
1825
+ }
1826
+ if (configs.length === 0)
1827
+ return skip("no-baseurl", "no tsconfig files");
1828
+ return offenders.length === 0 ? ok("no-baseurl", `${configs.length} tsconfig file(s)`) : fail("no-baseurl", `baseUrl is removed in TypeScript 7 (bun check fails on it): ${offenders.join(", ")}`);
1829
+ }
1830
+ function checkManagedBlocks(root) {
1831
+ const ctx = loadRepo(root);
1832
+ let items;
1833
+ try {
1834
+ items = runSync(root, { check: true, scripts: ctx.config.type !== undefined });
1835
+ } catch (error) {
1836
+ return fail("managed-blocks", error instanceof Error ? error.message : String(error));
1837
+ }
1838
+ const bad = items.filter((i) => i.status === "drift" || i.status === "missing");
1839
+ return bad.length === 0 ? ok("managed-blocks", items.filter((i) => i.status === "unchanged").map((i) => i.file).join(", ")) : fail("managed-blocks", `${bad.map((i) => `${i.file} (${i.status})`).join(", ")}; run \`boff sync\``);
1840
+ }
1841
+ function checkNoBunfigOverwrite(root) {
1842
+ const ci = [...workflowFiles(root), ...actionFiles(root)];
1843
+ const heredoc = linesMatching(ci, /(?:cat|tee|printf|echo)\b[^#\n]*>\s*["']?(?:\.\/|\$\{?\w+\}?\/)?bunfig\.toml\b|\btee\s+(?:-a\s+)?["']?(?:\.\/)?bunfig\.toml\b/);
1844
+ const mounts = linesMatching(dockerFiles(root), /--mount=type=secret[^\n]*(?:id=bunfig\b|target=[^\s,]*bunfig\.toml)/);
1845
+ const hits = [...heredoc, ...mounts];
1846
+ if (ci.length === 0 && mounts.length === 0 && dockerFiles(root).length === 0)
1847
+ return skip("bunfig-overwrite", "no workflows or Dockerfiles");
1848
+ return hits.length === 0 ? ok("bunfig-overwrite", "no CI or Docker step rewrites bunfig.toml") : fail("bunfig-overwrite", `bunfig.toml is overwritten or secret-mounted (its settings are lost): ${hits.join(", ")}`);
1849
+ }
1850
+ function checkBunPins(root) {
1851
+ const ci = [...workflowFiles(root), ...actionFiles(root)];
1852
+ const literal = linesMatching(ci, /^\s*bun-version:\s*\S/);
1853
+ const docker = [];
1854
+ for (const f of dockerFiles(root)) {
1855
+ f.text.split(`
1856
+ `).forEach((line, i) => {
1857
+ const m = /oven\/bun:([0-9][^\s@-]*)/.exec(line);
1858
+ if (m !== null && !/^\s*#/.test(line) && m[1] !== SUPPORTED_BUN)
1859
+ docker.push(`${f.path}:${i + 1} (${m[1] ?? ""})`);
1860
+ });
1861
+ }
1862
+ if (ci.length === 0 && dockerFiles(root).length === 0)
1863
+ return skip("bun-pins", "no workflows or Dockerfiles");
1864
+ const problems = [];
1865
+ if (literal.length > 0)
1866
+ problems.push(`literal bun-version (use bun-version-file: .bun-version): ${literal.join(", ")}`);
1867
+ if (docker.length > 0)
1868
+ problems.push(`Docker base is not oven/bun:${SUPPORTED_BUN}: ${docker.join(", ")}`);
1869
+ return problems.length === 0 ? ok("bun-pins", "Bun version comes from .bun-version") : fail("bun-pins", problems.join("; "));
1870
+ }
1871
+ function checkSetupBunSha(root) {
1872
+ const ci = [...workflowFiles(root), ...actionFiles(root)];
1873
+ const uses = linesMatching(ci, /uses:\s*["']?oven-sh\/setup-bun@/);
1874
+ if (uses.length === 0)
1875
+ return skip("setup-bun-sha", "setup-bun is not used");
1876
+ const unpinned = linesMatching(ci, /uses:\s*["']?oven-sh\/setup-bun@(?![0-9a-f]{40}\b)/);
1877
+ return unpinned.length === 0 ? ok("setup-bun-sha", `${uses.length} pinned use(s)`) : fail("setup-bun-sha", `oven-sh/setup-bun must be pinned to a full commit SHA: ${unpinned.join(", ")}`);
1878
+ }
1879
+ function checkPrGate(root) {
1880
+ const wfs = workflowFiles(root);
1881
+ if (wfs.length === 0)
1882
+ return skip("pr-gate", "no workflows");
1883
+ const gate = wfs.find((f) => /^\s*pull_request(?:_target)?\s*:?/m.test(f.text) && /^\s+name:\s*["']?PR Gate["']?\s*$/m.test(f.text));
1884
+ return gate === undefined ? fail("pr-gate", 'no pull_request workflow has a job named exactly "PR Gate" (the required check)') : ok("pr-gate", gate.path);
1885
+ }
1886
+ function checkBunfigUnchanged(root) {
1887
+ if (!existsSync8(join11(root, "bunfig.toml")))
1888
+ return skip("bunfig-unchanged", "no bunfig.toml");
1889
+ if (!isGitRepo(root))
1890
+ return skip("bunfig-unchanged", "not a git work tree");
1891
+ return differsFromHead(root, "bunfig.toml") ? fail("bunfig-unchanged", "bunfig.toml differs from HEAD (or is uncommitted); CI must use the committed file") : ok("bunfig-unchanged", "matches HEAD");
1892
+ }
1893
+ function runDoctorChecks(root) {
1894
+ return [
1895
+ checkBunVersion(root),
1896
+ checkPackageManager(root),
1897
+ checkNoBaseUrl(root),
1898
+ checkManagedBlocks(root),
1899
+ checkNoBunfigOverwrite(root),
1900
+ checkBunPins(root),
1901
+ checkSetupBunSha(root),
1902
+ checkPrGate(root),
1903
+ checkBunfigUnchanged(root)
1904
+ ];
1905
+ }
1906
+ function doctorCommand(root, argv, w = consoleWriter) {
1907
+ const args = parseArgs(argv);
1908
+ if (flagBool(args, "help")) {
1909
+ w.out(`usage: boff doctor [--ci]
1910
+
1911
+ --ci exit 1 when any check fails (otherwise failures are warnings)`);
1912
+ return 0;
1913
+ }
1914
+ const ci = flagBool(args, "ci");
1915
+ const results = runDoctorChecks(root);
1916
+ const label = (s) => s === "fail" ? ci ? "FAIL" : "warn" : s;
1917
+ for (const line of renderTable(["check", "status", "detail"], results.map((r) => [r.id, label(r.status), r.detail]))) {
1918
+ w.out(line);
1919
+ }
1920
+ const failed = results.filter((r) => r.status === "fail");
1921
+ if (isGithubActions() && ci) {
1922
+ for (const r of failed)
1923
+ w.err(`::error title=boff doctor (${r.id})::${r.detail}`);
1924
+ }
1925
+ if (failed.length > 0) {
1926
+ w.err(`boff doctor: ${failed.length} check(s) ${ci ? "failed" : "need attention"}`);
1927
+ return ci ? 1 : 0;
1928
+ }
1929
+ return 0;
1930
+ }
1931
+
1932
+ // src/gate.ts
1933
+ import { existsSync as existsSync10, mkdirSync as mkdirSync2, accessSync, constants as fsConstants } from "fs";
1934
+ import { availableParallelism, homedir } from "os";
1935
+ import { join as join12 } from "path";
1936
+
1937
+ // src/lib/lock.ts
1938
+ import { closeSync, existsSync as existsSync9, mkdirSync, openSync, readFileSync as readFileSync11, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
1939
+ var LOCK_EX = 2;
1940
+ var LOCK_NB = 4;
1941
+ var LOCK_UN = 8;
1942
+ var cachedFlock;
1943
+ function libcCandidates() {
1944
+ if (process.platform === "darwin")
1945
+ return ["libc.dylib", "/usr/lib/libSystem.B.dylib"];
1946
+ return ["libc.so.6", `libc.musl-${process.arch === "arm64" ? "aarch64" : "x86_64"}.so.1`, "libc.so"];
1947
+ }
1948
+ async function loadFlock() {
1949
+ if (cachedFlock !== undefined)
1950
+ return cachedFlock;
1951
+ if (process.env.BOFF_LOCK_IMPL === "mkdir") {
1952
+ cachedFlock = null;
1953
+ return null;
1954
+ }
1955
+ try {
1956
+ const ffi = await import("bun:ffi");
1957
+ for (const name of libcCandidates()) {
1958
+ try {
1959
+ const lib = ffi.dlopen(name, { flock: { args: [ffi.FFIType.i32, ffi.FFIType.i32], returns: ffi.FFIType.i32 } });
1960
+ const fn = lib.symbols.flock;
1961
+ cachedFlock = (fd, op) => fn(fd, op);
1962
+ return cachedFlock;
1963
+ } catch {}
1964
+ }
1965
+ } catch {}
1966
+ cachedFlock = null;
1967
+ return null;
1968
+ }
1969
+ function pidAlive(pid) {
1970
+ try {
1971
+ process.kill(pid, 0);
1972
+ return true;
1973
+ } catch (error) {
1974
+ return error instanceof Error && "code" in error && error.code === "EPERM";
1975
+ }
1976
+ }
1977
+ function tryMkdirLock(path) {
1978
+ const dir = `${path}.d`;
1979
+ const owner = `${dir}/pid`;
1980
+ for (let attempt = 0;attempt < 2; attempt++) {
1981
+ try {
1982
+ mkdirSync(dir);
1983
+ writeFileSync3(owner, String(process.pid));
1984
+ return {
1985
+ path,
1986
+ release: () => {
1987
+ rmSync3(dir, { recursive: true, force: true });
1988
+ }
1989
+ };
1990
+ } catch {
1991
+ let pid = Number.NaN;
1992
+ try {
1993
+ pid = Number(readFileSync11(owner, "utf8").trim());
1994
+ } catch {
1995
+ return null;
1996
+ }
1997
+ if (Number.isInteger(pid) && pid > 0 && pidAlive(pid))
1998
+ return null;
1999
+ rmSync3(dir, { recursive: true, force: true });
2000
+ }
2001
+ }
2002
+ return null;
2003
+ }
2004
+ async function tryLock(path) {
2005
+ const flock = await loadFlock();
2006
+ if (flock === null)
2007
+ return tryMkdirLock(path);
2008
+ let fd;
2009
+ try {
2010
+ fd = existsSync9(path) ? openSync(path, "r") : openSync(path, "a");
2011
+ } catch {
2012
+ return null;
2013
+ }
2014
+ if (flock(fd, LOCK_EX | LOCK_NB) !== 0) {
2015
+ closeSync(fd);
2016
+ return null;
2017
+ }
2018
+ let released = false;
2019
+ return {
2020
+ path,
2021
+ release: () => {
2022
+ if (released)
2023
+ return;
2024
+ released = true;
2025
+ flock(fd, LOCK_UN);
2026
+ closeSync(fd);
2027
+ }
2028
+ };
2029
+ }
2030
+
2031
+ // src/gate.ts
2032
+ var GATE_TIMEOUT_EXIT = 75;
2033
+ var DEFAULT_WAIT_SECONDS = 900;
2034
+ function clamp(n, lo, hi) {
2035
+ return Math.min(hi, Math.max(lo, n));
2036
+ }
2037
+ function positiveInt(value) {
2038
+ if (value === undefined || value.trim() === "")
2039
+ return;
2040
+ const n = Number(value);
2041
+ return Number.isInteger(n) && n > 0 ? n : undefined;
2042
+ }
2043
+ function cpuCount() {
2044
+ return availableParallelism();
2045
+ }
2046
+ function slotCount(env, cores) {
2047
+ return positiveInt(env.BOFF_BUILD_SLOTS) ?? clamp(Math.floor(cores / 8), 1, 4);
2048
+ }
2049
+ function threadCount(env, cores) {
2050
+ return positiveInt(env.BOFF_BUILD_THREADS) ?? clamp(Math.floor(cores / 4), 2, 8);
2051
+ }
2052
+ function waitSeconds(env) {
2053
+ const raw = env.BOFF_BUILD_WAIT;
2054
+ if (raw === undefined || raw.trim() === "")
2055
+ return DEFAULT_WAIT_SECONDS;
2056
+ const n = Number(raw);
2057
+ return Number.isFinite(n) && n >= 0 ? n : DEFAULT_WAIT_SECONDS;
2058
+ }
2059
+ function isSlotHeld(env) {
2060
+ return env.BOFF_BUILD_SLOT_HELD === "1" || env.BUILDSLOT_HELD === "1";
2061
+ }
2062
+ function writableDir(path) {
2063
+ try {
2064
+ accessSync(path, fsConstants.W_OK | fsConstants.X_OK);
2065
+ return true;
2066
+ } catch {
2067
+ return false;
2068
+ }
2069
+ }
2070
+ function lockRoot(env, uid = process.getuid?.() ?? 0, runUserBase = "/run/user") {
2071
+ const override = env.BOFF_BUILD_SLOT_DIR;
2072
+ if (override !== undefined && override !== "")
2073
+ return override;
2074
+ const runDir = join12(runUserBase, String(uid));
2075
+ if (existsSync10(runDir) && writableDir(runDir))
2076
+ return join12(runDir, "boff-build-slots");
2077
+ const home = env.HOME !== undefined && env.HOME !== "" ? env.HOME : homedir();
2078
+ return join12(home, ".cache", "boff", "build-slots");
2079
+ }
2080
+ function gateEnv(env, threads, held) {
2081
+ const out = {
2082
+ BOFF_BUILD_THREADS: String(threads),
2083
+ RAYON_NUM_THREADS: env.RAYON_NUM_THREADS ?? String(threads),
2084
+ ROLLDOWN_WORKER_THREADS: env.ROLLDOWN_WORKER_THREADS ?? String(threads),
2085
+ ROLLDOWN_MAX_BLOCKING_THREADS: env.ROLLDOWN_MAX_BLOCKING_THREADS ?? String(threads)
2086
+ };
2087
+ if (held) {
2088
+ out.BOFF_BUILD_SLOT_HELD = "1";
2089
+ out.BUILDSLOT_HELD = "1";
2090
+ }
2091
+ return out;
2092
+ }
2093
+ function hasFlag(argv, flag) {
2094
+ return argv.some((a) => a === flag || a.startsWith(`${flag}=`));
2095
+ }
2096
+ function isVitest(argv) {
2097
+ const bin = (a) => a !== undefined && /(^|\/)vitest$/.test(a);
2098
+ if (bin(argv[0]))
2099
+ return true;
2100
+ if ((argv[0] === "bunx" || argv[0] === "npx") && bin(argv[1]))
2101
+ return true;
2102
+ return argv[0] === "bun" && argv[1] === "x" && bin(argv[2]);
2103
+ }
2104
+ function injectThreadFlags(argv, threads) {
2105
+ const out = [...argv];
2106
+ const isBun = out[0] === "bun" || /(^|\/)bun$/.test(out[0] ?? "");
2107
+ if (isBun && (out[1] === "check" || out[1] === "fmt") && !hasFlag(out, "--threads")) {
2108
+ out.splice(2, 0, `--threads=${threads}`);
2109
+ return out;
2110
+ }
2111
+ if (isBun && out[1] === "test") {
2112
+ const i = out.indexOf("--parallel");
2113
+ if (i >= 0)
2114
+ out[i] = `--parallel=${threads}`;
2115
+ return out;
2116
+ }
2117
+ if (isVitest(out) && !hasFlag(out, "--maxWorkers")) {
2118
+ out.push(`--maxWorkers=${threads}`);
2119
+ }
2120
+ return out;
2121
+ }
2122
+ var cachedPrefix;
2123
+ function throttlePrefix() {
2124
+ if (cachedPrefix !== undefined)
2125
+ return cachedPrefix;
2126
+ const prefix = [];
2127
+ if (capture(["nice", "-n", "10", "true"]).code === 0)
2128
+ prefix.push("nice", "-n", "10");
2129
+ if (capture(["ionice", "-c3", "-t", "true"]).code === 0)
2130
+ prefix.push("ionice", "-c3", "-t");
2131
+ else if (capture(["ionice", "-c3", "true"]).code === 0)
2132
+ prefix.push("ionice", "-c3");
2133
+ cachedPrefix = prefix;
2134
+ return prefix;
2135
+ }
2136
+ async function acquireSlot(opts = {}) {
2137
+ const env = opts.env ?? process.env;
2138
+ const writer = opts.writer ?? consoleWriter;
2139
+ if (isSlotHeld(env))
2140
+ return { kind: "reentrant" };
2141
+ if (isCi(env) && env.BOFF_GATE_IN_CI !== "1")
2142
+ return { kind: "ci" };
2143
+ const cores = opts.cores ?? cpuCount();
2144
+ const slots = slotCount(env, cores);
2145
+ const root = lockRoot(env);
2146
+ try {
2147
+ mkdirSync2(root, { recursive: true });
2148
+ } catch (error) {
2149
+ const reason = error instanceof Error ? error.message : String(error);
2150
+ writer.err(`boff gate: cannot create the lock root ${root} (${reason}); running ungated`);
2151
+ return { kind: "ungated", waitedMs: 0 };
2152
+ }
2153
+ const strict = opts.strict === true || env.BOFF_GATE_STRICT === "1";
2154
+ const deadline = Date.now() + waitSeconds(env) * 1000;
2155
+ const started = Date.now();
2156
+ const pollMs = opts.pollMs ?? 500;
2157
+ let announced = false;
2158
+ for (;; ) {
2159
+ for (let i = 1;i <= slots; i++) {
2160
+ const lock = await tryLock(join12(root, `slot.${i}`));
2161
+ if (lock !== null)
2162
+ return { kind: "held", lock, waitedMs: Date.now() - started, slot: i };
2163
+ }
2164
+ if (Date.now() >= deadline) {
2165
+ const waitedMs = Date.now() - started;
2166
+ if (strict) {
2167
+ writer.err(`boff gate: no build slot freed within ${waitSeconds(env)}s (${slots} slots in ${root}); failing (strict mode)`);
2168
+ return { kind: "timeout", waitedMs };
2169
+ }
2170
+ writer.err(`boff gate: waited ${waitSeconds(env)}s for one of ${slots} build slots; running ungated`);
2171
+ return { kind: "ungated", waitedMs };
2172
+ }
2173
+ if (!announced) {
2174
+ writer.err(`boff gate: all ${slots} build slots busy (${root}); waiting...`);
2175
+ announced = true;
2176
+ }
2177
+ await Bun.sleep(pollMs + Math.floor(Math.random() * pollMs));
2178
+ }
2179
+ }
2180
+ async function runGated(argv, opts = {}) {
2181
+ if (argv.length === 0) {
2182
+ (opts.writer ?? consoleWriter).err("usage: boff gate [--pre-push] -- <command> [args...]");
2183
+ return 2;
2184
+ }
2185
+ const env = opts.env ?? process.env;
2186
+ const cores = opts.cores ?? cpuCount();
2187
+ const threads = threadCount(env, cores);
2188
+ const slot = await acquireSlot({ ...opts, env, cores });
2189
+ if (slot.kind === "timeout")
2190
+ return GATE_TIMEOUT_EXIT;
2191
+ const holds = slot.kind === "held" || slot.kind === "reentrant";
2192
+ const cmd = injectThreadFlags(argv, threads);
2193
+ const full = slot.kind === "held" || slot.kind === "ungated" ? [...throttlePrefix(), ...cmd] : cmd;
2194
+ const childEnv = { ...env, ...gateEnv(env, threads, holds) };
2195
+ try {
2196
+ const proc = Bun.spawn(full, {
2197
+ cwd: opts.cwd,
2198
+ env: childEnv,
2199
+ stdin: "inherit",
2200
+ stdout: "inherit",
2201
+ stderr: "inherit"
2202
+ });
2203
+ const forward = (sig) => {
2204
+ proc.kill(sig);
2205
+ };
2206
+ process.on("SIGINT", forward);
2207
+ process.on("SIGTERM", forward);
2208
+ try {
2209
+ return await proc.exited;
2210
+ } finally {
2211
+ process.off("SIGINT", forward);
2212
+ process.off("SIGTERM", forward);
2213
+ }
2214
+ } catch (error) {
2215
+ (opts.writer ?? consoleWriter).err(`boff gate: cannot run ${cmd[0] ?? ""}: ${error instanceof Error ? error.message : String(error)}`);
2216
+ return 127;
2217
+ } finally {
2218
+ if (slot.kind === "held")
2219
+ slot.lock.release();
2220
+ }
2221
+ }
2222
+
2223
+ // src/sanity.ts
2224
+ import {
2225
+ closeSync as closeSync2,
2226
+ existsSync as existsSync11,
2227
+ mkdirSync as mkdirSync3,
2228
+ openSync as openSync2,
2229
+ readFileSync as readFileSync12,
2230
+ readdirSync,
2231
+ renameSync,
2232
+ rmSync as rmSync4,
2233
+ statSync,
2234
+ writeFileSync as writeFileSync4,
2235
+ writeSync
2236
+ } from "fs";
2237
+ import { join as join13 } from "path";
2238
+ var STEP_NAMES = [
2239
+ "format",
2240
+ "codegen",
2241
+ "gendb",
2242
+ "migrate",
2243
+ "lint",
2244
+ "typecheck",
2245
+ "build",
2246
+ "test",
2247
+ "knip",
2248
+ "audit"
2249
+ ];
2250
+ function isStepName(v) {
2251
+ return STEP_NAMES.includes(v);
2252
+ }
2253
+ var FRONTEND = ["mfe", "fe-libs", "app", "website"];
2254
+ var BUILD_REFERENCES2 = ["mfe", "fe-libs", "website"];
2255
+ var COVERAGE_GATED = ["svc", "gateway", "be-sdk"];
2256
+ var DEFAULT_STEPS = {
2257
+ svc: ["format", "codegen", "gendb", "migrate", "lint", "typecheck", "build", "test", "knip"],
2258
+ gateway: ["format", "lint", "typecheck", "build", "test"],
2259
+ "be-sdk": ["format", "lint", "typecheck", "build", "test"],
2260
+ mfe: ["format", "codegen", "lint", "typecheck", "build", "test"],
2261
+ "fe-libs": ["format", "lint", "typecheck", "build", "test"],
2262
+ app: ["format", "codegen", "lint", "typecheck", "build", "test"],
2263
+ website: ["format", "lint", "typecheck", "build", "test"],
2264
+ docs: ["build"],
2265
+ cli: ["format", "lint", "typecheck", "build", "test"],
2266
+ mcp: ["format", "lint", "typecheck", "build", "test"],
2267
+ "sdk-node": ["format", "lint", "typecheck", "build", "test"],
2268
+ "browser-ext": ["format", "lint", "typecheck", "build", "test"],
2269
+ "vscode-ext": ["format", "lint", "typecheck", "build", "test"],
2270
+ plugin: ["format", "lint", "typecheck", "build", "test"],
2271
+ doctor: ["format", "lint", "typecheck", "test"],
2272
+ lib: ["format", "lint", "typecheck", "build", "test"]
2273
+ };
2274
+ var CODE = ["**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"];
2275
+ var FORMATTABLE = "**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,json,jsonc,json5,md,mdx,yml,yaml,css,scss,less,html,vue,svelte,graphql,gql,toml}";
2276
+ var FORMAT_CONFIG = [".prettierrc*", "prettier.config.*", ".prettierignore", ".editorconfig", ".gitignore"];
2277
+ var TYPECHECK_INPUTS = [...CODE, "src/**", "**/tsconfig*.json", "scripts/typecheck.sh"];
2278
+ var STEP_DEPS = {
2279
+ format: [],
2280
+ codegen: [],
2281
+ gendb: [],
2282
+ migrate: [],
2283
+ lint: ["codegen", "gendb"],
2284
+ typecheck: ["codegen", "gendb"],
2285
+ build: ["typecheck"],
2286
+ test: ["codegen", "gendb"],
2287
+ knip: [],
2288
+ audit: []
2289
+ };
2290
+ var GENERATORS = ["codegen", "gendb"];
2291
+ var MIGRATE_MAX_AGE_MS = 24 * 60 * 60 * 1000;
2292
+ var STAMPS_KEPT_PER_STEP = 10;
2293
+ function cliEntry() {
2294
+ const src = join13(PACKAGE_ROOT, "src", "cli.ts");
2295
+ return existsSync11(src) ? src : join13(PACKAGE_ROOT, "dist", "cli.js");
2296
+ }
2297
+ function binPath(root, name) {
2298
+ const p = join13(root, "node_modules", ".bin", name);
2299
+ return existsSync11(p) ? p : null;
2300
+ }
2301
+ function anyFile(root, names) {
2302
+ return names.some((n) => existsSync11(join13(root, n)));
2303
+ }
2304
+ function readEnvValue(root, env, key) {
2305
+ const direct = env[key];
2306
+ if (direct !== undefined && direct !== "")
2307
+ return direct;
2308
+ for (const file of [".env.local", ".env"]) {
2309
+ const path = join13(root, file);
2310
+ if (!existsSync11(path))
2311
+ continue;
2312
+ for (const line of readFileSync12(path, "utf8").split(`
2313
+ `)) {
2314
+ const m = new RegExp(`^\\s*(?:export\\s+)?${key}\\s*=\\s*(.*)$`).exec(line);
2315
+ if (m?.[1] !== undefined)
2316
+ return m[1].trim().replace(/^(['"])(.*)\1$/, "$2");
2317
+ }
2318
+ }
2319
+ return;
2320
+ }
2321
+ function databaseIdentity(root, env) {
2322
+ const url = readEnvValue(root, env, "DATABASE_URL");
2323
+ if (url === undefined)
2324
+ return;
2325
+ try {
2326
+ const u = new URL(url);
2327
+ return sha256(`${u.hostname}:${u.port}${u.pathname}`);
2328
+ } catch {
2329
+ return sha256("unparsable-database-url", url);
2330
+ }
2331
+ }
2332
+ function formatGlobs(paths) {
2333
+ return paths.flatMap((p) => /[*?[{]/.test(p) ? [p] : [p, `${p}/**`]);
2334
+ }
2335
+ function bunTestArgs(script) {
2336
+ if (script === undefined)
2337
+ return [];
2338
+ const m = /^bun test(?:\s+(.*))?$/.exec(script.trim());
2339
+ if (m === null)
2340
+ return null;
2341
+ const rest = m[1] ?? "";
2342
+ if (/[|&;<>$`()]/.test(rest))
2343
+ return null;
2344
+ return rest === "" ? [] : rest.split(/\s+/);
2345
+ }
2346
+ function specFor(step, ctx, files, env) {
2347
+ const { root, type } = ctx;
2348
+ const override = `sanity:${step}`;
2349
+ const scriptCmd = (name) => [["bun", "run", name]];
2350
+ const withOverride = (spec) => hasScript(ctx, override) ? { ...spec, commands: scriptCmd(override) } : spec;
2351
+ switch (step) {
2352
+ case "format": {
2353
+ const paths = ctx.config.sanity?.formatPaths;
2354
+ const inputs = paths === undefined ? [FORMATTABLE, ...FORMAT_CONFIG] : [...formatGlobs(paths), ...FORMAT_CONFIG];
2355
+ if (matchGlobs(files, inputs.slice(0, paths === undefined ? 1 : formatGlobs(paths).length)).length === 0) {
2356
+ return "no files to format";
2357
+ }
2358
+ const cmd = ["bun", "fmt", "--check", "--no-script"];
2359
+ if (paths !== undefined)
2360
+ cmd.push("--no-error-on-unmatched-pattern", ...paths);
2361
+ return withOverride({ inputs, commands: [cmd], keyParts: [] });
2362
+ }
2363
+ case "codegen": {
2364
+ if (!hasScript(ctx, "codegen"))
2365
+ return "no codegen script";
2366
+ const inputs = ["**/*.{gql,graphql}", "codegen.*", ".graphqlrc*", "scripts/*codegen*", "graphql/**"];
2367
+ if (FRONTEND.includes(type))
2368
+ inputs.push("src/**/*.{ts,tsx}");
2369
+ const committed = ctx.config.sanity?.codegenCommitted ?? (type === "svc" ? [".hive-schema.graphql"] : []);
2370
+ const drift = committed.filter((f) => files.includes(f)).map((f) => ["git", "diff", "--exit-code", "--", f]);
2371
+ return withOverride({ inputs, commands: [...scriptCmd("codegen"), ...drift], keyParts: committed });
2372
+ }
2373
+ case "gendb":
2374
+ if (!hasScript(ctx, "gendb"))
2375
+ return "no gendb script";
2376
+ return withOverride({
2377
+ inputs: ["prisma/**/*.prisma", "prisma.config.*", "prisma/schema/**"],
2378
+ commands: scriptCmd("gendb"),
2379
+ keyParts: []
2380
+ });
2381
+ case "migrate": {
2382
+ if (!hasScript(ctx, "migrate:deploy"))
2383
+ return "no migrate:deploy script";
2384
+ const db = databaseIdentity(root, env);
2385
+ if (db === undefined)
2386
+ return "no DATABASE_URL (environment, .env.local or .env)";
2387
+ return withOverride({
2388
+ inputs: ["prisma/migrations/**", "prisma/schema/migrations/**"],
2389
+ commands: scriptCmd("migrate:deploy"),
2390
+ keyParts: [db],
2391
+ maxAgeMs: MIGRATE_MAX_AGE_MS
2392
+ });
2393
+ }
2394
+ case "lint": {
2395
+ const inputs = [...CODE, "eslint.config.*", ".eslintignore", "**/tsconfig*.json"];
2396
+ const cache = ["--cache", "--cache-location", "node_modules/.cache/eslint/"];
2397
+ if (hasScript(ctx, override))
2398
+ return withOverride({ inputs, commands: [], keyParts: [] });
2399
+ if (hasScript(ctx, "lint"))
2400
+ return { inputs, commands: [["bun", "run", "lint", ...cache]], keyParts: [] };
2401
+ if (!anyFile(root, ["eslint.config.js", "eslint.config.mjs", "eslint.config.cjs", "eslint.config.ts"])) {
2402
+ return "no eslint config";
2403
+ }
2404
+ const eslint = binPath(root, "eslint");
2405
+ if (eslint === null)
2406
+ return "eslint is not installed";
2407
+ return { inputs, commands: [[eslint, ".", ...cache]], keyParts: [] };
2408
+ }
2409
+ case "typecheck": {
2410
+ if (hasScript(ctx, override))
2411
+ return withOverride({ inputs: TYPECHECK_INPUTS, commands: [], keyParts: [] });
2412
+ if (hasScript(ctx, "type:check")) {
2413
+ return { inputs: TYPECHECK_INPUTS, commands: scriptCmd("type:check"), keyParts: [] };
2414
+ }
2415
+ if (!existsSync11(join13(root, "tsconfig.json")))
2416
+ return "no tsconfig.json";
2417
+ const cmd = ["bun", "check", "--no-pretty", "--no-script"];
2418
+ if (BUILD_REFERENCES2.includes(type))
2419
+ cmd.push("-b");
2420
+ return { inputs: TYPECHECK_INPUTS, commands: [cmd], keyParts: [] };
2421
+ }
2422
+ case "build": {
2423
+ const script = type === "svc" && hasScript(ctx, "build:bundle") ? "build:bundle" : "build";
2424
+ if (!hasScript(ctx, override) && !hasScript(ctx, script))
2425
+ return "no build script";
2426
+ return withOverride({
2427
+ inputs: [
2428
+ ...TYPECHECK_INPUTS,
2429
+ "*.config.*",
2430
+ "vite.config.*",
2431
+ "index.html",
2432
+ "public/**",
2433
+ "scripts/**",
2434
+ "build.ts",
2435
+ "prisma/**",
2436
+ "manifest.json"
2437
+ ],
2438
+ commands: scriptCmd(script),
2439
+ keyParts: []
2440
+ });
2441
+ }
2442
+ case "test": {
2443
+ const inputs = [
2444
+ ...CODE,
2445
+ "src/**",
2446
+ "tests/**",
2447
+ "test/**",
2448
+ "**/__tests__/**",
2449
+ "**/__snapshots__/**",
2450
+ "bunfig*.toml",
2451
+ "vitest.config.*",
2452
+ "fixtures/**"
2453
+ ];
2454
+ const envFile = existsSync11(join13(root, ".env.local")) ? join13(root, ".env.local") : null;
2455
+ const keyParts = envFile === null ? [] : [sha256(readFileSync12(envFile, "utf8"))];
2456
+ if (hasScript(ctx, override))
2457
+ return withOverride({ inputs, commands: [], keyParts });
2458
+ const script = ctx.pkg?.scripts?.test;
2459
+ const args = bunTestArgs(script);
2460
+ if (args === null)
2461
+ return { inputs, commands: scriptCmd("test"), keyParts };
2462
+ const hasTests = matchGlobs(files, ["**/*.{test,spec}.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"]).length > 0;
2463
+ if (!hasTests)
2464
+ return "no test files";
2465
+ const cmd = [
2466
+ "bun",
2467
+ ...envFile === null ? [] : ["--env-file=.env.local"],
2468
+ "test",
2469
+ "--coverage",
2470
+ "--coverage-reporter=text",
2471
+ "--coverage-reporter=lcov",
2472
+ "--reporter=junit",
2473
+ "--reporter-outfile=junit.xml",
2474
+ ...args
2475
+ ];
2476
+ const commands = [cmd];
2477
+ if (ctx.config.coverage?.global !== undefined || COVERAGE_GATED.includes(type)) {
2478
+ commands.push([process.execPath, cliEntry(), "coverage-gate"]);
2479
+ }
2480
+ return { inputs, commands, keyParts };
2481
+ }
2482
+ case "knip": {
2483
+ const inputs = [...CODE, "knip.*", ".knip.*", "**/tsconfig*.json"];
2484
+ if (hasScript(ctx, override))
2485
+ return withOverride({ inputs, commands: [], keyParts: [] });
2486
+ if (hasScript(ctx, "knip"))
2487
+ return { inputs, commands: scriptCmd("knip"), keyParts: [] };
2488
+ if (!anyFile(root, ["knip.json", "knip.jsonc", ".knip.json", "knip.config.ts", "knip.config.js", "knip.ts"])) {
2489
+ return "no knip config";
2490
+ }
2491
+ const knip = binPath(root, "knip");
2492
+ if (knip === null)
2493
+ return "knip is not installed";
2494
+ return { inputs, commands: [[knip, "--cache"]], keyParts: [] };
2495
+ }
2496
+ case "audit":
2497
+ if (!existsSync11(join13(root, "bun.lock")))
2498
+ return "no bun.lock";
2499
+ return withOverride({
2500
+ inputs: [],
2501
+ commands: [["bun", "audit", "--audit-level=high"]],
2502
+ keyParts: [new Date().toISOString().slice(0, 10)]
2503
+ });
2504
+ }
2505
+ }
2506
+ function planSanity(root, opts = {}) {
2507
+ const env = opts.env ?? process.env;
2508
+ const ctx = loadRepo(root);
2509
+ const files = repoFiles(root);
2510
+ const hasher = new FileHasher(root);
2511
+ const explicit = ctx.config.sanity?.steps;
2512
+ if (explicit !== undefined) {
2513
+ for (const s of explicit) {
2514
+ if (!isStepName(s))
2515
+ throw new Error(`package.json boff.sanity.steps: unknown step "${s}" (known: ${STEP_NAMES.join(", ")})`);
2516
+ }
2517
+ }
2518
+ const wanted = (explicit ?? DEFAULT_STEPS[ctx.type]).filter(isStepName);
2519
+ const base = sha256("boff-sanity/1", TOOLCHAIN_VERSION, Bun.version, isCi(env) ? "ci" : "local", ctx.type, hasher.combined(["package.json", "bun.lock", "bunfig.toml", ".bun-version"]));
2520
+ const steps = [];
2521
+ const skipped = [];
2522
+ const keys = new Map;
2523
+ for (const name of STEP_NAMES) {
2524
+ if (!wanted.includes(name))
2525
+ continue;
2526
+ const spec = specFor(name, ctx, files, env);
2527
+ if (typeof spec === "string") {
2528
+ if (explicit !== undefined && name !== "migrate") {
2529
+ throw new Error(`package.json boff.sanity.steps lists "${name}" but it cannot run here: ${spec}`);
2530
+ }
2531
+ skipped.push({ name, reason: spec });
2532
+ continue;
2533
+ }
2534
+ const deps = STEP_DEPS[name].filter((d) => keys.has(d));
2535
+ const key = sha256(base, name, JSON.stringify(spec.commands), hasher.combined(matchGlobs(files, spec.inputs)), ...deps.map((d) => keys.get(d) ?? ""), ...spec.keyParts).slice(0, 32);
2536
+ keys.set(name, key);
2537
+ steps.push({
2538
+ name,
2539
+ outputKey: name,
2540
+ inputs: spec.inputs,
2541
+ deps,
2542
+ generator: GENERATORS.includes(name),
2543
+ commands: spec.commands,
2544
+ keyParts: spec.keyParts,
2545
+ maxAgeMs: spec.maxAgeMs,
2546
+ key
2547
+ });
2548
+ }
2549
+ const allBuiltinKeys = [...keys.values()];
2550
+ for (const script of ctx.config.sanity?.extra ?? []) {
2551
+ if (!hasScript(ctx, script))
2552
+ throw new Error(`package.json boff.sanity.extra lists "${script}" but there is no such script`);
2553
+ const name = `extra:${script}`;
2554
+ const commands = [["bun", "run", script]];
2555
+ const key = sha256(base, name, JSON.stringify(commands), hasher.combined(files), ...allBuiltinKeys).slice(0, 32);
2556
+ steps.push({
2557
+ name,
2558
+ outputKey: `extra_${script.replace(/[^A-Za-z0-9_]/g, "_")}`,
2559
+ inputs: ["**"],
2560
+ deps: [],
2561
+ generator: false,
2562
+ commands,
2563
+ keyParts: [],
2564
+ key
2565
+ });
2566
+ }
2567
+ return { ctx, steps, skipped, stampDir: join13(root, "node_modules", ".cache", "sanity") };
2568
+ }
2569
+ function stampFile(plan, step) {
2570
+ return join13(plan.stampDir, `${step.name.replace(/[^A-Za-z0-9_.-]/g, "_")}.${step.key}`);
2571
+ }
2572
+ function isStampFresh(plan, step, now = Date.now()) {
2573
+ const path = stampFile(plan, step);
2574
+ if (!existsSync11(path))
2575
+ return false;
2576
+ if (step.maxAgeMs === undefined)
2577
+ return true;
2578
+ return now - statSync(path).mtimeMs <= step.maxAgeMs;
2579
+ }
2580
+ function writeStamp(plan, step) {
2581
+ mkdirSync3(plan.stampDir, { recursive: true });
2582
+ const final = stampFile(plan, step);
2583
+ const tmp = `${final}.tmp-${process.pid}`;
2584
+ writeFileSync4(tmp, "");
2585
+ renameSync(tmp, final);
2586
+ const prefix = `${step.name.replace(/[^A-Za-z0-9_.-]/g, "_")}.`;
2587
+ const mine = readdirSync(plan.stampDir).filter((f) => f.startsWith(prefix) && !f.includes(".tmp-")).map((f) => ({ f, t: statSync(join13(plan.stampDir, f)).mtimeMs })).sort((a, b) => b.t - a.t);
2588
+ for (const old of mine.slice(STAMPS_KEPT_PER_STEP))
2589
+ rmSync4(join13(plan.stampDir, old.f), { force: true });
2590
+ }
2591
+ function removePartialState(plan) {
2592
+ if (!existsSync11(plan.stampDir))
2593
+ return;
2594
+ for (const f of readdirSync(plan.stampDir)) {
2595
+ if (f.includes(".tmp-"))
2596
+ rmSync4(join13(plan.stampDir, f), { force: true });
2597
+ }
2598
+ }
2599
+ function appendTiming(plan, entry) {
2600
+ mkdirSync3(plan.stampDir, { recursive: true });
2601
+ writeFileSync4(join13(plan.stampDir, "timings.jsonl"), `${JSON.stringify({ ...entry, ts: new Date().toISOString() })}
2602
+ `, {
2603
+ flag: "a"
2604
+ });
2605
+ }
2606
+ function printKeys(plan) {
2607
+ const lines = plan.steps.map((s) => `${s.outputKey}=${s.key}`);
2608
+ lines.push(`all=${sha256(...plan.steps.map((s) => `${s.name}:${s.key}`)).slice(0, 32)}`);
2609
+ return lines;
2610
+ }
2611
+ async function pump(stream, sink, logFd) {
2612
+ for await (const chunk of stream) {
2613
+ sink.write(chunk);
2614
+ writeSync(logFd, chunk);
2615
+ }
2616
+ }
2617
+ async function runCommand(prefix, cmd, cwd, env, logFd, state) {
2618
+ writeSync(logFd, `$ ${cmd.join(" ")}
2619
+ `);
2620
+ let child;
2621
+ try {
2622
+ child = Bun.spawn([...prefix, ...cmd], { cwd, env, stdin: "ignore", stdout: "pipe", stderr: "pipe" });
2623
+ } catch (error) {
2624
+ const msg = `boff sanity: cannot run ${cmd[0] ?? ""}: ${error instanceof Error ? error.message : String(error)}
2625
+ `;
2626
+ process.stderr.write(msg);
2627
+ writeSync(logFd, msg);
2628
+ return 127;
2629
+ }
2630
+ state.child = child;
2631
+ await Promise.all([pump(child.stdout, process.stdout, logFd), pump(child.stderr, process.stderr, logFd)]);
2632
+ const rc = await child.exited;
2633
+ state.child = null;
2634
+ return rc;
2635
+ }
2636
+ var HELP4 = `usage: boff sanity [--force] [--only step,...] [--print-keys] [--pre-push]
2637
+
2638
+ --force ignore stamps and run every step (also SANITY_FORCE=1)
2639
+ --only a,b run only these steps (generators they need still run)
2640
+ --print-keys print step=key lines and all=<key> for $GITHUB_OUTPUT, run nothing
2641
+ --pre-push fail instead of running ungated when no build slot frees in time
2642
+ --print-keys / print-keys are identical
2643
+
2644
+ Steps: ${STEP_NAMES.join(", ")} (+ boff.sanity.extra scripts)`;
2645
+ async function runSanity(opts) {
2646
+ const env = opts.env ?? process.env;
2647
+ const w = opts.writer ?? consoleWriter;
2648
+ const args = parseArgs(opts.argv ?? [], ["only"]);
2649
+ if (flagBool(args, "help")) {
2650
+ w.out(HELP4);
2651
+ return 0;
2652
+ }
2653
+ let plan;
2654
+ try {
2655
+ plan = planSanity(opts.root, { env });
2656
+ } catch (error) {
2657
+ w.err(`boff sanity: ${error instanceof Error ? error.message : String(error)}`);
2658
+ return 2;
2659
+ }
2660
+ if (flagBool(args, "print-keys")) {
2661
+ for (const line of printKeys(plan))
2662
+ w.out(line);
2663
+ return 0;
2664
+ }
2665
+ const only = flagString(args, "only")?.split(",").map((s) => s.trim()).filter((s) => s !== "");
2666
+ if (only !== undefined) {
2667
+ const unknown = only.filter((s) => !plan.steps.some((p) => p.name === s));
2668
+ if (unknown.length > 0) {
2669
+ w.err(`boff sanity: --only names steps that are not planned here: ${unknown.join(", ")}`);
2670
+ return 2;
2671
+ }
2672
+ }
2673
+ const selected = plan.steps.filter((s) => only === undefined || only.includes(s.name));
2674
+ const force = flagBool(args, "force") || env.SANITY_FORCE === "1";
2675
+ const missed = new Set(selected.filter((s) => force || !isStampFresh(plan, s)).map((s) => s.name));
2676
+ for (const s of plan.skipped)
2677
+ w.out(`sanity: ${s.name} skipped (${s.reason})`);
2678
+ if (missed.size === 0) {
2679
+ w.out(`sanity: all ${selected.length} steps cached (${selected.map((s) => s.name).join(", ")})`);
2680
+ return 0;
2681
+ }
2682
+ const toRun = new Set(missed);
2683
+ for (const s of plan.steps) {
2684
+ if (!missed.has(s.name))
2685
+ continue;
2686
+ for (const d of s.deps) {
2687
+ const dep = plan.steps.find((p) => p.name === d);
2688
+ if (dep?.generator === true)
2689
+ toRun.add(d);
2690
+ }
2691
+ }
2692
+ const strict = flagBool(args, "pre-push") || env.BOFF_GATE_STRICT === "1";
2693
+ const slot = await acquireSlot({ env, strict, writer: w });
2694
+ if (slot.kind === "timeout")
2695
+ return GATE_TIMEOUT_EXIT;
2696
+ const cores = cpuCount();
2697
+ const threads = threadCount(env, cores);
2698
+ const prefix = slot.kind === "held" || slot.kind === "ungated" ? throttlePrefix() : [];
2699
+ const childEnv = { ...env, ...gateEnv(env, threads, true) };
2700
+ const logPath = join13(opts.root, "sanity.log");
2701
+ const logFd = openSync2(logPath, "w");
2702
+ const state = { child: null, aborted: null };
2703
+ const onSignal = (sig) => {
2704
+ state.aborted = sig;
2705
+ state.child?.kill(sig);
2706
+ };
2707
+ if (opts.handleSignals === true) {
2708
+ process.on("SIGINT", onSignal);
2709
+ process.on("SIGTERM", onSignal);
2710
+ }
2711
+ const rows = [];
2712
+ let exitCode = 0;
2713
+ try {
2714
+ for (const step of plan.steps) {
2715
+ if (!toRun.has(step.name)) {
2716
+ if (selected.includes(step))
2717
+ rows.push([step.name, "cached", "-"]);
2718
+ continue;
2719
+ }
2720
+ w.out(`sanity: ${step.name} (${step.key.slice(0, 12)})`);
2721
+ const started = performance.now();
2722
+ let rc = 0;
2723
+ for (const cmd of step.commands) {
2724
+ rc = await runCommand(prefix, injectThreadFlags(cmd, threads), opts.root, childEnv, logFd, state);
2725
+ if (rc !== 0 || state.aborted !== null)
2726
+ break;
2727
+ }
2728
+ const ms = Math.round(performance.now() - started);
2729
+ appendTiming(plan, { step: step.name, key: step.key, ms, rc });
2730
+ if (state.aborted !== null) {
2731
+ exitCode = state.aborted === "SIGINT" ? 130 : 143;
2732
+ rows.push([step.name, `interrupted (${state.aborted})`, `${ms}ms`]);
2733
+ break;
2734
+ }
2735
+ if (rc !== 0) {
2736
+ exitCode = rc;
2737
+ rows.push([step.name, `FAILED (exit ${rc})`, `${ms}ms`]);
2738
+ break;
2739
+ }
2740
+ writeStamp(plan, step);
2741
+ rows.push([step.name, "passed", `${ms}ms`]);
2742
+ }
2743
+ } finally {
2744
+ closeSync2(logFd);
2745
+ removePartialState(plan);
2746
+ if (slot.kind === "held")
2747
+ slot.lock.release();
2748
+ if (opts.handleSignals === true) {
2749
+ process.off("SIGINT", onSignal);
2750
+ process.off("SIGTERM", onSignal);
2751
+ }
2752
+ }
2753
+ for (const line of renderTable(["step", "result", "time"], rows))
2754
+ w.out(line);
2755
+ if (exitCode !== 0)
2756
+ w.err(`boff sanity: failed; full output in sanity.log`);
2757
+ return exitCode;
2758
+ }
2759
+
2760
+ // src/workflow-policy.ts
2761
+ import { existsSync as existsSync12, statSync as statSync2 } from "fs";
2762
+ import { resolve as resolve2 } from "path";
2763
+ function logicalLines(text) {
2764
+ const out = [];
2765
+ let buf = "";
2766
+ let start = 0;
2767
+ text.split(`
2768
+ `).forEach((raw, i) => {
2769
+ if (/^[ \t]*#/.test(raw) && buf === "")
2770
+ return;
2771
+ let line = raw;
2772
+ if (buf === "")
2773
+ start = i + 1;
2774
+ if (/\\[ \t\r]*$/.test(line)) {
2775
+ line = line.replace(/\\[ \t\r]*$/, " ");
2776
+ buf += line;
2777
+ return;
2778
+ }
2779
+ out.push({ line: start, text: buf + line });
2780
+ buf = "";
2781
+ });
2782
+ if (buf !== "")
2783
+ out.push({ line: start, text: buf });
2784
+ return out;
2785
+ }
2786
+ var CMD_START = "(^|[^A-Za-z0-9_.-])";
2787
+ var USES_RE = /^\s*(-\s+)?uses:\s*(.*)$/;
2788
+ var SHA_REF_RE = /^[^@\s]+@[0-9a-f]{40}$/;
2789
+ var DOCKER_DIGEST_RE = /^docker:\/\/[^@\s]+@sha256:[0-9a-f]{64}$/;
2790
+ var LATEST_RE = /@latest([^A-Za-z0-9_.-]|$)/;
2791
+ var REMOTE_PIPE_RE = /(curl|wget)[^|]*\|\s*(sudo\s+(-\S+\s+)*)?(env\s+)?([A-Za-z0-9_./-]*\/)?((ba|z|da|k)?sh|python[0-9.]*|node|perl|ruby|bun|deno)(\s|$|;|\))/;
2792
+ var REMOTE_SUBST_RE = new RegExp(`${CMD_START}(source|\\.|eval|(ba|z|da|k)?sh(\\s+-[A-Za-z]+)*)\\s+["']?(<\\(|\\$\\()\\s*(curl|wget)`);
2793
+ var EXEC_RE = new RegExp(`${CMD_START}(npx|bunx|npm\\s+exec|pnpm\\s+dlx|yarn\\s+dlx)\\s+`);
2794
+ var BUN_INSTALL_RE = new RegExp(`${CMD_START}bun\\s+(install|i)(\\s|$|;|&|\\|)`);
2795
+ var NPM_INSTALL_RE = new RegExp(`${CMD_START}npm\\s+(install|i|add)(\\s|$|;|&|\\|)`);
2796
+ var PIP_INSTALL_RE = new RegExp(`${CMD_START}pip[0-9.]*\\s+install(\\s|$|;|&|\\|)`);
2797
+ var EXACT_VERSION_RE = /^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.+-]+)?$/;
2798
+ var UNTRUSTED_RE = /\$\{\{\s*(inputs\.|github\.(event|ref|head_ref))/;
2799
+ function commandArgs(re, text) {
2800
+ const out = [];
2801
+ let rest = text;
2802
+ for (;; ) {
2803
+ const m = re.exec(rest);
2804
+ if (m === null)
2805
+ break;
2806
+ const match = m[0];
2807
+ rest = rest.slice(m.index + match.length);
2808
+ if (/[;&|]$/.test(match))
2809
+ out.push("");
2810
+ else
2811
+ out.push(rest.split(/[;&|]/)[0] ?? "");
2812
+ }
2813
+ return out;
2814
+ }
2815
+ function execSpecs(text) {
2816
+ const specs = [];
2817
+ for (const args of commandArgs(EXEC_RE, text)) {
2818
+ let spec = "";
2819
+ for (const token of args.trim().split(/\s+/).filter((t) => t !== "")) {
2820
+ if (token.startsWith("--package=")) {
2821
+ spec = token.slice("--package=".length);
2822
+ break;
2823
+ }
2824
+ if (token.startsWith("-"))
2825
+ continue;
2826
+ spec = token;
2827
+ break;
2828
+ }
2829
+ spec = spec.replace(/^["']/, "").replace(/["']$/, "");
2830
+ if (spec !== "")
2831
+ specs.push(spec);
2832
+ }
2833
+ return specs;
2834
+ }
2835
+ function checkRunInterpolation(file, add) {
2836
+ const msg = "pass dispatch and GitHub context values through env, not directly into run";
2837
+ let keyIndent = -1;
2838
+ file.text.split(`
2839
+ `).forEach((line, i) => {
2840
+ const text = line.replace(/^\s+/, "");
2841
+ const indent = line.length - text.length;
2842
+ const m = /^(-\s+)?run:\s*(.*)$/.exec(text);
2843
+ if (m !== null) {
2844
+ const value = m[2] ?? "";
2845
+ const block = /^[|>][-+0-9]*\s*(#.*)?$/.test(value);
2846
+ keyIndent = block ? indent + (m[1]?.length ?? 0) : -1;
2847
+ if (!block && UNTRUSTED_RE.test(value))
2848
+ add(i + 1, "unsafe-interpolation", msg);
2849
+ return;
2850
+ }
2851
+ if (keyIndent >= 0 && text !== "" && indent <= keyIndent)
2852
+ keyIndent = -1;
2853
+ if (keyIndent >= 0 && UNTRUSTED_RE.test(line))
2854
+ add(i + 1, "unsafe-interpolation", msg);
2855
+ });
2856
+ }
2857
+ function checkFile(file) {
2858
+ const violations = [];
2859
+ const add = (line, rule, message) => {
2860
+ violations.push({ file: file.path, line, rule, message });
2861
+ };
2862
+ for (const { line, text } of logicalLines(file.text)) {
2863
+ const uses = USES_RE.exec(text);
2864
+ if (uses !== null) {
2865
+ const ref = (uses[2] ?? "").replace(/\s+#.*$/, "").trim().replace(/^["']/, "").replace(/["']$/, "");
2866
+ if (ref.startsWith("./")) {} else if (ref.startsWith("docker://")) {
2867
+ if (!DOCKER_DIGEST_RE.test(ref))
2868
+ add(line, "pinned-uses", `'${ref}' must be pinned to an image digest (@sha256:...)`);
2869
+ } else if (!SHA_REF_RE.test(ref)) {
2870
+ add(line, "pinned-uses", `'${ref}' must be pinned to a full 40-character commit SHA`);
2871
+ }
2872
+ }
2873
+ if (LATEST_RE.test(text)) {
2874
+ add(line, "floating-latest", "'@latest' resolves to whatever is published at run time; pin an exact version");
2875
+ }
2876
+ if (REMOTE_PIPE_RE.test(text) || REMOTE_SUBST_RE.test(text)) {
2877
+ add(line, "remote-script", "a downloaded script is executed directly; download a pinned release and verify its checksum first");
2878
+ }
2879
+ if (EXEC_RE.test(text)) {
2880
+ for (const spec of execSpecs(text)) {
2881
+ if (!spec.slice(1).includes("@"))
2882
+ continue;
2883
+ const version = spec.slice(spec.lastIndexOf("@") + 1);
2884
+ if (!EXACT_VERSION_RE.test(version)) {
2885
+ add(line, "exact-exec", `'${spec}' does not name an exact version; pin x.y.z or use the lockfile-installed package`);
2886
+ }
2887
+ }
2888
+ }
2889
+ if (BUN_INSTALL_RE.test(text) && !text.includes("--lockfile-only")) {
2890
+ for (const args of commandArgs(BUN_INSTALL_RE, text)) {
2891
+ if (!` ${args} `.includes(" --frozen-lockfile "))
2892
+ add(line, "frozen-install", "'bun install' must run with --frozen-lockfile");
2893
+ }
2894
+ }
2895
+ if (NPM_INSTALL_RE.test(text)) {
2896
+ add(line, "frozen-install", "use 'npm ci' against the committed lockfile instead of 'npm install'");
2897
+ }
2898
+ if (text.includes("--no-package-lock")) {
2899
+ add(line, "frozen-install", "'--no-package-lock' installs unlocked transitive dependencies");
2900
+ }
2901
+ if (PIP_INSTALL_RE.test(text)) {
2902
+ for (const args of commandArgs(PIP_INSTALL_RE, text)) {
2903
+ if (` ${args} `.includes(" --lockfile-only "))
2904
+ continue;
2905
+ if (!` ${args} `.includes(" --require-hashes ")) {
2906
+ add(line, "hashed-pip", "'pip install' must use --require-hashes with a hash-locked requirements file");
2907
+ }
2908
+ }
2909
+ }
2910
+ }
2911
+ checkRunInterpolation(file, add);
2912
+ return violations;
2913
+ }
2914
+ function checkRepo(root) {
2915
+ const files = [...workflowFiles(root), ...actionFiles(root)];
2916
+ return { files: files.length, violations: files.flatMap(checkFile) };
2917
+ }
2918
+ function workflowPolicyCommand(cwd, argv, w = consoleWriter) {
2919
+ if (argv.includes("--help")) {
2920
+ w.out("usage: boff workflow-policy [repo-root] (default: current directory)");
2921
+ return 0;
2922
+ }
2923
+ const root = resolve2(cwd, argv.find((a) => !a.startsWith("--")) ?? ".");
2924
+ if (!existsSync12(root) || !statSync2(root).isDirectory()) {
2925
+ w.err(`boff workflow-policy: '${root}' is not a directory`);
2926
+ return 2;
2927
+ }
2928
+ const { files, violations } = checkRepo(root);
2929
+ if (files === 0) {
2930
+ w.out(`boff workflow-policy: no workflows or composite actions under ${root}/.github`);
2931
+ return 0;
2932
+ }
2933
+ for (const v of violations) {
2934
+ w.out(`::error file=${v.file},line=${v.line},title=workflow policy (${v.rule})::${v.message}`);
2935
+ }
2936
+ if (violations.length > 0) {
2937
+ w.err(`boff workflow-policy: ${violations.length} violation(s) in ${files} file(s)`);
2938
+ return 1;
2939
+ }
2940
+ w.out(`boff workflow-policy: ${files} file(s) clean`);
2941
+ return 0;
2942
+ }
2943
+
2944
+ // src/cli.ts
2945
+ var USAGE = `boff ${TOOLCHAIN_VERSION} \u2014 Burdenoff toolchain (${TOOLCHAIN_NAME}, Bun ${SUPPORTED_BUN})
2946
+
2947
+ usage: boff <command> [options]
2948
+
2949
+ commands:
2950
+ sanity hash-stamped sanity runner (--force, --only, --print-keys, --pre-push)
2951
+ print-keys same as sanity --print-keys (k=v lines for $GITHUB_OUTPUT)
2952
+ gate -- <cmd> run a heavy command under the machine-wide build semaphore
2953
+ coverage-gate lcov aggregate + diff coverage gate
2954
+ doctor check the repo against the toolchain contract (--ci to fail)
2955
+ sync write or --check the managed blocks (bunfig, lefthook, scripts)
2956
+ deps-mode switch @burdenoff/* deps between registry and local sources
2957
+ workflow-policy check GitHub workflows for supply-chain policy violations
2958
+ version print the toolchain version
2959
+
2960
+ Run boff <command> --help for the options of a command.`;
2961
+ async function main(argv, cwd = process.cwd(), w = consoleWriter) {
2962
+ const [command, ...rest] = argv;
2963
+ switch (command) {
2964
+ case undefined:
2965
+ case "help":
2966
+ case "--help":
2967
+ case "-h":
2968
+ w.out(USAGE);
2969
+ return command === undefined ? 2 : 0;
2970
+ case "version":
2971
+ case "--version":
2972
+ case "-v":
2973
+ w.out(TOOLCHAIN_VERSION);
2974
+ return 0;
2975
+ case "sanity":
2976
+ return runSanity({ root: cwd, argv: rest, writer: w, handleSignals: true });
2977
+ case "print-keys":
2978
+ return runSanity({ root: cwd, argv: ["--print-keys", ...rest], writer: w });
2979
+ case "gate": {
2980
+ const dash = rest.indexOf("--");
2981
+ const opts = dash < 0 ? [] : rest.slice(0, dash);
2982
+ const cmd = dash < 0 ? rest : rest.slice(dash + 1);
2983
+ return runGated(cmd, { cwd, writer: w, strict: opts.includes("--pre-push") });
2984
+ }
2985
+ case "coverage-gate":
2986
+ return coverageGate({ root: cwd, argv: rest, writer: w });
2987
+ case "doctor":
2988
+ return doctorCommand(cwd, rest, w);
2989
+ case "sync":
2990
+ return syncCommand(cwd, rest, w);
2991
+ case "deps-mode":
2992
+ return depsModeCommand(cwd, rest, w);
2993
+ case "workflow-policy":
2994
+ return workflowPolicyCommand(cwd, rest, w);
2995
+ default:
2996
+ w.err(`boff: unknown command "${command}"
2997
+
2998
+ ${USAGE}`);
2999
+ return 2;
3000
+ }
3001
+ }
3002
+ if (import.meta.main) {
3003
+ try {
3004
+ process.exitCode = await main(process.argv.slice(2));
3005
+ } catch (error) {
3006
+ process.stderr.write(`boff: ${error instanceof Error ? error.message : String(error)}
3007
+ `);
3008
+ process.exitCode = 1;
3009
+ }
3010
+ }
3011
+ export {
3012
+ USAGE,
3013
+ main
3014
+ };