@shrkcrft/cli 0.1.0-alpha.27 → 0.1.0-alpha.28
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/commands/baseline.command.d.ts +8 -0
- package/dist/commands/baseline.command.d.ts.map +1 -0
- package/dist/commands/baseline.command.js +511 -0
- package/dist/commands/changelog-data.d.ts.map +1 -1
- package/dist/commands/changelog-data.js +22 -0
- package/dist/commands/check.command.d.ts.map +1 -1
- package/dist/commands/check.command.js +28 -1
- package/dist/commands/command-catalog.d.ts.map +1 -1
- package/dist/commands/command-catalog.js +112 -0
- package/dist/commands/gates.command.d.ts +6 -0
- package/dist/commands/gates.command.d.ts.map +1 -0
- package/dist/commands/gates.command.js +334 -0
- package/dist/commands/generated.command.d.ts +6 -0
- package/dist/commands/generated.command.d.ts.map +1 -0
- package/dist/commands/generated.command.js +514 -0
- package/dist/commands/ingest.command.d.ts +11 -0
- package/dist/commands/ingest.command.d.ts.map +1 -1
- package/dist/commands/ingest.command.js +49 -23
- package/dist/commands/policy-lint.command.d.ts +37 -0
- package/dist/commands/policy-lint.command.d.ts.map +1 -1
- package/dist/commands/policy-lint.command.js +119 -2
- package/dist/commands/registry.command.d.ts.map +1 -1
- package/dist/commands/registry.command.js +36 -4
- package/dist/commands/wiring.command.d.ts.map +1 -1
- package/dist/commands/wiring.command.js +25 -0
- package/dist/finish/run-finish.d.ts.map +1 -1
- package/dist/finish/run-finish.js +9 -3
- package/dist/gates/gate-rule-view.d.ts +35 -0
- package/dist/gates/gate-rule-view.d.ts.map +1 -0
- package/dist/gates/gate-rule-view.js +80 -0
- package/dist/gates/rule-coverage.d.ts +53 -0
- package/dist/gates/rule-coverage.d.ts.map +1 -0
- package/dist/gates/rule-coverage.js +165 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +25 -2
- package/package.json +33 -33
|
@@ -1,13 +1,115 @@
|
|
|
1
1
|
import * as nodePath from 'node:path';
|
|
2
|
-
import { runPolicyLint } from '@shrkcrft/boundaries';
|
|
2
|
+
import { runPolicyLint, } from '@shrkcrft/boundaries';
|
|
3
3
|
import { classifyChangedScope, resolveChangedFiles, resolveProjectConfig } from '@shrkcrft/inspector';
|
|
4
4
|
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
5
5
|
import { asJson, header, kv } from "../output/format-output.js";
|
|
6
6
|
const VALID_SURFACES = new Set(['template', 'style', 'ts']);
|
|
7
|
+
export const POLICY_EXPLAIN_SCHEMA = 'sharkcraft.policy-explain/v1';
|
|
8
|
+
/**
|
|
9
|
+
* Dry-run ONE policy rule and return everything it saw — including the hits an
|
|
10
|
+
* exemption swallowed.
|
|
11
|
+
*
|
|
12
|
+
* Showing suppressed hits is the point: an exemption that silently deletes a
|
|
13
|
+
* finding is indistinguishable from a stale glob, so both the kept and the
|
|
14
|
+
* dropped hits are reported, each labelled with the exemption that applied.
|
|
15
|
+
*/
|
|
16
|
+
export function runPolicyExplain(cwd, rule, excludeDirs) {
|
|
17
|
+
const report = runPolicyLint(cwd, [rule], { excludeDirs });
|
|
18
|
+
const result = report.rules[0];
|
|
19
|
+
const skip = report.skipped[0];
|
|
20
|
+
return {
|
|
21
|
+
schema: POLICY_EXPLAIN_SCHEMA,
|
|
22
|
+
ruleId: rule.id,
|
|
23
|
+
...(rule.description ? { description: rule.description } : {}),
|
|
24
|
+
surface: rule.surface,
|
|
25
|
+
severity: rule.severity ?? 'error',
|
|
26
|
+
pattern: rule.pattern,
|
|
27
|
+
scan: rule.scan ?? 'all',
|
|
28
|
+
unitsScanned: result?.unitsScanned ?? 0,
|
|
29
|
+
status: result?.status ?? 'error',
|
|
30
|
+
findings: report.findings,
|
|
31
|
+
suppressed: report.suppressed,
|
|
32
|
+
exemptFiles: rule.exemptFiles ?? [],
|
|
33
|
+
...(rule.exemptLines ? { exemptLines: rule.exemptLines } : {}),
|
|
34
|
+
diagnostics: report.diagnostics,
|
|
35
|
+
...(skip ? { skipReason: skip.reason } : {}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Render an {@link IPolicyExplain}. Always returns 0 — explain is informational. */
|
|
39
|
+
export function renderPolicyExplain(explain, wantJson) {
|
|
40
|
+
if (wantJson) {
|
|
41
|
+
process.stdout.write(asJson(explain) + '\n');
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
process.stdout.write(header(`Policy explain: ${explain.ruleId} (${explain.surface})`));
|
|
45
|
+
if (explain.description)
|
|
46
|
+
process.stdout.write(` ${explain.description}\n`);
|
|
47
|
+
process.stdout.write(kv('pattern', `/${explain.pattern}/`) + '\n');
|
|
48
|
+
process.stdout.write(kv('scan zone', explain.scan) + '\n');
|
|
49
|
+
process.stdout.write(kv('units scanned', String(explain.unitsScanned)) + '\n');
|
|
50
|
+
process.stdout.write(kv('status', explain.status) + '\n');
|
|
51
|
+
if (explain.exemptFiles.length > 0) {
|
|
52
|
+
process.stdout.write(kv('exemptFiles', explain.exemptFiles.join(', ')) + '\n');
|
|
53
|
+
}
|
|
54
|
+
if (explain.exemptLines)
|
|
55
|
+
process.stdout.write(kv('exemptLines', explain.exemptLines) + '\n');
|
|
56
|
+
process.stdout.write(`\nHits that COUNT (${explain.findings.length}):\n`);
|
|
57
|
+
for (const f of explain.findings.slice(0, 60)) {
|
|
58
|
+
process.stdout.write(` ✗ ${f.match} (${f.file}:${f.line})${f.inlineTemplate ? ' [inline template]' : ''}\n`);
|
|
59
|
+
}
|
|
60
|
+
if (explain.findings.length > 60) {
|
|
61
|
+
process.stdout.write(` … (${explain.findings.length - 60} more)\n`);
|
|
62
|
+
}
|
|
63
|
+
if (explain.suppressed.length > 0) {
|
|
64
|
+
process.stdout.write(`\nHits an exemption DROPPED (${explain.suppressed.length}):\n`);
|
|
65
|
+
for (const s of explain.suppressed.slice(0, 60)) {
|
|
66
|
+
process.stdout.write(` – ${s.match} (${s.file}:${s.line}) via ${s.via}\n`);
|
|
67
|
+
}
|
|
68
|
+
if (explain.suppressed.length > 60) {
|
|
69
|
+
process.stdout.write(` … (${explain.suppressed.length - 60} more)\n`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (explain.skipReason) {
|
|
73
|
+
process.stdout.write(`\n! SKIPPED — ${explain.skipReason}. A rule that scans nothing is a bug in the rule,\n` +
|
|
74
|
+
' not a pass. Fix the glob, or set `failOnEmpty: true` to make this a hard failure.\n');
|
|
75
|
+
}
|
|
76
|
+
for (const d of explain.diagnostics)
|
|
77
|
+
process.stdout.write(` ! ${d}\n`);
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
export const policyLintExplainCommand = {
|
|
81
|
+
name: 'explain',
|
|
82
|
+
description: 'Dry-run ONE policyRule and print every hit with file:line — INCLUDING the hits an exemption or the scan zone dropped, each labelled with which one applied.',
|
|
83
|
+
usage: 'shrk policy-lint explain <ruleId> [--json]',
|
|
84
|
+
booleanFlags: new Set(['json']),
|
|
85
|
+
async run(args) {
|
|
86
|
+
const id = args.positional[0] ?? flagString(args, 'id');
|
|
87
|
+
if (!id) {
|
|
88
|
+
process.stderr.write('Usage: shrk policy-lint explain <ruleId> [--json]\n');
|
|
89
|
+
return 2;
|
|
90
|
+
}
|
|
91
|
+
const cwd = resolveCwd(args);
|
|
92
|
+
const loaded = await resolveProjectConfig(cwd);
|
|
93
|
+
if (!loaded.ok) {
|
|
94
|
+
process.stderr.write(`Could not load config: ${loaded.error.message}\n`);
|
|
95
|
+
return 2;
|
|
96
|
+
}
|
|
97
|
+
const rules = loaded.value.config.policyRules ?? [];
|
|
98
|
+
const rule = rules.find((r) => r.id === id);
|
|
99
|
+
if (!rule) {
|
|
100
|
+
process.stderr.write(`No policy rule "${id}". Configured: ${rules.map((r) => r.id).join(', ') || '(none)'}\n`);
|
|
101
|
+
return 2;
|
|
102
|
+
}
|
|
103
|
+
const rel = nodePath.relative(cwd, loaded.value.sharkcraftDir).split(nodePath.sep).join('/');
|
|
104
|
+
const excludeDirs = rel && !rel.startsWith('..') ? [rel] : [];
|
|
105
|
+
return renderPolicyExplain(runPolicyExplain(cwd, rule, excludeDirs), flagBool(args, 'json'));
|
|
106
|
+
},
|
|
107
|
+
};
|
|
7
108
|
export const policyLintCommand = {
|
|
8
109
|
name: 'policy-lint',
|
|
9
110
|
description: 'Lint template/markup, stylesheet, and AOT-invisible TS surfaces against data-defined policyRules[] (e.g. flag raw markup when a primitive exists). Sees `.html` files AND inline `template:` strings — surfaces tsc/AOT cannot. Deterministic; no AI.',
|
|
10
111
|
usage: 'shrk [--cwd <dir>] policy-lint [--surface template|style|ts] [--changed-only] [--new-only] [--since <ref>] [--only <ids>] [--json]\n (--changed-only SCANS just the changed files; --new-only scans the whole tree but shows only findings the change introduced, hiding pre-existing baseline debt)',
|
|
112
|
+
booleanFlags: new Set(['json', 'changed-only', 'new-only']),
|
|
11
113
|
async run(args) {
|
|
12
114
|
const cwd = resolveCwd(args);
|
|
13
115
|
const wantJson = flagBool(args, 'json');
|
|
@@ -126,12 +228,23 @@ export const policyLintCommand = {
|
|
|
126
228
|
if (report.evaluated === 0) {
|
|
127
229
|
process.stdout.write(` ! Nothing evaluated — ${report.rules.length} rule(s) configured but none matched files in scope` +
|
|
128
230
|
(changedOnly || since ? ' (changed-only).\n' : '.\n'));
|
|
129
|
-
|
|
231
|
+
for (const sk of report.skipped) {
|
|
232
|
+
process.stdout.write(` – ${sk.ruleId}: ${sk.reason}${sk.failed ? ' (failOnEmpty → FAILED)' : ''}\n`);
|
|
233
|
+
}
|
|
234
|
+
// Scanning nothing is not a pass. `2` = not verified (the repo-wide
|
|
235
|
+
// contract); a `failOnEmpty` rule promotes it to a real failure.
|
|
236
|
+
return report.skipped.some((sk) => sk.failed) ? 1 : 2;
|
|
130
237
|
}
|
|
131
238
|
process.stdout.write(kv('rules evaluated', `${report.evaluated} of ${report.rules.length}`) + '\n');
|
|
132
239
|
const errors = report.findings.filter((f) => f.severity === 'error').length;
|
|
133
240
|
const warnings = report.findings.filter((f) => f.severity === 'warning').length;
|
|
134
241
|
process.stdout.write(kv('findings', `${errors} error(s), ${warnings} warning(s)`) + '\n');
|
|
242
|
+
if (report.suppressed.length > 0) {
|
|
243
|
+
process.stdout.write(kv('suppressed', `${report.suppressed.length} hit(s) dropped by an exemption — see \`policy-lint explain <id>\``) + '\n');
|
|
244
|
+
}
|
|
245
|
+
for (const sk of report.skipped) {
|
|
246
|
+
process.stdout.write(` ${sk.failed ? '✗' : '–'} ${sk.ruleId} ${sk.failed ? 'FAILED' : 'SKIPPED'} — ${sk.reason}\n`);
|
|
247
|
+
}
|
|
135
248
|
if (newOnly) {
|
|
136
249
|
process.stdout.write(kv('scope', `new-only (${hiddenBaseline} pre-existing finding(s) hidden — run without --new-only to see all)`) + '\n');
|
|
137
250
|
}
|
|
@@ -140,6 +253,10 @@ export const policyLintCommand = {
|
|
|
140
253
|
for (const d of report.diagnostics)
|
|
141
254
|
process.stdout.write(` ! ${d}\n`);
|
|
142
255
|
}
|
|
256
|
+
if (report.skipped.some((sk) => sk.failed)) {
|
|
257
|
+
process.stdout.write('\nA rule with `failOnEmpty: true` matched nothing — that is a bug in the rule, not a pass.\n');
|
|
258
|
+
return 1;
|
|
259
|
+
}
|
|
143
260
|
if (report.findings.length === 0 && report.diagnostics.length === 0) {
|
|
144
261
|
process.stdout.write(newOnly
|
|
145
262
|
? `\nNo NEW policy violations from this change${hiddenBaseline > 0 ? ` (${hiddenBaseline} pre-existing hidden)` : ''}. ✓\n`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.command.d.ts","sourceRoot":"","sources":["../../src/commands/registry.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"registry.command.d.ts","sourceRoot":"","sources":["../../src/commands/registry.command.ts"],"names":[],"mappings":"AA8BA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAKhC,eAAO,MAAM,wBAAwB,EAAE,eA8CtC,CAAC;AAoLF,eAAO,MAAM,eAAe,EAAE,eA4B7B,CAAC"}
|
|
@@ -8,13 +8,14 @@
|
|
|
8
8
|
* [--fail-if-taken] # guard: non-zero when taken (free → 0)
|
|
9
9
|
* [--fail-if-missing] # guard: non-zero when NOT registered
|
|
10
10
|
* shrk registry <name> where <id> [--json] # declaration (+ consumer) sites
|
|
11
|
+
* shrk registry <name> duplicates [--json] # ids declared in more than one place
|
|
11
12
|
*
|
|
12
13
|
* `<name>` resolves a `registries[]` declaration in sharkcraft.config.ts — one
|
|
13
14
|
* deterministic multi-root scan that answers "is this id taken / where is it"
|
|
14
15
|
* without an agent re-running a fragile grep.
|
|
15
16
|
*/
|
|
16
17
|
import { buildRegistryLifecycleReport, renderRegistryLifecycleReportText, resolveChangedFiles, resolveProjectConfig, } from '@shrkcrft/inspector';
|
|
17
|
-
import { scanRegistry, registryExists, registryWhere, } from '@shrkcrft/boundaries';
|
|
18
|
+
import { scanRegistry, registryDuplicates, registryExists, registryWhere, } from '@shrkcrft/boundaries';
|
|
18
19
|
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
19
20
|
import { ExitCode } from "../exit-codes.js";
|
|
20
21
|
import { asJson } from "../output/format-output.js";
|
|
@@ -166,6 +167,37 @@ async function runRegistryInventory(args, name) {
|
|
|
166
167
|
process.stdout.write(`${exists ? 'yes' : 'no'} — "${canonical}" is ${exists ? 'declared' : 'NOT declared'} in registry "${inventory.name}".\n`);
|
|
167
168
|
return code;
|
|
168
169
|
}
|
|
170
|
+
if (action === 'duplicates') {
|
|
171
|
+
// Two roots claiming the same id compile fine; whichever registration wins
|
|
172
|
+
// at runtime is an accident of load order. Every site is printed so the
|
|
173
|
+
// duplicate can be resolved, not merely detected.
|
|
174
|
+
const dupes = registryDuplicates(inventory);
|
|
175
|
+
if (json) {
|
|
176
|
+
process.stdout.write(asJson({
|
|
177
|
+
name: inventory.name,
|
|
178
|
+
scanned: inventory.entries.length,
|
|
179
|
+
duplicates: dupes,
|
|
180
|
+
diagnostics: [...inventory.diagnostics, ...loaded.planeDiagnostics],
|
|
181
|
+
}) + '\n');
|
|
182
|
+
return inventory.entries.length === 0 ? 2 : dupes.length > 0 ? 1 : 0;
|
|
183
|
+
}
|
|
184
|
+
if (inventory.entries.length === 0) {
|
|
185
|
+
process.stdout.write(`Registry "${inventory.name}" matched 0 ids — nothing was checked. This is NOT a pass;\n` +
|
|
186
|
+
' the source selector is probably stale (see `shrk gates coverage`).\n');
|
|
187
|
+
return 2;
|
|
188
|
+
}
|
|
189
|
+
if (dupes.length === 0) {
|
|
190
|
+
process.stdout.write(`No duplicate ids in registry "${inventory.name}" (${inventory.entries.length} scanned). ✓\n`);
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
process.stdout.write(`Duplicate ids in registry "${inventory.name}" (${dupes.length}):\n`);
|
|
194
|
+
for (const e of dupes) {
|
|
195
|
+
process.stdout.write(` ✗ ${e.id} (${e.sites.length} declarations)\n`);
|
|
196
|
+
for (const s of e.sites)
|
|
197
|
+
process.stdout.write(` ${s.file}:${s.line}\n`);
|
|
198
|
+
}
|
|
199
|
+
return 1;
|
|
200
|
+
}
|
|
169
201
|
if (action === 'where') {
|
|
170
202
|
if (!id) {
|
|
171
203
|
process.stderr.write(`Usage: shrk registry ${name} where <id>\n`);
|
|
@@ -187,13 +219,13 @@ async function runRegistryInventory(args, name) {
|
|
|
187
219
|
process.stdout.write(` consumed ${s.file}:${s.line}\n`);
|
|
188
220
|
return 0;
|
|
189
221
|
}
|
|
190
|
-
process.stderr.write(`Unknown action "${action}". Usage: shrk registry ${name} list | exists <id> | where <id
|
|
222
|
+
process.stderr.write(`Unknown action "${action}". Usage: shrk registry ${name} list | exists <id> | where <id> | duplicates\n`);
|
|
191
223
|
return 2;
|
|
192
224
|
}
|
|
193
225
|
export const registryCommand = {
|
|
194
226
|
name: 'registry',
|
|
195
227
|
description: 'Registry inspections: lifecycle symmetry + declared-registry inventory. Read-only.',
|
|
196
|
-
usage: 'shrk registry lifecycle | <name> list | <name> exists <id> [--resolve] [--fail-if-taken|--fail-if-missing] | <name> where <id>',
|
|
228
|
+
usage: 'shrk registry lifecycle | <name> list | <name> exists <id> [--resolve] [--fail-if-taken|--fail-if-missing] | <name> where <id> | <name> duplicates',
|
|
197
229
|
// Guard-mode + query flags take no value — declare them so `exists <id>
|
|
198
230
|
// --fail-if-taken` (flag last) and `exists --resolve <id>` (flag first) both
|
|
199
231
|
// keep the id as a positional instead of swallowing it.
|
|
@@ -211,7 +243,7 @@ export const registryCommand = {
|
|
|
211
243
|
const cwd = resolveCwd(args);
|
|
212
244
|
const loaded = await loadRegistries(cwd);
|
|
213
245
|
const names = loaded.ok ? loaded.registries.map((r) => r.name) : [];
|
|
214
|
-
process.stderr.write('Usage: shrk registry lifecycle | <name> list | <name> exists <id> | <name> where <id
|
|
246
|
+
process.stderr.write('Usage: shrk registry lifecycle | <name> list | <name> exists <id> | <name> where <id> | <name> duplicates\n' +
|
|
215
247
|
(names.length > 0 ? `Declared registries: ${names.join(', ')}.\n` : 'No registries declared (sharkcraft.config.ts `registries[]`).\n'));
|
|
216
248
|
return 2;
|
|
217
249
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wiring.command.d.ts","sourceRoot":"","sources":["../../src/commands/wiring.command.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,cAAc,EACpB,MAAM,sBAAsB,CAAC;AAM9B,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAMhC;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"wiring.command.d.ts","sourceRoot":"","sources":["../../src/commands/wiring.command.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,cAAc,EACpB,MAAM,sBAAsB,CAAC;AAM9B,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAMhC;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAoFrF;AAyZD,eAAO,MAAM,aAAa,EAAE,eAgB3B,CAAC"}
|
|
@@ -24,6 +24,10 @@ export function renderWiringExplain(report, wantJson) {
|
|
|
24
24
|
process.stdout.write(` ${report.description}\n`);
|
|
25
25
|
if (report.groupBy)
|
|
26
26
|
process.stdout.write(kv('groupBy', report.groupBy) + '\n');
|
|
27
|
+
if (report.registeredMode === 'intersection') {
|
|
28
|
+
process.stdout.write(kv('registeredMode', 'intersection (must be in EVERY sink)') + '\n');
|
|
29
|
+
}
|
|
30
|
+
process.stdout.write(kv('status', report.status) + '\n');
|
|
27
31
|
process.stdout.write(kv('declared', `${report.declared.distinctCount} distinct across ${report.declared.filesScanned} file(s)`) +
|
|
28
32
|
'\n');
|
|
29
33
|
process.stdout.write(kv('registered', `${report.registered.distinctCount} distinct across ${report.registered.filesScanned} file(s)`) + '\n');
|
|
@@ -52,6 +56,27 @@ export function renderWiringExplain(report, wantJson) {
|
|
|
52
56
|
process.stdout.write(` … (${report.registeredNotDeclared.length - SITE_DISPLAY_CAP} more)\n`);
|
|
53
57
|
}
|
|
54
58
|
}
|
|
59
|
+
if (report.overlap.length > 0) {
|
|
60
|
+
process.stdout.write(`\nPresent on BOTH sides (disjoint, ${report.overlap.length}):\n`);
|
|
61
|
+
for (const s of report.overlap.slice(0, SITE_DISPLAY_CAP)) {
|
|
62
|
+
process.stdout.write(` ✗ ${s.token} (${s.file}:${s.line})\n`);
|
|
63
|
+
}
|
|
64
|
+
if (report.overlap.length > SITE_DISPLAY_CAP) {
|
|
65
|
+
process.stdout.write(` … (${report.overlap.length - SITE_DISPLAY_CAP} more)\n`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (report.hops && report.hops.length > 0) {
|
|
69
|
+
process.stdout.write('\nChain hops:\n');
|
|
70
|
+
for (const h of report.hops) {
|
|
71
|
+
process.stdout.write(` hop ${h.index}: ${h.fromCount} → ${h.toCount}` +
|
|
72
|
+
`${h.missing > 0 ? ` ✗ ${h.missing} missing` : ' ✓'}\n`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// A rule that checked NOTHING must never read like a clean pass.
|
|
76
|
+
if (report.skipReason) {
|
|
77
|
+
process.stdout.write(`\n! SKIPPED — ${report.skipReason}. A rule that matches nothing is a bug in the rule,\n` +
|
|
78
|
+
' not a pass. Fix the selector, or set `failOnEmpty: true` to make this a hard failure.\n');
|
|
79
|
+
}
|
|
55
80
|
for (const d of report.diagnostics)
|
|
56
81
|
process.stdout.write(` ! ${d}\n`);
|
|
57
82
|
process.stdout.write(`\nVerdict: ${report.verdict}\n`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-finish.d.ts","sourceRoot":"","sources":["../../src/finish/run-finish.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAC;AAiB7B,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,eAAO,MAAM,aAAa,EAAG,sBAA+B,CAAC;AAQ7D,2FAA2F;AAC3F,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAE3D,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,YAAY,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;IAClG,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,yEAAyE;IACzE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,OAAO,aAAa,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;QACzD,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,cAAc,CAAC;IACnD,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;IACzD,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC;CACtC;AAmBD;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"run-finish.d.ts","sourceRoot":"","sources":["../../src/finish/run-finish.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAC;AAiB7B,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,eAAO,MAAM,aAAa,EAAG,sBAA+B,CAAC;AAQ7D,2FAA2F;AAC3F,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAE3D,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,YAAY,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;IAClG,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,yEAAyE;IACzE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,OAAO,aAAa,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;QACzD,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,cAAc,CAAC;IACnD,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;IACzD,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC;CACtC;AAmBD;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC,CA2JnF"}
|
|
@@ -88,16 +88,22 @@ export async function runFinishGates(input) {
|
|
|
88
88
|
const report = buildImportHygieneReport(cwd, { files: codeChanged });
|
|
89
89
|
const errors = report.counts?.['error'] ?? (report.verdict === 'errors' ? report.findings.length : 0);
|
|
90
90
|
const warnings = report.counts?.['warning'] ?? (report.verdict === 'warnings' ? report.findings.length : 0);
|
|
91
|
+
// Only the findings that actually DRIVE the verdict become "failing items".
|
|
92
|
+
// An allowlisted import is downgraded to `info` by design and is not what
|
|
93
|
+
// failed — listing it anyway pads the renderer's 15-item cap and can push a
|
|
94
|
+
// real error out of view, while its allowlist justification reads like a
|
|
95
|
+
// fix instruction. A fix-list that names non-failures is not a fix-list.
|
|
96
|
+
const driving = report.findings.filter((f) => report.verdict === 'errors' ? f.severity === 'error' : f.severity === 'warning');
|
|
91
97
|
gates.push({
|
|
92
98
|
name: 'imports',
|
|
93
99
|
status: report.verdict === 'errors' ? 'fail' : 'pass',
|
|
94
|
-
detail: `verdict=${report.verdict} (${report.findings.length} finding(s))`,
|
|
100
|
+
detail: `verdict=${report.verdict} (${driving.length} of ${report.findings.length} finding(s) drive it)`,
|
|
95
101
|
errors,
|
|
96
102
|
warnings,
|
|
97
|
-
items:
|
|
103
|
+
items: driving.map((f) => ({
|
|
98
104
|
file: f.file,
|
|
99
105
|
line: f.line,
|
|
100
|
-
message:
|
|
106
|
+
message: `[${f.severity}] ${f.kind}: ${f.suggestedFix || f.reason || f.snippet}`.trim(),
|
|
101
107
|
})),
|
|
102
108
|
});
|
|
103
109
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { IBaselineRule, IGeneratedArtifactRule, IPolicyRule, IRegistrationIdiom, IRegistryDeclaration, IRuleSelfTest, IWiringRule } from '@shrkcrft/core';
|
|
2
|
+
/** Which data-defined plane a rule belongs to. */
|
|
3
|
+
export type GatePlane = 'wiring' | 'policy' | 'registry' | 'registration' | 'baseline' | 'generated';
|
|
4
|
+
/** Every plane, in the order `shrk gates list` prints them. */
|
|
5
|
+
export declare const GATE_PLANES: readonly GatePlane[];
|
|
6
|
+
/**
|
|
7
|
+
* One data-defined rule, normalized across planes.
|
|
8
|
+
*
|
|
9
|
+
* The trust layer's whole job is to answer "what did this rule actually match?"
|
|
10
|
+
* for ANY rule, so it needs one shape to iterate. The plane-specific rule object
|
|
11
|
+
* rides along in `raw` for the explain dispatch.
|
|
12
|
+
*/
|
|
13
|
+
export interface IGateRuleView {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly plane: GatePlane;
|
|
16
|
+
readonly description?: string;
|
|
17
|
+
readonly severity: 'error' | 'warning';
|
|
18
|
+
/** True when a zero-match is a hard failure rather than a loud skip. */
|
|
19
|
+
readonly failOnEmpty: boolean;
|
|
20
|
+
readonly selfTest?: IRuleSelfTest;
|
|
21
|
+
/** The underlying rule, for the plane-specific explainer. */
|
|
22
|
+
readonly raw: IWiringRule | IPolicyRule | IRegistryDeclaration | IRegistrationIdiom | IBaselineRule | IGeneratedArtifactRule;
|
|
23
|
+
}
|
|
24
|
+
/** The config planes this view is built from. */
|
|
25
|
+
export interface IGatePlanes {
|
|
26
|
+
readonly wiringRules?: readonly IWiringRule[];
|
|
27
|
+
readonly policyRules?: readonly IPolicyRule[];
|
|
28
|
+
readonly registries?: readonly IRegistryDeclaration[];
|
|
29
|
+
readonly registrationGraph?: readonly IRegistrationIdiom[];
|
|
30
|
+
readonly baselines?: readonly IBaselineRule[];
|
|
31
|
+
readonly generatedArtifacts?: readonly IGeneratedArtifactRule[];
|
|
32
|
+
}
|
|
33
|
+
/** Flatten every declared rule across every plane into one iterable list. */
|
|
34
|
+
export declare function collectGateRules(planes: IGatePlanes): IGateRuleView[];
|
|
35
|
+
//# sourceMappingURL=gate-rule-view.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate-rule-view.d.ts","sourceRoot":"","sources":["../../src/gates/gate-rule-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,sBAAsB,EACtB,WAAW,EACX,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,EACb,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAExB,kDAAkD;AAClD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,CAAC;AAErG,+DAA+D;AAC/D,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAO3C,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,wEAAwE;IACxE,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;IAClC,6DAA6D;IAC7D,QAAQ,CAAC,GAAG,EACR,WAAW,GACX,WAAW,GACX,oBAAoB,GACpB,kBAAkB,GAClB,aAAa,GACb,sBAAsB,CAAC;CAC5B;AAED,iDAAiD;AACjD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9C,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACtD,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAC3D,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IAC9C,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,sBAAsB,EAAE,CAAC;CACjE;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,aAAa,EAAE,CAqErE"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** Every plane, in the order `shrk gates list` prints them. */
|
|
2
|
+
export const GATE_PLANES = [
|
|
3
|
+
'wiring',
|
|
4
|
+
'policy',
|
|
5
|
+
'registry',
|
|
6
|
+
'registration',
|
|
7
|
+
'baseline',
|
|
8
|
+
'generated',
|
|
9
|
+
];
|
|
10
|
+
/** Flatten every declared rule across every plane into one iterable list. */
|
|
11
|
+
export function collectGateRules(planes) {
|
|
12
|
+
const out = [];
|
|
13
|
+
for (const r of planes.wiringRules ?? []) {
|
|
14
|
+
out.push({
|
|
15
|
+
id: r.id,
|
|
16
|
+
plane: 'wiring',
|
|
17
|
+
...(r.description ? { description: r.description } : {}),
|
|
18
|
+
severity: r.severity ?? 'error',
|
|
19
|
+
failOnEmpty: r.failOnEmpty === true,
|
|
20
|
+
...(r.selfTest ? { selfTest: r.selfTest } : {}),
|
|
21
|
+
raw: r,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
for (const r of planes.policyRules ?? []) {
|
|
25
|
+
out.push({
|
|
26
|
+
id: r.id,
|
|
27
|
+
plane: 'policy',
|
|
28
|
+
...(r.description ? { description: r.description } : {}),
|
|
29
|
+
severity: r.severity ?? 'error',
|
|
30
|
+
failOnEmpty: r.failOnEmpty === true,
|
|
31
|
+
...(r.selfTest ? { selfTest: r.selfTest } : {}),
|
|
32
|
+
raw: r,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
for (const r of planes.registries ?? []) {
|
|
36
|
+
out.push({
|
|
37
|
+
id: r.name,
|
|
38
|
+
plane: 'registry',
|
|
39
|
+
...(r.description ? { description: r.description } : {}),
|
|
40
|
+
// A registry is an inventory, not a gate — it never fails a build on its
|
|
41
|
+
// own, so it carries no severity of its own.
|
|
42
|
+
severity: 'warning',
|
|
43
|
+
failOnEmpty: false,
|
|
44
|
+
raw: r,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
for (const r of planes.registrationGraph ?? []) {
|
|
48
|
+
out.push({
|
|
49
|
+
id: r.name,
|
|
50
|
+
plane: 'registration',
|
|
51
|
+
...(r.description ? { description: r.description } : {}),
|
|
52
|
+
severity: 'warning',
|
|
53
|
+
failOnEmpty: false,
|
|
54
|
+
raw: r,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
for (const r of planes.baselines ?? []) {
|
|
58
|
+
out.push({
|
|
59
|
+
id: r.id,
|
|
60
|
+
plane: 'baseline',
|
|
61
|
+
...(r.description ? { description: r.description } : {}),
|
|
62
|
+
severity: r.severity ?? 'error',
|
|
63
|
+
failOnEmpty: r.failOnEmpty === true,
|
|
64
|
+
...(r.selfTest ? { selfTest: r.selfTest } : {}),
|
|
65
|
+
raw: r,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
for (const r of planes.generatedArtifacts ?? []) {
|
|
69
|
+
out.push({
|
|
70
|
+
id: r.id,
|
|
71
|
+
plane: 'generated',
|
|
72
|
+
...(r.description ? { description: r.description } : {}),
|
|
73
|
+
severity: r.severity ?? 'error',
|
|
74
|
+
failOnEmpty: r.failOnEmpty === true,
|
|
75
|
+
...(r.selfTest ? { selfTest: r.selfTest } : {}),
|
|
76
|
+
raw: r,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { IGateRuleView } from './gate-rule-view.js';
|
|
2
|
+
export declare const GATE_COVERAGE_SCHEMA: "sharkcraft.gate-coverage/v1";
|
|
3
|
+
/**
|
|
4
|
+
* Whether a rule is connected to anything at all.
|
|
5
|
+
*
|
|
6
|
+
* `empty` is the finding this whole surface exists for: a stale selector that
|
|
7
|
+
* matches nothing passes every gate forever, so the ONLY way to notice is to
|
|
8
|
+
* report the match count itself and flag zero. `failed-expectation` is the
|
|
9
|
+
* stronger form — the author wrote down what the rule should match, and it
|
|
10
|
+
* doesn't.
|
|
11
|
+
*/
|
|
12
|
+
export type GateCoverageStatus = 'ok' | 'empty' | 'error' | 'failed-expectation';
|
|
13
|
+
export interface IGateCoverage {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly plane: IGateRuleView['plane'];
|
|
16
|
+
readonly description?: string;
|
|
17
|
+
readonly status: GateCoverageStatus;
|
|
18
|
+
/** Files the rule's primary selector matched. */
|
|
19
|
+
readonly filesMatched: number;
|
|
20
|
+
/** Ids/units the rule extracted (findings scanned, for the policy plane). */
|
|
21
|
+
readonly unitsMatched: number;
|
|
22
|
+
/** What "units" means for this plane, for honest reporting. */
|
|
23
|
+
readonly unitLabel: string;
|
|
24
|
+
/** A few of the extracted ids, so the author can eyeball correctness. */
|
|
25
|
+
readonly sampleIds: readonly string[];
|
|
26
|
+
/** True when a zero match is a hard failure for this rule. */
|
|
27
|
+
readonly failOnEmpty: boolean;
|
|
28
|
+
readonly error?: string;
|
|
29
|
+
/** Unmet `selfTest` expectations, each a human-readable sentence. */
|
|
30
|
+
readonly expectationFailures: readonly string[];
|
|
31
|
+
}
|
|
32
|
+
export interface IGateCoverageReport {
|
|
33
|
+
readonly schema: typeof GATE_COVERAGE_SCHEMA;
|
|
34
|
+
readonly rules: readonly IGateCoverage[];
|
|
35
|
+
readonly total: number;
|
|
36
|
+
readonly empty: number;
|
|
37
|
+
readonly errored: number;
|
|
38
|
+
readonly expectationFailures: number;
|
|
39
|
+
/**
|
|
40
|
+
* `pass` — every rule matched something and met its expectations.
|
|
41
|
+
* `stale` — at least one rule matched nothing (or broke an expectation).
|
|
42
|
+
*/
|
|
43
|
+
readonly verdict: 'pass' | 'stale';
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve every declared rule against the live tree and report what it matched.
|
|
47
|
+
*
|
|
48
|
+
* The `command`-compute baseline is the one rule kind that cannot be inspected
|
|
49
|
+
* without side effects; it is reported as un-inspected (never as `empty`), so
|
|
50
|
+
* the report never claims a fact it did not check.
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildGateCoverage(cwd: string, rules: readonly IGateRuleView[], excludeDirs?: readonly string[]): IGateCoverageReport;
|
|
53
|
+
//# sourceMappingURL=rule-coverage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-coverage.d.ts","sourceRoot":"","sources":["../../src/gates/rule-coverage.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,eAAO,MAAM,oBAAoB,EAAG,6BAAsC,CAAC;AAE3E;;;;;;;;GAQG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG,OAAO,GAAG,OAAO,GAAG,oBAAoB,CAAC;AAEjF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IACvC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,iDAAiD;IACjD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,8DAA8D;IAC9D,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,QAAQ,CAAC,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;CACjD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,OAAO,oBAAoB,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACpC;AAsHD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,WAAW,GAAE,SAAS,MAAM,EAAO,GAClC,mBAAmB,CA4DrB"}
|