@geonosis/ratchet 1.3.0 → 2.0.0
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/README.md +3 -1
- package/dist/{chunk-HBM6F3GO.js → chunk-H4PLCM5J.js} +403 -90
- package/dist/cli.js +26 -2
- package/dist/index.d.ts +105 -2
- package/dist/index.js +13 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -363,11 +363,12 @@ Every counter takes its `command` from the config, so the toolchain stays the re
|
|
|
363
363
|
| `testFailures` | the runner's own failure summary — or, with `report: "vitest-json"`, `numFailedTests` out of the JSON report the command wrote; throws when nothing is readable, when the report is absent, and when the report failed with nothing failing | `command`, `report`, `reportPath` |
|
|
364
364
|
| `unformattedFiles` | paths `--list-different` names that exist on disk | `command` |
|
|
365
365
|
| `cloneCount` | jscpd's `Found N clones` | `command` |
|
|
366
|
-
| `knipIssues` | the
|
|
366
|
+
| `knipIssues` | the total under EVERY heading knip's compact reporter emits — the shape `<Title> (N)`, not a list of titles, so a heading the kit has not heard of is debt rather than a silent zero. `headings` narrows it, and every printed heading the narrowing leaves out must be named under `excusedHeadings` with a reason or the run refuses | `command`, `headings`, `excusedHeadings` |
|
|
367
367
|
| `boundaryIssues` | `N issues found` from a boundary scan | `command` |
|
|
368
368
|
| `archViolations` | lines matching a marker your own architecture scan prints, refusing a non-zero exit that printed none of them — a scan that could not run is not a clean scan | `command`, `match` |
|
|
369
369
|
| `sumOfCounts` | the total of one capture group across a per-file census (`grep -rc`) | `command`, `match` |
|
|
370
370
|
| `lawLineCount` | the lines of the law file — a ceiling that can only come down | `path` |
|
|
371
|
+
| `probelessRules` | the rules an oxlint config ENABLES that the plugin it loads declares no `probe()` for. Those are the ones `geonosis-doctor`'s `exercised` can only answer UNJUDGED about, which #137 makes a WARN naming the kit as owner — and a WARN the kit carries across releases is a downgraded rule by another name. The plugin is imported from the tree it is pointed at, and one that cannot be loaded is a refusal, never a zero | `config`, `plugin` |
|
|
371
372
|
| `runtimeCodeShipped` | 0 when a change shipped runtime code, 1 when it shipped none | `command`, `patterns` |
|
|
372
373
|
| `disabledCiJobs` | lines of `if: false` across the workflow files — a job switched off to get a release through, still off. A condition that merely mentions `false` is not one. No workflows directory at all reads 0 | `dir` |
|
|
373
374
|
| `bundleBytes` | one integer out of whatever your sizing command printed, separators and all; with `match`, the group that pattern names rather than the last integer, refusing when it matches nothing. **Tolerates.** | `command`, `match`, `tolerance` |
|
|
@@ -375,6 +376,7 @@ Every counter takes its `command` from the config, so the toolchain stays the re
|
|
|
375
376
|
| `testsWithoutRunner` | workspaces holding `*.test.*`, `*.spec.*` or `__tests__/` with no `test` script — the suites nobody runs, which read exactly like suites that pass | `script` |
|
|
376
377
|
| `packagesWithoutTypecheck` | workspaces with no `typecheck` script | `script` |
|
|
377
378
|
| `walkFindings` | the defects in the report `geonosis-walk` wrote, over every page; with `classes`, only those classes, refusing a class the walk does not have. A missing or unparsable report is a refusal — the walk writes none when it could not run | `report`, `classes` |
|
|
379
|
+
| `orphanTodos` | debt markers outside the plan graph: one naming no plan, and one naming a plan that is not in the plans directory. Leading zeroes are a spelling, so `(021)` finds `21-…md`. Fixtures, dependencies and build output are not read | `markers`, `plans`, `roots` |
|
|
378
380
|
|
|
379
381
|
### `tolerance`, and the two counters that accept one
|
|
380
382
|
|
|
@@ -1,13 +1,76 @@
|
|
|
1
|
+
// src/core/envelope.ts
|
|
2
|
+
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
3
|
+
import { dirname, join } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
var ENVELOPES_DIR = ".geonosis/envelopes";
|
|
6
|
+
var envelopePath = (root, tool) => join(root, ENVELOPES_DIR, `${tool}.json`);
|
|
7
|
+
var UnbalancedEnvelope = class extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "UnbalancedEnvelope";
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
var isCount = (value) => Number.isSafeInteger(value) && value >= 0;
|
|
14
|
+
var unbalancedMessage = (envelope, next) => `${envelope.tool}: considered ${envelope.considered} but accounts for ${envelope.read + envelope.refused.length + envelope.excused.length} \u2014 ${envelope.read} read + ${envelope.refused.length} refused + ${envelope.excused.length} excused. A run that has lost count of its own inputs cannot say what it measured, so no verdict was rendered and no envelope was written. Next: ${next}`;
|
|
15
|
+
var writeEnvelope = ({
|
|
16
|
+
envelope,
|
|
17
|
+
next,
|
|
18
|
+
root
|
|
19
|
+
}) => {
|
|
20
|
+
if (envelope.tool.trim() === "") {
|
|
21
|
+
throw new UnbalancedEnvelope(
|
|
22
|
+
`an envelope with no tool name cannot be filed or reported against. Next: ${next}`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
if (envelope.version.trim() === "") {
|
|
26
|
+
throw new UnbalancedEnvelope(
|
|
27
|
+
`${envelope.tool}: an envelope that cannot name the build that wrote it dates nothing, and a stale one reads exactly like a fresh one. Next: ${next}`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
if (!isCount(envelope.considered) || !isCount(envelope.read)) {
|
|
31
|
+
throw new UnbalancedEnvelope(
|
|
32
|
+
`${envelope.tool}: considered ${envelope.considered} and read ${envelope.read} \u2014 a census is a whole number of things, and arithmetic over anything else balances by accident. Next: ${next}`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if (envelope.considered !== envelope.read + envelope.refused.length + envelope.excused.length) {
|
|
36
|
+
throw new UnbalancedEnvelope(unbalancedMessage(envelope, next));
|
|
37
|
+
}
|
|
38
|
+
const at = envelopePath(root, envelope.tool);
|
|
39
|
+
mkdirSync(dirname(at), { recursive: true });
|
|
40
|
+
writeFileSync(at, `${JSON.stringify(envelope, void 0, 2)}
|
|
41
|
+
`);
|
|
42
|
+
return at;
|
|
43
|
+
};
|
|
44
|
+
var UNKNOWN = "unknown";
|
|
45
|
+
var versionIn = (dir) => {
|
|
46
|
+
try {
|
|
47
|
+
const manifest = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
48
|
+
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
49
|
+
} catch {
|
|
50
|
+
return void 0;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var versionOf = (moduleUrl) => {
|
|
54
|
+
let dir = dirname(fileURLToPath(moduleUrl));
|
|
55
|
+
for (; ; ) {
|
|
56
|
+
const found = versionIn(dir);
|
|
57
|
+
if (found !== void 0) return found;
|
|
58
|
+
const up = dirname(dir);
|
|
59
|
+
if (up === dir) return UNKNOWN;
|
|
60
|
+
dir = up;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
1
64
|
// src/core/lock.ts
|
|
2
65
|
import { randomUUID } from "crypto";
|
|
3
|
-
import { linkSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
66
|
+
import { linkSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
4
67
|
import { homedir } from "os";
|
|
5
|
-
import { dirname, join } from "path";
|
|
6
|
-
var heavyLockPath = () => process.env.GEONOSIS_HEAVY_LOCK ??
|
|
68
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
69
|
+
var heavyLockPath = () => process.env.GEONOSIS_HEAVY_LOCK ?? join2(homedir(), ".cache", "geonosis", "heavy.lock");
|
|
7
70
|
var sleep = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
8
71
|
var holderOf = (path) => {
|
|
9
72
|
try {
|
|
10
|
-
const parsed = JSON.parse(
|
|
73
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
11
74
|
return typeof parsed.pid === "number" ? {
|
|
12
75
|
cwd: parsed.cwd ?? "somewhere",
|
|
13
76
|
pid: parsed.pid,
|
|
@@ -31,7 +94,7 @@ var heldFor = (holder) => {
|
|
|
31
94
|
return `${Math.round((Date.now() - since) / 1e3)}s`;
|
|
32
95
|
};
|
|
33
96
|
var write = (path) => {
|
|
34
|
-
|
|
97
|
+
mkdirSync2(dirname2(path), { recursive: true });
|
|
35
98
|
const mine = {
|
|
36
99
|
cwd: process.cwd(),
|
|
37
100
|
pid: process.pid,
|
|
@@ -39,7 +102,7 @@ var write = (path) => {
|
|
|
39
102
|
};
|
|
40
103
|
const staging = `${path}.${process.pid}.${randomUUID()}`;
|
|
41
104
|
try {
|
|
42
|
-
|
|
105
|
+
writeFileSync2(staging, JSON.stringify(mine));
|
|
43
106
|
linkSync(staging, path);
|
|
44
107
|
return true;
|
|
45
108
|
} catch (error) {
|
|
@@ -89,7 +152,7 @@ var acquireExclusive = async ({
|
|
|
89
152
|
};
|
|
90
153
|
|
|
91
154
|
// src/core/config.ts
|
|
92
|
-
import { existsSync, readFileSync as
|
|
155
|
+
import { existsSync, readFileSync as readFileSync3 } from "fs";
|
|
93
156
|
import { resolve } from "path";
|
|
94
157
|
var CONFIG_FILE = "geonosis.ratchet.json";
|
|
95
158
|
var keyOf = (entry) => entry.key ?? entry.counter;
|
|
@@ -98,7 +161,7 @@ var loadConfig = (cwd) => {
|
|
|
98
161
|
if (!existsSync(path)) {
|
|
99
162
|
throw new Error(`no ${CONFIG_FILE} in ${cwd} \u2014 the ratchet has nothing to count`);
|
|
100
163
|
}
|
|
101
|
-
const parsed = JSON.parse(
|
|
164
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
102
165
|
if (!Array.isArray(parsed.counters)) {
|
|
103
166
|
throw new Error(`${CONFIG_FILE} has no "counters" array`);
|
|
104
167
|
}
|
|
@@ -126,7 +189,7 @@ var loadConfig = (cwd) => {
|
|
|
126
189
|
};
|
|
127
190
|
var resolveBaseline = (cwd) => {
|
|
128
191
|
try {
|
|
129
|
-
const parsed = JSON.parse(
|
|
192
|
+
const parsed = JSON.parse(readFileSync3(resolve(cwd, CONFIG_FILE), "utf8"));
|
|
130
193
|
return typeof parsed.baseline === "string" ? parsed.baseline : "gate-baseline.json";
|
|
131
194
|
} catch {
|
|
132
195
|
return "gate-baseline.json";
|
|
@@ -172,13 +235,13 @@ ${output.trim()}`
|
|
|
172
235
|
};
|
|
173
236
|
|
|
174
237
|
// src/core/prove.ts
|
|
175
|
-
import { existsSync as existsSync2, mkdtempSync, rmSync as rmSync2 } from "fs";
|
|
238
|
+
import { existsSync as existsSync2, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
176
239
|
import { tmpdir } from "os";
|
|
177
|
-
import { delimiter, dirname as
|
|
240
|
+
import { delimiter, dirname as dirname4, join as join3, resolve as resolve3 } from "path";
|
|
178
241
|
|
|
179
242
|
// src/core/exclusive.ts
|
|
180
243
|
import { spawn } from "child_process";
|
|
181
|
-
import { dirname as
|
|
244
|
+
import { dirname as dirname3, resolve as resolve2 } from "path";
|
|
182
245
|
var MARK = /^exclusive-hold (start|end) (\d+)$/gm;
|
|
183
246
|
var KEY = "exclusive";
|
|
184
247
|
var spanOf = (output) => {
|
|
@@ -193,7 +256,7 @@ var hold = (cli, holdMs, lockPath) => new Promise((done) => {
|
|
|
193
256
|
process.execPath,
|
|
194
257
|
[cli, "--exclusive", "--exclusive-timeout", "60", "--hold", String(holdMs)],
|
|
195
258
|
{
|
|
196
|
-
cwd:
|
|
259
|
+
cwd: dirname3(resolve2(cli)),
|
|
197
260
|
env: { ...process.env, GEONOSIS_HEAVY_LOCK: lockPath },
|
|
198
261
|
stdio: ["ignore", "pipe", "pipe"]
|
|
199
262
|
}
|
|
@@ -245,9 +308,9 @@ var toolPath = (cwd) => {
|
|
|
245
308
|
const dirs = [];
|
|
246
309
|
let dir = resolve3(cwd);
|
|
247
310
|
for (; ; ) {
|
|
248
|
-
const bin =
|
|
311
|
+
const bin = join3(dir, "node_modules", ".bin");
|
|
249
312
|
if (existsSync2(bin)) dirs.push(bin);
|
|
250
|
-
const parent =
|
|
313
|
+
const parent = dirname4(dir);
|
|
251
314
|
if (parent === dir) break;
|
|
252
315
|
dir = parent;
|
|
253
316
|
}
|
|
@@ -255,7 +318,7 @@ var toolPath = (cwd) => {
|
|
|
255
318
|
};
|
|
256
319
|
var NO_PROBE = "no probe \u2014 a counter nobody has seen read a planted finding has not been shown to measure";
|
|
257
320
|
var oneProofOf = async (counter, key, path, probe) => {
|
|
258
|
-
const dir = mkdtempSync(
|
|
321
|
+
const dir = mkdtempSync(join3(tmpdir(), "geonosis-prove-"));
|
|
259
322
|
try {
|
|
260
323
|
probe.input(dir);
|
|
261
324
|
const command = probe.command?.(dir);
|
|
@@ -322,11 +385,43 @@ var runProve = async ({
|
|
|
322
385
|
if (taken.some((one) => one.verdict !== "proven" && one.verdict !== "skipped")) {
|
|
323
386
|
return { proofs, proven: false };
|
|
324
387
|
}
|
|
388
|
+
const customized = (counter.readingParams ?? []).filter((one) => entry[one] !== void 0);
|
|
389
|
+
if (customized.length > 0) {
|
|
390
|
+
const declared = entry.probe;
|
|
391
|
+
const named = customized.map((one) => `${one} ${JSON.stringify(entry[one])}`).join(", ");
|
|
392
|
+
if (declared === void 0 || typeof declared.sample !== "string" || typeof declared.expect !== "number") {
|
|
393
|
+
proofs.push({
|
|
394
|
+
counter: counter.id,
|
|
395
|
+
key,
|
|
396
|
+
reason: `the configured ${named} is not exercised by this counter's shipped probe \u2014 declare probe: { sample, expect } beside it, a sample this reading must count`,
|
|
397
|
+
verdict: "unproven"
|
|
398
|
+
});
|
|
399
|
+
return { proofs, proven: false };
|
|
400
|
+
}
|
|
401
|
+
const sample = declared.sample;
|
|
402
|
+
const entryProbe = {
|
|
403
|
+
command: () => "cat sample.txt",
|
|
404
|
+
expect: declared.expect,
|
|
405
|
+
input: (dir) => writeFileSync3(join3(dir, "sample.txt"), sample),
|
|
406
|
+
name: "configured reading",
|
|
407
|
+
params: Object.fromEntries(customized.map((one) => [one, entry[one]]))
|
|
408
|
+
};
|
|
409
|
+
const proof = await oneProofOf(counter, key, path, entryProbe);
|
|
410
|
+
const said = proof.verdict === "cannot-fail" ? {
|
|
411
|
+
counter: counter.id,
|
|
412
|
+
expected: declared.expect,
|
|
413
|
+
key,
|
|
414
|
+
reading: 0,
|
|
415
|
+
verdict: "misread"
|
|
416
|
+
} : proof;
|
|
417
|
+
proofs.push(said);
|
|
418
|
+
if (said.verdict !== "proven") return { proofs, proven: false };
|
|
419
|
+
}
|
|
325
420
|
}
|
|
326
421
|
if (exclusiveVia !== void 0) {
|
|
327
|
-
const dir = mkdtempSync(
|
|
422
|
+
const dir = mkdtempSync(join3(tmpdir(), "geonosis-lock-"));
|
|
328
423
|
try {
|
|
329
|
-
const proof = await proveExclusive({ cli: exclusiveVia, lockPath:
|
|
424
|
+
const proof = await proveExclusive({ cli: exclusiveVia, lockPath: join3(dir, "heavy.lock") });
|
|
330
425
|
proofs.push(proof);
|
|
331
426
|
if (proof.verdict !== "serialised") return { proofs, proven: false };
|
|
332
427
|
} finally {
|
|
@@ -337,20 +432,23 @@ var runProve = async ({
|
|
|
337
432
|
};
|
|
338
433
|
|
|
339
434
|
// src/core/ratchet.ts
|
|
340
|
-
import { existsSync as existsSync3, readFileSync as
|
|
435
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
341
436
|
import { resolve as resolve4 } from "path";
|
|
342
437
|
var EVIDENCE_LINES = 10;
|
|
343
438
|
var recorded = (counter, params, run) => {
|
|
344
439
|
let output = "";
|
|
440
|
+
let lastCommand;
|
|
345
441
|
const trimmed = (lines) => lines.map((line) => line.trimEnd()).filter((line) => line !== "").slice(-EVIDENCE_LINES);
|
|
346
442
|
return {
|
|
347
443
|
cited: () => {
|
|
348
444
|
const counted = counter.evidence === void 0 ? [] : counter.evidence({ output, params });
|
|
349
445
|
return trimmed(counted.length > 0 ? counted : output.split("\n"));
|
|
350
446
|
},
|
|
447
|
+
lastCommand: () => lastCommand,
|
|
351
448
|
run: (command) => {
|
|
352
449
|
const result = run(command);
|
|
353
450
|
output = result.output;
|
|
451
|
+
lastCommand = command;
|
|
354
452
|
return result;
|
|
355
453
|
}
|
|
356
454
|
};
|
|
@@ -386,9 +484,10 @@ var runRatchet = async ({
|
|
|
386
484
|
if (!existsSync3(baselinePath)) {
|
|
387
485
|
throw new Error(`no ${config.baseline} in ${cwd} \u2014 nothing to ratchet against`);
|
|
388
486
|
}
|
|
389
|
-
const baseline = JSON.parse(
|
|
487
|
+
const baseline = JSON.parse(readFileSync4(baselinePath, "utf8"));
|
|
390
488
|
const byId = new Map(counters.map((one) => [one.id, one]));
|
|
391
489
|
const measurements = [];
|
|
490
|
+
const refusals = [];
|
|
392
491
|
for (const entry of config.counters) {
|
|
393
492
|
const counter = byId.get(entry.counter);
|
|
394
493
|
if (counter === void 0) {
|
|
@@ -409,7 +508,17 @@ var runRatchet = async ({
|
|
|
409
508
|
}
|
|
410
509
|
const tolerance = toleranceOf(entry, key, counter);
|
|
411
510
|
const recorder = recorded(counter, entry, runCommand(cwd, entry.counter));
|
|
412
|
-
|
|
511
|
+
let now;
|
|
512
|
+
try {
|
|
513
|
+
now = await counter.run({ cwd, key, params: entry, run: recorder.run });
|
|
514
|
+
} catch (error) {
|
|
515
|
+
const ran = recorder.lastCommand();
|
|
516
|
+
refusals.push({
|
|
517
|
+
key,
|
|
518
|
+
reason: `${error.message}${ran === void 0 ? "" : `. Next: run \`${ran}\` in ${cwd} and read what it prints`}`
|
|
519
|
+
});
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
413
522
|
const verdict = verdictOf(now, limit, tolerance);
|
|
414
523
|
measurements.push({
|
|
415
524
|
baseline: limit,
|
|
@@ -421,16 +530,16 @@ var runRatchet = async ({
|
|
|
421
530
|
}
|
|
422
531
|
const grew = measurements.some((one) => one.verdict === "grew");
|
|
423
532
|
const shrank = measurements.some((one) => one.verdict === "shrank");
|
|
424
|
-
if (shrank && !grew) {
|
|
533
|
+
if (shrank && !grew && refusals.length === 0) {
|
|
425
534
|
const next = { ...baseline };
|
|
426
535
|
for (const one of measurements) {
|
|
427
536
|
if (one.verdict === "shrank") next[one.key] = one.now;
|
|
428
537
|
}
|
|
429
|
-
|
|
538
|
+
writeFileSync4(baselinePath, `${JSON.stringify(next, null, 2)}
|
|
430
539
|
`);
|
|
431
|
-
return { measurements, rewritten: true };
|
|
540
|
+
return { considered: config.counters.length, measurements, refusals, rewritten: true };
|
|
432
541
|
}
|
|
433
|
-
return { measurements, rewritten: false };
|
|
542
|
+
return { considered: config.counters.length, measurements, refusals, rewritten: false };
|
|
434
543
|
};
|
|
435
544
|
|
|
436
545
|
// src/counters/params.ts
|
|
@@ -449,12 +558,12 @@ var countMatches = (text, pattern) => text.match(new RegExp(pattern.source, `${p
|
|
|
449
558
|
var escapeForRegex = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
450
559
|
|
|
451
560
|
// src/counters/plant.ts
|
|
452
|
-
import { mkdirSync as
|
|
453
|
-
import { dirname as
|
|
561
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
562
|
+
import { dirname as dirname5, join as join4 } from "path";
|
|
454
563
|
var plant = (dir, relative, contents) => {
|
|
455
|
-
const path =
|
|
456
|
-
|
|
457
|
-
|
|
564
|
+
const path = join4(dir, relative);
|
|
565
|
+
mkdirSync3(dirname5(path), { recursive: true });
|
|
566
|
+
writeFileSync5(path, contents);
|
|
458
567
|
};
|
|
459
568
|
var captured = (sample) => ({
|
|
460
569
|
command: () => "cat sample.txt",
|
|
@@ -539,8 +648,8 @@ var bundleBytes = {
|
|
|
539
648
|
};
|
|
540
649
|
|
|
541
650
|
// src/counters/ci.ts
|
|
542
|
-
import { readdirSync, readFileSync as
|
|
543
|
-
import { join as
|
|
651
|
+
import { readdirSync, readFileSync as readFileSync5 } from "fs";
|
|
652
|
+
import { join as join5, resolve as resolve5 } from "path";
|
|
544
653
|
var DISABLED = /\bif:[ \t]*false\b/;
|
|
545
654
|
var WORKFLOW = /\.ya?ml$/;
|
|
546
655
|
var disabledCiJobs = {
|
|
@@ -558,7 +667,7 @@ var disabledCiJobs = {
|
|
|
558
667
|
const dir = resolve5(cwd, relative);
|
|
559
668
|
let files;
|
|
560
669
|
try {
|
|
561
|
-
files = readdirSync(dir, { withFileTypes: true }).filter((entry) => WORKFLOW.test(entry.name)).map((entry) =>
|
|
670
|
+
files = readdirSync(dir, { withFileTypes: true }).filter((entry) => WORKFLOW.test(entry.name)).map((entry) => join5(dir, entry.name));
|
|
562
671
|
} catch {
|
|
563
672
|
return 0;
|
|
564
673
|
}
|
|
@@ -566,7 +675,7 @@ var disabledCiJobs = {
|
|
|
566
675
|
for (const file of files) {
|
|
567
676
|
let contents;
|
|
568
677
|
try {
|
|
569
|
-
contents =
|
|
678
|
+
contents = readFileSync5(file, "utf8");
|
|
570
679
|
} catch (error) {
|
|
571
680
|
throw new CounterError(
|
|
572
681
|
"disabledCiJobs",
|
|
@@ -589,29 +698,49 @@ var cloneCount = {
|
|
|
589
698
|
const output = run(command).output;
|
|
590
699
|
const found = output.match(CLONES)?.[1];
|
|
591
700
|
if (found === void 0) {
|
|
701
|
+
const said = output.trim();
|
|
592
702
|
throw new CounterError(
|
|
593
703
|
"cloneCount",
|
|
594
|
-
`no "Found N clones" line:
|
|
595
|
-
${
|
|
704
|
+
said === "" ? 'the command printed nothing at all, so there is no "Found N clones" line to read and no output to show \u2014 it is not installed, or it never ran' : `no "Found N clones" line in what it printed:
|
|
705
|
+
${said.slice(-500)}`
|
|
596
706
|
);
|
|
597
707
|
}
|
|
598
708
|
return Number(found);
|
|
599
709
|
}
|
|
600
710
|
};
|
|
601
|
-
var
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
"
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
711
|
+
var HEADING = /^([A-Z][A-Za-z ]+?) \((\d+)\)\s*$/gm;
|
|
712
|
+
var headingsIn = (output) => {
|
|
713
|
+
const found = /* @__PURE__ */ new Map();
|
|
714
|
+
for (const [, heading = "", count = "0"] of output.matchAll(HEADING)) {
|
|
715
|
+
found.set(heading, Number(count));
|
|
716
|
+
}
|
|
717
|
+
return found;
|
|
718
|
+
};
|
|
719
|
+
var excusesIn = (params) => {
|
|
720
|
+
const declared = params["excusedHeadings"];
|
|
721
|
+
return new Set(
|
|
722
|
+
typeof declared === "object" && declared !== null && !Array.isArray(declared) ? Object.keys(declared) : []
|
|
723
|
+
);
|
|
724
|
+
};
|
|
608
725
|
var knipIssues = {
|
|
609
726
|
id: "knipIssues",
|
|
610
727
|
probe: { ...captured("Unused files (1)\nsrc/gone.ts\n"), expect: 1 },
|
|
728
|
+
readingParams: ["headings"],
|
|
611
729
|
run: async ({ params, run }) => {
|
|
612
730
|
const command = stringParam("knipIssues", params, "command", "npx knip --reporter compact");
|
|
613
|
-
const
|
|
614
|
-
|
|
731
|
+
const printed = headingsIn(run(command).output);
|
|
732
|
+
const counted = stringsParam(params, "headings", [...printed.keys()]);
|
|
733
|
+
const excused = excusesIn(params);
|
|
734
|
+
const dropped = [...printed.entries()].filter(
|
|
735
|
+
([heading, count]) => count > 0 && !counted.includes(heading) && !excused.has(heading)
|
|
736
|
+
);
|
|
737
|
+
if (dropped.length > 0) {
|
|
738
|
+
throw new CounterError(
|
|
739
|
+
"knipIssues",
|
|
740
|
+
`knip printed ${dropped.map(([heading, count]) => `${heading} (${count})`).join(", ")} and the configured "headings" counts ${counted.join(", ")} \u2014 three ways out: add each to "headings", name it under "excusedHeadings" with the reason this repo does not hold it, or remove "headings" altogether and count every heading knip prints`
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
return counted.reduce((sum, heading) => sum + (printed.get(heading) ?? 0), 0);
|
|
615
744
|
}
|
|
616
745
|
};
|
|
617
746
|
var ISSUES = /(\d+) issues? found/;
|
|
@@ -646,7 +775,7 @@ var unformattedFiles = {
|
|
|
646
775
|
};
|
|
647
776
|
|
|
648
777
|
// src/counters/gate-report.ts
|
|
649
|
-
import { existsSync as existsSync5, readFileSync as
|
|
778
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
|
|
650
779
|
import { resolve as resolve7 } from "path";
|
|
651
780
|
var DEFAULT_REPORT = ".geonosis/gate-report.json";
|
|
652
781
|
var fastTierMs = {
|
|
@@ -680,7 +809,7 @@ var fastTierMs = {
|
|
|
680
809
|
}
|
|
681
810
|
let report;
|
|
682
811
|
try {
|
|
683
|
-
report = JSON.parse(
|
|
812
|
+
report = JSON.parse(readFileSync6(path, "utf8"));
|
|
684
813
|
} catch (error) {
|
|
685
814
|
throw new CounterError(
|
|
686
815
|
"fastTierMs",
|
|
@@ -702,7 +831,7 @@ var fastTierMs = {
|
|
|
702
831
|
};
|
|
703
832
|
|
|
704
833
|
// src/counters/law.ts
|
|
705
|
-
import { existsSync as existsSync6, readFileSync as
|
|
834
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
|
|
706
835
|
import { resolve as resolve8 } from "path";
|
|
707
836
|
var lawLineCount = {
|
|
708
837
|
id: "lawLineCount",
|
|
@@ -715,12 +844,12 @@ var lawLineCount = {
|
|
|
715
844
|
const relative = stringParam("lawLineCount", params, "path", "CLAUDE.md");
|
|
716
845
|
const path = resolve8(cwd, relative);
|
|
717
846
|
if (!existsSync6(path)) throw new CounterError("lawLineCount", `no law file at ${relative}`);
|
|
718
|
-
return
|
|
847
|
+
return readFileSync7(path, "utf8").replace(/\n$/, "").split("\n").length;
|
|
719
848
|
}
|
|
720
849
|
};
|
|
721
850
|
|
|
722
851
|
// src/counters/oxlint.ts
|
|
723
|
-
import { existsSync as existsSync7, readFileSync as
|
|
852
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
724
853
|
import { resolve as resolve9 } from "path";
|
|
725
854
|
var DEFAULT_COMMAND = "npx oxlint --format=unix --config .oxlintrc.json .";
|
|
726
855
|
var PROBE_COMMAND = "oxlint --format=unix --config .oxlintrc.json .";
|
|
@@ -887,11 +1016,11 @@ var oxlintRule = {
|
|
|
887
1016
|
const source = resolve9(cwd, config);
|
|
888
1017
|
if (!existsSync7(source)) throw new CounterError("oxlintRule", `no config at ${config}`);
|
|
889
1018
|
const strictName = `.oxlintrc.ratchet-${key}.json`;
|
|
890
|
-
const strict =
|
|
1019
|
+
const strict = readFileSync8(source, "utf8").replace(
|
|
891
1020
|
new RegExp(`("[^"]*${escapeForRegex(rule)}"\\s*:\\s*\\[?\\s*)"(warn|off)"`),
|
|
892
1021
|
'$1"error"'
|
|
893
1022
|
);
|
|
894
|
-
|
|
1023
|
+
writeFileSync6(resolve9(cwd, strictName), strict);
|
|
895
1024
|
try {
|
|
896
1025
|
return countRule(run(command.replace("{config}", strictName)), rule, expect);
|
|
897
1026
|
} finally {
|
|
@@ -900,6 +1029,100 @@ var oxlintRule = {
|
|
|
900
1029
|
}
|
|
901
1030
|
};
|
|
902
1031
|
|
|
1032
|
+
// src/counters/probes.ts
|
|
1033
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9 } from "fs";
|
|
1034
|
+
import { createRequire } from "module";
|
|
1035
|
+
import { join as join6, resolve as resolve10 } from "path";
|
|
1036
|
+
import { pathToFileURL } from "url";
|
|
1037
|
+
var ID = "probelessRules";
|
|
1038
|
+
var OFF = /* @__PURE__ */ new Set([0, "0", "allow", "off", false]);
|
|
1039
|
+
var severityOf2 = (level) => Array.isArray(level) ? level[0] : level;
|
|
1040
|
+
var enabledIn = (config, namespace) => {
|
|
1041
|
+
const on = /* @__PURE__ */ new Set();
|
|
1042
|
+
for (const block of [
|
|
1043
|
+
config.rules ?? {},
|
|
1044
|
+
...(config.overrides ?? []).map((one) => one.rules ?? {})
|
|
1045
|
+
]) {
|
|
1046
|
+
for (const [id, level] of Object.entries(block)) {
|
|
1047
|
+
if (id.startsWith(`${namespace}/`) && !OFF.has(severityOf2(level))) {
|
|
1048
|
+
on.add(id.slice(namespace.length + 1));
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
return [...on].toSorted();
|
|
1053
|
+
};
|
|
1054
|
+
var probedBy = async (cwd, plugin) => {
|
|
1055
|
+
let entry;
|
|
1056
|
+
try {
|
|
1057
|
+
entry = createRequire(join6(cwd, "noop.js")).resolve(plugin);
|
|
1058
|
+
} catch (error) {
|
|
1059
|
+
throw new CounterError(
|
|
1060
|
+
ID,
|
|
1061
|
+
`could not resolve ${plugin} from ${cwd} \u2014 ${String(error.message).split("\n")[0]}. A plugin nobody can load says nothing about which rules ship a probe, and it is not zero of them`
|
|
1062
|
+
);
|
|
1063
|
+
}
|
|
1064
|
+
let loaded;
|
|
1065
|
+
try {
|
|
1066
|
+
loaded = await import(pathToFileURL(entry).href);
|
|
1067
|
+
} catch (error) {
|
|
1068
|
+
throw new CounterError(ID, `could not import ${entry} \u2014 ${error.message}`);
|
|
1069
|
+
}
|
|
1070
|
+
const namespace = loaded.default?.meta?.name;
|
|
1071
|
+
if (typeof namespace !== "string" || namespace === "") {
|
|
1072
|
+
throw new CounterError(
|
|
1073
|
+
ID,
|
|
1074
|
+
`${plugin} declares no meta.name, so nothing says which rule ids in the config are its own`
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1077
|
+
return {
|
|
1078
|
+
namespace,
|
|
1079
|
+
probed: new Set(
|
|
1080
|
+
Object.entries(loaded.default?.rules ?? {}).filter(([, rule]) => typeof rule?.probe === "function").map(([name]) => name)
|
|
1081
|
+
)
|
|
1082
|
+
};
|
|
1083
|
+
};
|
|
1084
|
+
var PLUGIN = "@geonosis/oxlint-plugin-biological-architecture";
|
|
1085
|
+
var probelessRules = {
|
|
1086
|
+
id: ID,
|
|
1087
|
+
probe: {
|
|
1088
|
+
expect: 1,
|
|
1089
|
+
input: (dir) => {
|
|
1090
|
+
plant(
|
|
1091
|
+
dir,
|
|
1092
|
+
".oxlintrc.json",
|
|
1093
|
+
JSON.stringify({
|
|
1094
|
+
jsPlugins: ["probe-plugin"],
|
|
1095
|
+
rules: { "x/probed": "error", "x/bare": "error" }
|
|
1096
|
+
})
|
|
1097
|
+
);
|
|
1098
|
+
plant(
|
|
1099
|
+
dir,
|
|
1100
|
+
"node_modules/probe-plugin/package.json",
|
|
1101
|
+
'{"name":"probe-plugin","main":"index.js"}'
|
|
1102
|
+
);
|
|
1103
|
+
plant(
|
|
1104
|
+
dir,
|
|
1105
|
+
"node_modules/probe-plugin/index.js",
|
|
1106
|
+
"export default { meta: { name: 'x' }, rules: { probed: { probe: () => [], create: () => ({}) }, bare: { create: () => ({}) } } }\n"
|
|
1107
|
+
);
|
|
1108
|
+
},
|
|
1109
|
+
params: { plugin: "probe-plugin" }
|
|
1110
|
+
},
|
|
1111
|
+
run: async ({ cwd, params }) => {
|
|
1112
|
+
const relative = stringParam(ID, params, "config", ".oxlintrc.json");
|
|
1113
|
+
const path = resolve10(cwd, relative);
|
|
1114
|
+
if (!existsSync8(path)) throw new CounterError(ID, `no oxlint config at ${relative}`);
|
|
1115
|
+
let config;
|
|
1116
|
+
try {
|
|
1117
|
+
config = JSON.parse(readFileSync9(path, "utf8"));
|
|
1118
|
+
} catch (error) {
|
|
1119
|
+
throw new CounterError(ID, `${relative} does not parse: ${error.message}`);
|
|
1120
|
+
}
|
|
1121
|
+
const { namespace, probed } = await probedBy(cwd, stringParam(ID, params, "plugin", PLUGIN));
|
|
1122
|
+
return enabledIn(config, namespace).filter((rule) => !probed.has(rule)).length;
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
|
|
903
1126
|
// src/counters/runtime-code.ts
|
|
904
1127
|
var DEFAULT_PATTERNS = ["^(apps|packages)/[^/]+/src/"];
|
|
905
1128
|
var NOT_RUNTIME = /(\.test\.|\.spec\.|__tests__\/|__fixtures__\/|\.d\.ts$)/;
|
|
@@ -925,15 +1148,15 @@ var runtimeCodeShipped = {
|
|
|
925
1148
|
|
|
926
1149
|
// src/counters/scripts.ts
|
|
927
1150
|
import { readdirSync as readdirSync3 } from "fs";
|
|
928
|
-
import { join as
|
|
1151
|
+
import { join as join8 } from "path";
|
|
929
1152
|
|
|
930
1153
|
// src/counters/workspace.ts
|
|
931
|
-
import { existsSync as
|
|
932
|
-
import { join as
|
|
1154
|
+
import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync10 } from "fs";
|
|
1155
|
+
import { join as join7, resolve as resolve11 } from "path";
|
|
933
1156
|
var SKIP = /^(node_modules|\.)/;
|
|
934
1157
|
var childDirs = (dir) => {
|
|
935
1158
|
try {
|
|
936
|
-
return readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !SKIP.test(entry.name)).map((entry) =>
|
|
1159
|
+
return readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !SKIP.test(entry.name)).map((entry) => join7(dir, entry.name));
|
|
937
1160
|
} catch {
|
|
938
1161
|
return [];
|
|
939
1162
|
}
|
|
@@ -943,14 +1166,14 @@ var expand = (root, pattern) => {
|
|
|
943
1166
|
const segments = pattern.split("/").filter((one) => one !== "" && one !== ".");
|
|
944
1167
|
let dirs = [root];
|
|
945
1168
|
for (const segment of segments) {
|
|
946
|
-
dirs = segment === "*" ? dirs.flatMap((dir) => childDirs(dir)) : segment === "**" ? dirs.flatMap((dir) => descendants(dir, 3)) : dirs.map((dir) =>
|
|
1169
|
+
dirs = segment === "*" ? dirs.flatMap((dir) => childDirs(dir)) : segment === "**" ? dirs.flatMap((dir) => descendants(dir, 3)) : dirs.map((dir) => join7(dir, segment)).filter((dir) => existsSync9(dir));
|
|
947
1170
|
}
|
|
948
1171
|
return dirs;
|
|
949
1172
|
};
|
|
950
1173
|
var QUOTED = /^['"]|['"]$/g;
|
|
951
1174
|
var cleaned = (value) => value.replace(/#.*$/, "").trim().replaceAll(QUOTED, "");
|
|
952
1175
|
var pnpmPatterns = (path) => {
|
|
953
|
-
const lines =
|
|
1176
|
+
const lines = readFileSync10(path, "utf8").split("\n");
|
|
954
1177
|
const at = lines.findIndex((line) => line.startsWith("packages:"));
|
|
955
1178
|
if (at === -1) return [];
|
|
956
1179
|
const inline = lines[at]?.slice("packages:".length).trim() ?? "";
|
|
@@ -969,31 +1192,31 @@ var pnpmPatterns = (path) => {
|
|
|
969
1192
|
return patterns;
|
|
970
1193
|
};
|
|
971
1194
|
var npmPatterns = (path) => {
|
|
972
|
-
const parsed = JSON.parse(
|
|
1195
|
+
const parsed = JSON.parse(readFileSync10(path, "utf8"));
|
|
973
1196
|
const declared = Array.isArray(parsed.workspaces) ? parsed.workspaces : parsed.workspaces?.packages ?? [];
|
|
974
1197
|
return declared.filter((one) => typeof one === "string");
|
|
975
1198
|
};
|
|
976
1199
|
var nameOf = (dir) => {
|
|
977
|
-
const manifest =
|
|
978
|
-
if (!
|
|
1200
|
+
const manifest = join7(dir, "package.json");
|
|
1201
|
+
if (!existsSync9(manifest)) return void 0;
|
|
979
1202
|
try {
|
|
980
|
-
const { name } = JSON.parse(
|
|
1203
|
+
const { name } = JSON.parse(readFileSync10(manifest, "utf8"));
|
|
981
1204
|
return typeof name === "string" && name !== "" ? name : void 0;
|
|
982
1205
|
} catch {
|
|
983
1206
|
return void 0;
|
|
984
1207
|
}
|
|
985
1208
|
};
|
|
986
1209
|
var workspaceDirs = (cwd) => {
|
|
987
|
-
const root =
|
|
988
|
-
const pnpm =
|
|
989
|
-
const manifest =
|
|
990
|
-
const patterns =
|
|
991
|
-
const dirs = patterns.filter((pattern) => !pattern.startsWith("!")).flatMap((pattern) => expand(root, pattern)).filter((dir) =>
|
|
1210
|
+
const root = resolve11(cwd);
|
|
1211
|
+
const pnpm = join7(root, "pnpm-workspace.yaml");
|
|
1212
|
+
const manifest = join7(root, "package.json");
|
|
1213
|
+
const patterns = existsSync9(pnpm) ? pnpmPatterns(pnpm) : existsSync9(manifest) ? npmPatterns(manifest) : [];
|
|
1214
|
+
const dirs = patterns.filter((pattern) => !pattern.startsWith("!")).flatMap((pattern) => expand(root, pattern)).filter((dir) => existsSync9(join7(dir, "package.json")));
|
|
992
1215
|
return [...new Set(dirs)];
|
|
993
1216
|
};
|
|
994
1217
|
var manifestOf = (dir) => {
|
|
995
1218
|
try {
|
|
996
|
-
const parsed = JSON.parse(
|
|
1219
|
+
const parsed = JSON.parse(readFileSync10(join7(dir, "package.json"), "utf8"));
|
|
997
1220
|
return typeof parsed === "object" && parsed !== null ? parsed : void 0;
|
|
998
1221
|
} catch {
|
|
999
1222
|
return void 0;
|
|
@@ -1023,7 +1246,7 @@ var holdsTests = (dir, depth = 6) => {
|
|
|
1023
1246
|
if (entry.isDirectory()) {
|
|
1024
1247
|
if (entry.name === TEST_DIR) return true;
|
|
1025
1248
|
if (SKIP2.test(entry.name) || depth === 0) continue;
|
|
1026
|
-
if (holdsTests(
|
|
1249
|
+
if (holdsTests(join8(dir, entry.name), depth - 1)) return true;
|
|
1027
1250
|
continue;
|
|
1028
1251
|
}
|
|
1029
1252
|
if (TEST_FILE.test(entry.name)) return true;
|
|
@@ -1064,16 +1287,26 @@ var packagesWithoutTypecheck = {
|
|
|
1064
1287
|
var sumOfCounts = {
|
|
1065
1288
|
id: "sumOfCounts",
|
|
1066
1289
|
probe: { ...captured("src/a.ts:1\nsrc/b.ts:0\n"), expect: 1 },
|
|
1290
|
+
readingParams: ["match"],
|
|
1067
1291
|
run: async ({ params, run }) => {
|
|
1068
1292
|
const command = stringParam("sumOfCounts", params, "command");
|
|
1069
|
-
const
|
|
1070
|
-
|
|
1293
|
+
const pattern = stringParam("sumOfCounts", params, "match", ":(\\d+)$");
|
|
1294
|
+
const match = new RegExp(pattern, "gm");
|
|
1295
|
+
const output = run(command).output;
|
|
1296
|
+
const found = [...output.matchAll(match)];
|
|
1297
|
+
if (found.length === 0 && output.trim() !== "") {
|
|
1298
|
+
throw new CounterError(
|
|
1299
|
+
"sumOfCounts",
|
|
1300
|
+
`its match ${pattern} read nothing from ${output.trim().split("\n").length} line(s) of output (first: ${JSON.stringify(output.trim().split("\n")[0])}) \u2014 a match that cannot read the tool is not a zero`
|
|
1301
|
+
);
|
|
1302
|
+
}
|
|
1303
|
+
return found.map(([, digits]) => Number(digits ?? 0)).filter((count) => Number.isFinite(count)).reduce((sum, count) => sum + count, 0);
|
|
1071
1304
|
}
|
|
1072
1305
|
};
|
|
1073
1306
|
|
|
1074
1307
|
// src/counters/suppressions.ts
|
|
1075
|
-
import { readdirSync as readdirSync4, readFileSync as
|
|
1076
|
-
import { join as
|
|
1308
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync11 } from "fs";
|
|
1309
|
+
import { join as join9, resolve as resolve12 } from "path";
|
|
1077
1310
|
var CODE_FILE = /\.(?:ts|tsx|js|jsx|mjs|cjs|mts|cts|vue|svelte|astro)$/;
|
|
1078
1311
|
var SKIP3 = /^(?:node_modules|dist|coverage|\.[^.]|__fixtures__)/;
|
|
1079
1312
|
var SUPPRESSION = /(?:eslint|oxlint|biome)-(?:disable|ignore)(?:-next-line|-line)?|@ts-expect-error|@ts-ignore/g;
|
|
@@ -1088,12 +1321,12 @@ var countIn = (dir, depth = 12) => {
|
|
|
1088
1321
|
for (const entry of entries) {
|
|
1089
1322
|
if (SKIP3.test(entry.name)) continue;
|
|
1090
1323
|
if (entry.isDirectory()) {
|
|
1091
|
-
if (depth > 0) found += countIn(
|
|
1324
|
+
if (depth > 0) found += countIn(join9(dir, entry.name), depth - 1);
|
|
1092
1325
|
continue;
|
|
1093
1326
|
}
|
|
1094
1327
|
if (!CODE_FILE.test(entry.name)) continue;
|
|
1095
1328
|
try {
|
|
1096
|
-
found +=
|
|
1329
|
+
found += readFileSync11(join9(dir, entry.name), "utf8").match(SUPPRESSION)?.length ?? 0;
|
|
1097
1330
|
} catch {
|
|
1098
1331
|
}
|
|
1099
1332
|
}
|
|
@@ -1107,14 +1340,14 @@ var suppressionCount = {
|
|
|
1107
1340
|
},
|
|
1108
1341
|
run: async ({ cwd, params }) => {
|
|
1109
1342
|
const roots = stringsParam(params, "roots", ["."]);
|
|
1110
|
-
return roots.reduce((sum, root) => sum + countIn(
|
|
1343
|
+
return roots.reduce((sum, root) => sum + countIn(resolve12(cwd, root)), 0);
|
|
1111
1344
|
}
|
|
1112
1345
|
};
|
|
1113
1346
|
|
|
1114
1347
|
// src/counters/tests.ts
|
|
1115
|
-
import { existsSync as
|
|
1348
|
+
import { existsSync as existsSync10, mkdtempSync as mkdtempSync2, readFileSync as readFileSync12, rmSync as rmSync4 } from "fs";
|
|
1116
1349
|
import { tmpdir as tmpdir2 } from "os";
|
|
1117
|
-
import { join as
|
|
1350
|
+
import { join as join10, resolve as resolve13 } from "path";
|
|
1118
1351
|
var COUNTER = "testFailures";
|
|
1119
1352
|
var VITEST_LINE = /^\s*Tests {2,}(.+?)\s*$/;
|
|
1120
1353
|
var VITEST_TOTAL = /\(\d+\)$/;
|
|
@@ -1146,7 +1379,7 @@ ${output.trim().slice(-500)}`
|
|
|
1146
1379
|
);
|
|
1147
1380
|
};
|
|
1148
1381
|
var fromReport = (path) => {
|
|
1149
|
-
if (!
|
|
1382
|
+
if (!existsSync10(path)) {
|
|
1150
1383
|
throw new CounterError(
|
|
1151
1384
|
COUNTER,
|
|
1152
1385
|
`the runner wrote no report at ${path} \u2014 a crash before the reporter is not a pass`
|
|
@@ -1154,7 +1387,7 @@ var fromReport = (path) => {
|
|
|
1154
1387
|
}
|
|
1155
1388
|
let report;
|
|
1156
1389
|
try {
|
|
1157
|
-
report = JSON.parse(
|
|
1390
|
+
report = JSON.parse(readFileSync12(path, "utf8"));
|
|
1158
1391
|
} catch (error) {
|
|
1159
1392
|
throw new CounterError(
|
|
1160
1393
|
COUNTER,
|
|
@@ -1178,14 +1411,14 @@ var fromReport = (path) => {
|
|
|
1178
1411
|
};
|
|
1179
1412
|
var reportPathFor = (cwd, params, command) => {
|
|
1180
1413
|
const named = params.reportPath;
|
|
1181
|
-
if (typeof named === "string" && named !== "") return { own: false, path:
|
|
1414
|
+
if (typeof named === "string" && named !== "") return { own: false, path: resolve13(cwd, named) };
|
|
1182
1415
|
if (!command.includes(PLACEHOLDER)) {
|
|
1183
1416
|
throw new CounterError(
|
|
1184
1417
|
COUNTER,
|
|
1185
1418
|
`report: "${VITEST_JSON}" needs somewhere to put the report \u2014 write ${PLACEHOLDER} into the command (--outputFile=${PLACEHOLDER}) or give the entry a "reportPath"`
|
|
1186
1419
|
);
|
|
1187
1420
|
}
|
|
1188
|
-
return { own: true, path:
|
|
1421
|
+
return { own: true, path: join10(mkdtempSync2(join10(tmpdir2(), "geonosis-report-")), "report.json") };
|
|
1189
1422
|
};
|
|
1190
1423
|
var testFailures = {
|
|
1191
1424
|
id: COUNTER,
|
|
@@ -1234,11 +1467,77 @@ var testFailures = {
|
|
|
1234
1467
|
run(command.replaceAll(PLACEHOLDER, path));
|
|
1235
1468
|
return fromReport(path);
|
|
1236
1469
|
} finally {
|
|
1237
|
-
if (own) rmSync4(
|
|
1470
|
+
if (own) rmSync4(join10(path, ".."), { force: true, recursive: true });
|
|
1238
1471
|
}
|
|
1239
1472
|
}
|
|
1240
1473
|
};
|
|
1241
1474
|
|
|
1475
|
+
// src/counters/todos.ts
|
|
1476
|
+
import { existsSync as existsSync11, readdirSync as readdirSync5, readFileSync as readFileSync13 } from "fs";
|
|
1477
|
+
import { join as join11, resolve as resolve14 } from "path";
|
|
1478
|
+
var CODE_FILE2 = /\.(?:ts|tsx|js|jsx|mjs|cjs|mts|cts|vue|svelte|astro)$/;
|
|
1479
|
+
var SKIP4 = /^(?:node_modules|dist|coverage|\.[^.]|__fixtures__)/;
|
|
1480
|
+
var PLAN_FILE = /^(\d{3,})-[a-z\d][a-z\d.-]*\.md$/;
|
|
1481
|
+
var plansIn = (dir) => {
|
|
1482
|
+
if (!existsSync11(dir)) return /* @__PURE__ */ new Set();
|
|
1483
|
+
const found = /* @__PURE__ */ new Set();
|
|
1484
|
+
for (const name of readdirSync5(dir)) {
|
|
1485
|
+
const number = PLAN_FILE.exec(name)?.[1];
|
|
1486
|
+
if (number !== void 0) found.add(String(Number(number)));
|
|
1487
|
+
}
|
|
1488
|
+
return found;
|
|
1489
|
+
};
|
|
1490
|
+
var countIn2 = (dir, marker, plans, depth = 12) => {
|
|
1491
|
+
let entries;
|
|
1492
|
+
try {
|
|
1493
|
+
entries = readdirSync5(dir, { withFileTypes: true });
|
|
1494
|
+
} catch {
|
|
1495
|
+
return 0;
|
|
1496
|
+
}
|
|
1497
|
+
let found = 0;
|
|
1498
|
+
for (const entry of entries) {
|
|
1499
|
+
if (SKIP4.test(entry.name)) continue;
|
|
1500
|
+
if (entry.isDirectory()) {
|
|
1501
|
+
if (depth > 0) found += countIn2(join11(dir, entry.name), marker, plans, depth - 1);
|
|
1502
|
+
continue;
|
|
1503
|
+
}
|
|
1504
|
+
if (!CODE_FILE2.test(entry.name)) continue;
|
|
1505
|
+
let text = "";
|
|
1506
|
+
try {
|
|
1507
|
+
text = readFileSync13(join11(dir, entry.name), "utf8");
|
|
1508
|
+
} catch {
|
|
1509
|
+
continue;
|
|
1510
|
+
}
|
|
1511
|
+
for (const match of text.matchAll(marker)) {
|
|
1512
|
+
const cited = match[1];
|
|
1513
|
+
if (cited === void 0 || !plans.has(String(Number(cited)))) found += 1;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
return found;
|
|
1517
|
+
};
|
|
1518
|
+
var orphanTodos = {
|
|
1519
|
+
id: "orphanTodos",
|
|
1520
|
+
probe: {
|
|
1521
|
+
expect: 2,
|
|
1522
|
+
input: (dir) => plant(
|
|
1523
|
+
dir,
|
|
1524
|
+
"src/planted.ts",
|
|
1525
|
+
"// TODO: no plan named at all\n// TODO(099): a plan nobody wrote\nexport const a = 1\n"
|
|
1526
|
+
)
|
|
1527
|
+
},
|
|
1528
|
+
readingParams: ["markers"],
|
|
1529
|
+
run: async ({ cwd, params }) => {
|
|
1530
|
+
const markers = stringsParam(params, "markers", ["TODO", "FIXME"]);
|
|
1531
|
+
const plans = plansIn(resolve14(cwd, stringParam("orphanTodos", params, "plans", "plans")));
|
|
1532
|
+
const marker = new RegExp(
|
|
1533
|
+
`\\b(?:${markers.map(escapeForRegex).join("|")})\\b(?:\\((\\d+)\\))?`,
|
|
1534
|
+
"g"
|
|
1535
|
+
);
|
|
1536
|
+
const roots = stringsParam(params, "roots", ["."]);
|
|
1537
|
+
return roots.reduce((sum, root) => sum + countIn2(resolve14(cwd, root), marker, plans), 0);
|
|
1538
|
+
}
|
|
1539
|
+
};
|
|
1540
|
+
|
|
1242
1541
|
// src/counters/typecheck.ts
|
|
1243
1542
|
var UNRESOLVED = /error TS(?:2305|2307): ([^\n]*)/g;
|
|
1244
1543
|
var SPECIFIER = /'([^']+)'/g;
|
|
@@ -1274,8 +1573,8 @@ var typecheckErrors = {
|
|
|
1274
1573
|
};
|
|
1275
1574
|
|
|
1276
1575
|
// src/counters/walk.ts
|
|
1277
|
-
import { existsSync as
|
|
1278
|
-
import { resolve as
|
|
1576
|
+
import { existsSync as existsSync12, readFileSync as readFileSync14 } from "fs";
|
|
1577
|
+
import { resolve as resolve15 } from "path";
|
|
1279
1578
|
var DEFAULT_REPORT2 = ".geonosis/walk-report.json";
|
|
1280
1579
|
var CLASSES = /* @__PURE__ */ new Set([
|
|
1281
1580
|
"buy-box-above-fold",
|
|
@@ -1319,8 +1618,8 @@ var walkFindings = {
|
|
|
1319
1618
|
},
|
|
1320
1619
|
run: async ({ cwd, params }) => {
|
|
1321
1620
|
const relative = stringParam("walkFindings", params, "report", DEFAULT_REPORT2);
|
|
1322
|
-
const path =
|
|
1323
|
-
if (!
|
|
1621
|
+
const path = resolve15(cwd, relative);
|
|
1622
|
+
if (!existsSync12(path)) {
|
|
1324
1623
|
throw new CounterError(
|
|
1325
1624
|
"walkFindings",
|
|
1326
1625
|
`no walk report at ${relative} \u2014 run \`geonosis-walk\` before measuring it`
|
|
@@ -1328,7 +1627,7 @@ var walkFindings = {
|
|
|
1328
1627
|
}
|
|
1329
1628
|
let report;
|
|
1330
1629
|
try {
|
|
1331
|
-
report = JSON.parse(
|
|
1630
|
+
report = JSON.parse(readFileSync14(path, "utf8"));
|
|
1332
1631
|
} catch (error) {
|
|
1333
1632
|
throw new CounterError(
|
|
1334
1633
|
"walkFindings",
|
|
@@ -1362,7 +1661,9 @@ var COUNTERS = [
|
|
|
1362
1661
|
oxlintErrors,
|
|
1363
1662
|
oxlintRule,
|
|
1364
1663
|
oxlintWarnings,
|
|
1664
|
+
orphanTodos,
|
|
1365
1665
|
packagesWithoutTypecheck,
|
|
1666
|
+
probelessRules,
|
|
1366
1667
|
runtimeCodeShipped,
|
|
1367
1668
|
sumOfCounts,
|
|
1368
1669
|
testFailures,
|
|
@@ -1383,7 +1684,7 @@ var counterById = (id) => {
|
|
|
1383
1684
|
|
|
1384
1685
|
// src/report.ts
|
|
1385
1686
|
var WIDTH = 28;
|
|
1386
|
-
var formatReport = ({ measurements, rewritten }) => {
|
|
1687
|
+
var formatReport = ({ measurements, refusals, rewritten }) => {
|
|
1387
1688
|
const lines = measurements.map((one) => {
|
|
1388
1689
|
if (one.verdict === "skipped") {
|
|
1389
1690
|
return ` SKIP ${one.key}: not measured by --tier ${one.tier}`;
|
|
@@ -1396,8 +1697,14 @@ var formatReport = ({ measurements, rewritten }) => {
|
|
|
1396
1697
|
if (one.verdict === "shrank") return `${head} <-- improved -${one.baseline - one.now}`;
|
|
1397
1698
|
return head;
|
|
1398
1699
|
});
|
|
1700
|
+
lines.push(...refusals.map((one) => ` CANNOT MEASURE ${one.key}: ${one.reason}`));
|
|
1399
1701
|
const grew = measurements.filter((one) => one.verdict === "grew");
|
|
1400
|
-
if (
|
|
1702
|
+
if (refusals.length > 0) {
|
|
1703
|
+
lines.push(
|
|
1704
|
+
"",
|
|
1705
|
+
`ratchet FAIL \u2014 ${refusals.length} counter(s) could not be measured, and a gate that cannot measure has not passed. The baseline was left exactly as it was. Next: run each counter's own command by hand \u2014 ${refusals.map((one) => one.key).join(", ")} \u2014 and fix what it prints.`
|
|
1706
|
+
);
|
|
1707
|
+
} else if (grew.length > 0) {
|
|
1401
1708
|
lines.push(
|
|
1402
1709
|
"",
|
|
1403
1710
|
"ratchet FAIL \u2014 debt grew. Fix it, or say in the commit message why the baseline goes up."
|
|
@@ -1419,6 +1726,7 @@ var formatProve = ({ proofs, proven }) => {
|
|
|
1419
1726
|
}
|
|
1420
1727
|
if (one.verdict === "cannot-measure") return ` CANNOT MEASURE ${one.key}: ${one.reason}`;
|
|
1421
1728
|
if (one.verdict === "cannot-fail") return ` CANNOT FAIL ${one.key}: read 0`;
|
|
1729
|
+
if (one.verdict === "unproven") return ` UNPROVEN ${one.key}: ${one.reason}`;
|
|
1422
1730
|
if (one.verdict === "misread") {
|
|
1423
1731
|
return ` MISREAD ${one.key}: read ${one.reading} where its probe planted ${one.expected}`;
|
|
1424
1732
|
}
|
|
@@ -1433,6 +1741,11 @@ var formatProve = ({ proofs, proven }) => {
|
|
|
1433
1741
|
};
|
|
1434
1742
|
|
|
1435
1743
|
export {
|
|
1744
|
+
ENVELOPES_DIR,
|
|
1745
|
+
envelopePath,
|
|
1746
|
+
UnbalancedEnvelope,
|
|
1747
|
+
writeEnvelope,
|
|
1748
|
+
versionOf,
|
|
1436
1749
|
heavyLockPath,
|
|
1437
1750
|
acquireExclusive,
|
|
1438
1751
|
CONFIG_FILE,
|
package/dist/cli.js
CHANGED
|
@@ -4,8 +4,10 @@ import {
|
|
|
4
4
|
formatProve,
|
|
5
5
|
formatReport,
|
|
6
6
|
runProve,
|
|
7
|
-
runRatchet
|
|
8
|
-
|
|
7
|
+
runRatchet,
|
|
8
|
+
versionOf,
|
|
9
|
+
writeEnvelope
|
|
10
|
+
} from "./chunk-H4PLCM5J.js";
|
|
9
11
|
|
|
10
12
|
// src/cli.ts
|
|
11
13
|
import process from "process";
|
|
@@ -100,6 +102,19 @@ var holdTheLock = async (ms) => {
|
|
|
100
102
|
`);
|
|
101
103
|
return 0;
|
|
102
104
|
};
|
|
105
|
+
var ENVELOPE_NEXT = "geonosis-ratchet --print-config-shape, then count the entries in geonosis.ratchet.json \u2014 a run that measured fewer counters than the config named is a bug in this tool, not in the tree it measured";
|
|
106
|
+
var envelopeOf = (result, durationMs) => ({
|
|
107
|
+
considered: result.considered,
|
|
108
|
+
durationMs,
|
|
109
|
+
excused: result.measurements.flatMap(
|
|
110
|
+
(one) => one.verdict === "skipped" ? [{ path: one.key, reason: `not measured by --tier ${one.tier}` }] : []
|
|
111
|
+
),
|
|
112
|
+
findings: result.measurements.filter((one) => one.verdict === "grew"),
|
|
113
|
+
read: result.measurements.filter((one) => one.verdict !== "skipped").length,
|
|
114
|
+
refused: result.refusals.map((one) => ({ path: one.key, reason: one.reason })),
|
|
115
|
+
tool: "ratchet",
|
|
116
|
+
version: versionOf(import.meta.url)
|
|
117
|
+
});
|
|
103
118
|
var measure = async () => {
|
|
104
119
|
if (holdMs !== void 0) return holdTheLock(holdMs);
|
|
105
120
|
if (proving) {
|
|
@@ -112,8 +127,17 @@ var measure = async () => {
|
|
|
112
127
|
process.stdout.write(formatProve(proof));
|
|
113
128
|
return proof.proven ? 0 : 2;
|
|
114
129
|
}
|
|
130
|
+
const startedAt = Date.now();
|
|
115
131
|
const result = await runRatchet({ counters: COUNTERS, cwd, tier });
|
|
116
132
|
process.stdout.write(formatReport(result));
|
|
133
|
+
const at = writeEnvelope({
|
|
134
|
+
envelope: envelopeOf(result, Date.now() - startedAt),
|
|
135
|
+
next: ENVELOPE_NEXT,
|
|
136
|
+
root: cwd
|
|
137
|
+
});
|
|
138
|
+
process.stdout.write(`envelope: ${at}
|
|
139
|
+
`);
|
|
140
|
+
if (result.refusals.length > 0) return 2;
|
|
117
141
|
return result.measurements.some((one) => one.verdict === "grew") ? 1 : 0;
|
|
118
142
|
};
|
|
119
143
|
try {
|
package/dist/index.d.ts
CHANGED
|
@@ -76,6 +76,14 @@ type Counter = {
|
|
|
76
76
|
* nobody configured says nothing about the mode they did.
|
|
77
77
|
*/
|
|
78
78
|
probe?: CounterProbe | CounterProbe[];
|
|
79
|
+
/**
|
|
80
|
+
* The params that change WHAT this counter reads (a `match`), as opposed to where it looks.
|
|
81
|
+
* #140: a configured reading the probe never exercises can be wrong in a way the probe cannot
|
|
82
|
+
* see — during.day's mis-escaped match read 0, was certified PROVEN, and the false win was
|
|
83
|
+
* banked into the baseline. A config customizing one of these must declare `probe: { sample,
|
|
84
|
+
* expect }` beside it, or the prove line says UNPROVEN.
|
|
85
|
+
*/
|
|
86
|
+
readingParams?: string[];
|
|
79
87
|
run: (context: CounterContext) => Promise<number>;
|
|
80
88
|
};
|
|
81
89
|
/** One line of `geonosis.ratchet.json`'s `counters` array. */
|
|
@@ -134,6 +142,11 @@ type Proof = {
|
|
|
134
142
|
key: string;
|
|
135
143
|
reason: string;
|
|
136
144
|
verdict: 'cannot-measure';
|
|
145
|
+
} | {
|
|
146
|
+
counter: string;
|
|
147
|
+
key: string;
|
|
148
|
+
reason: string;
|
|
149
|
+
verdict: 'unproven';
|
|
137
150
|
}
|
|
138
151
|
/**
|
|
139
152
|
* Not a counter: `--exclusive` itself, measured by running two of it. A lock is a claim about the
|
|
@@ -157,8 +170,24 @@ type ProveResult = {
|
|
|
157
170
|
/** False as soon as one counter could not be shown to read its own planted finding. */
|
|
158
171
|
proven: boolean;
|
|
159
172
|
};
|
|
173
|
+
/** A counter whose tool could not be read. Never a number, and never a silent zero. */
|
|
174
|
+
type Refusal = {
|
|
175
|
+
key: string;
|
|
176
|
+
reason: string;
|
|
177
|
+
};
|
|
160
178
|
type RatchetResult = {
|
|
179
|
+
/**
|
|
180
|
+
* How many counters the config named. The denominator: `considered === measurements.length +
|
|
181
|
+
* refusals.length`, and a run whose two do not add up has lost one somewhere between the config
|
|
182
|
+
* and the report.
|
|
183
|
+
*/
|
|
184
|
+
considered: number;
|
|
161
185
|
measurements: Measurement[];
|
|
186
|
+
/**
|
|
187
|
+
* Every counter that blew up, not just the first. The throw that used to leave the run at the
|
|
188
|
+
* first failure also left every counter behind it unrun and unreported.
|
|
189
|
+
*/
|
|
190
|
+
refusals: Refusal[];
|
|
162
191
|
/** True when the baseline file was rewritten because something shrank. */
|
|
163
192
|
rewritten: boolean;
|
|
164
193
|
};
|
|
@@ -178,6 +207,80 @@ declare const loadConfig: (cwd: string) => RatchetConfig;
|
|
|
178
207
|
*/
|
|
179
208
|
declare const resolveBaseline: (cwd: string) => string;
|
|
180
209
|
|
|
210
|
+
/**
|
|
211
|
+
* The report envelope: what a tool CONSIDERED, against what it actually read.
|
|
212
|
+
*
|
|
213
|
+
* Four denominator bugs landed in one day — a migrations run that reported on three of four files,
|
|
214
|
+
* a plan check that printed `PASS — 0 plan(s)` over a directory of twenty-one, a parity run over a
|
|
215
|
+
* tree the second config ignored, a chains validator that walked a list it had already filtered.
|
|
216
|
+
* Every one of them was GREEN, and every one of them was green because the tool published the
|
|
217
|
+
* numerator and nobody published the denominator.
|
|
218
|
+
*
|
|
219
|
+
* So every gate writes this, and the law is arithmetic:
|
|
220
|
+
*
|
|
221
|
+
* considered === read + refused.length + excused.length
|
|
222
|
+
*
|
|
223
|
+
* checked HERE, at write time. A tool whose own accounting does not add up has not measured what it
|
|
224
|
+
* says it measured, so it renders no verdict at all: the write throws, the file is not created, and
|
|
225
|
+
* the bin exits 2. That is deliberately harsher than a failing gate — a wrong verdict is worse than
|
|
226
|
+
* no verdict, and a tool that has lost count of its own inputs cannot tell which it is.
|
|
227
|
+
*
|
|
228
|
+
* `refused` and `excused` are both "considered and NOT read". They differ in whose fault it is:
|
|
229
|
+
* refused is the tool unable to read the thing (an unparseable migration, a counter whose command
|
|
230
|
+
* blew up), excused is the thing legitimately exempt (a contract-migration marker the gate believed,
|
|
231
|
+
* a step the run never reached, a plan written before the contract). Both carry a reason, because a
|
|
232
|
+
* bucket without reasons is just a smaller silence.
|
|
233
|
+
*/
|
|
234
|
+
type EnvelopeEntry = {
|
|
235
|
+
/** What was not read — a file, a counter key, a step id. Whatever the tool names its inputs by. */
|
|
236
|
+
path: string;
|
|
237
|
+
/** Why it was not read, in a sentence a reader can act on. */
|
|
238
|
+
reason: string;
|
|
239
|
+
};
|
|
240
|
+
type ReportEnvelope = {
|
|
241
|
+
/** Everything this run took as its input. The denominator. */
|
|
242
|
+
considered: number;
|
|
243
|
+
durationMs: number;
|
|
244
|
+
excused: EnvelopeEntry[];
|
|
245
|
+
/** Whatever the tool found. Shape is the tool's own; nothing here reads into it. */
|
|
246
|
+
findings: unknown[];
|
|
247
|
+
/** How many of `considered` the run actually read. The numerator. */
|
|
248
|
+
read: number;
|
|
249
|
+
refused: EnvelopeEntry[];
|
|
250
|
+
/** The name the envelope is filed under, and the name a refusal is reported against. */
|
|
251
|
+
tool: string;
|
|
252
|
+
/** The build that wrote it. An envelope that cannot name its build dates nothing. */
|
|
253
|
+
version: string;
|
|
254
|
+
};
|
|
255
|
+
/** Where every tool writes, and the one directory the doctor's envelope check reads. */
|
|
256
|
+
declare const ENVELOPES_DIR = ".geonosis/envelopes";
|
|
257
|
+
declare const envelopePath: (root: string, tool: string) => string;
|
|
258
|
+
/** A run whose accounting does not add up. Never a verdict — the refusal to render one. */
|
|
259
|
+
declare class UnbalancedEnvelope extends Error {
|
|
260
|
+
constructor(message: string);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Writes the envelope, or refuses. Returns the path written so a caller can print it.
|
|
264
|
+
*
|
|
265
|
+
* The check is before the write and not after it, because a file on disk is a verdict: the doctor
|
|
266
|
+
* would read a half-true envelope and report on numbers nobody stands behind.
|
|
267
|
+
*/
|
|
268
|
+
declare const writeEnvelope: ({ envelope, next, root, }: {
|
|
269
|
+
envelope: ReportEnvelope;
|
|
270
|
+
/** The command a reader should run to see the accounting for themselves. */
|
|
271
|
+
next: string;
|
|
272
|
+
root: string;
|
|
273
|
+
}) => string;
|
|
274
|
+
/**
|
|
275
|
+
* The version of the package a module belongs to, found by walking up to the nearest manifest.
|
|
276
|
+
*
|
|
277
|
+
* Called with `import.meta.url`, it answers the same in `src/` under vitest and in the bundled
|
|
278
|
+
* `dist/` a bin loads — which is the whole point, because the envelope a consumer's CI writes is
|
|
279
|
+
* written by the build, and a version baked in at compile time would be the version of whoever
|
|
280
|
+
* bundled it.
|
|
281
|
+
*/
|
|
282
|
+
declare const versionOf: (moduleUrl: string) => string;
|
|
283
|
+
|
|
181
284
|
declare const COUNTERS: Counter[];
|
|
182
285
|
declare const counterById: (id: string) => Counter;
|
|
183
286
|
|
|
@@ -251,7 +354,7 @@ declare const runRatchet: ({ counters, cwd, tier, }: {
|
|
|
251
354
|
* the tier left out says so by name — printing a number for it would be the stale OK this exists
|
|
252
355
|
* to prevent.
|
|
253
356
|
*/
|
|
254
|
-
declare const formatReport: ({ measurements, rewritten }: RatchetResult) => string;
|
|
357
|
+
declare const formatReport: ({ measurements, refusals, rewritten }: RatchetResult) => string;
|
|
255
358
|
declare const formatProve: ({ proofs, proven }: ProveResult) => string;
|
|
256
359
|
|
|
257
360
|
/**
|
|
@@ -270,4 +373,4 @@ declare const formatProve: ({ proofs, proven }: ProveResult) => string;
|
|
|
270
373
|
*/
|
|
271
374
|
declare const runCommand: (cwd: string, counterId: string, env?: NodeJS.ProcessEnv) => (command: string) => CommandResult;
|
|
272
375
|
|
|
273
|
-
export { CONFIG_FILE, COUNTERS, type CommandResult, type Counter, type CounterConfig, type CounterContext, CounterError, type CounterProbe, type Holder, type Measurement, type Proof, type ProveResult, type RatchetConfig, type RatchetResult, type Verdict, acquireExclusive, counterById, formatProve, formatReport, heavyLockPath, keyOf, loadConfig, resolveBaseline, runCommand, runProve, runRatchet };
|
|
376
|
+
export { CONFIG_FILE, COUNTERS, type CommandResult, type Counter, type CounterConfig, type CounterContext, CounterError, type CounterProbe, ENVELOPES_DIR, type EnvelopeEntry, type Holder, type Measurement, type Proof, type ProveResult, type RatchetConfig, type RatchetResult, type Refusal, type ReportEnvelope, UnbalancedEnvelope, type Verdict, acquireExclusive, counterById, envelopePath, formatProve, formatReport, heavyLockPath, keyOf, loadConfig, resolveBaseline, runCommand, runProve, runRatchet, versionOf, writeEnvelope };
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,11 @@ import {
|
|
|
2
2
|
CONFIG_FILE,
|
|
3
3
|
COUNTERS,
|
|
4
4
|
CounterError,
|
|
5
|
+
ENVELOPES_DIR,
|
|
6
|
+
UnbalancedEnvelope,
|
|
5
7
|
acquireExclusive,
|
|
6
8
|
counterById,
|
|
9
|
+
envelopePath,
|
|
7
10
|
formatProve,
|
|
8
11
|
formatReport,
|
|
9
12
|
heavyLockPath,
|
|
@@ -12,14 +15,19 @@ import {
|
|
|
12
15
|
resolveBaseline,
|
|
13
16
|
runCommand,
|
|
14
17
|
runProve,
|
|
15
|
-
runRatchet
|
|
16
|
-
|
|
18
|
+
runRatchet,
|
|
19
|
+
versionOf,
|
|
20
|
+
writeEnvelope
|
|
21
|
+
} from "./chunk-H4PLCM5J.js";
|
|
17
22
|
export {
|
|
18
23
|
CONFIG_FILE,
|
|
19
24
|
COUNTERS,
|
|
20
25
|
CounterError,
|
|
26
|
+
ENVELOPES_DIR,
|
|
27
|
+
UnbalancedEnvelope,
|
|
21
28
|
acquireExclusive,
|
|
22
29
|
counterById,
|
|
30
|
+
envelopePath,
|
|
23
31
|
formatProve,
|
|
24
32
|
formatReport,
|
|
25
33
|
heavyLockPath,
|
|
@@ -28,5 +36,7 @@ export {
|
|
|
28
36
|
resolveBaseline,
|
|
29
37
|
runCommand,
|
|
30
38
|
runProve,
|
|
31
|
-
runRatchet
|
|
39
|
+
runRatchet,
|
|
40
|
+
versionOf,
|
|
41
|
+
writeEnvelope
|
|
32
42
|
};
|