archcheck 0.2.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/LICENSE +21 -0
- package/README.md +536 -0
- package/dist/archcheck.d.ts +99 -0
- package/dist/archcheck.d.ts.map +1 -0
- package/dist/archcheck.js +339 -0
- package/dist/archcheck.js.map +1 -0
- package/dist/baseline/index.d.ts +29 -0
- package/dist/baseline/index.d.ts.map +1 -0
- package/dist/baseline/index.js +116 -0
- package/dist/baseline/index.js.map +1 -0
- package/dist/cli/args.d.ts +36 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +233 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/index.d.ts +4 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +219 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/version.d.ts +2 -0
- package/dist/cli/version.d.ts.map +1 -0
- package/dist/cli/version.js +29 -0
- package/dist/cli/version.js.map +1 -0
- package/dist/contracts/banned-syntax.d.ts +48 -0
- package/dist/contracts/banned-syntax.d.ts.map +1 -0
- package/dist/contracts/banned-syntax.js +201 -0
- package/dist/contracts/banned-syntax.js.map +1 -0
- package/dist/contracts/declared-field.d.ts +39 -0
- package/dist/contracts/declared-field.d.ts.map +1 -0
- package/dist/contracts/declared-field.js +142 -0
- package/dist/contracts/declared-field.js.map +1 -0
- package/dist/contracts/encapsulation.d.ts +28 -0
- package/dist/contracts/encapsulation.d.ts.map +1 -0
- package/dist/contracts/encapsulation.js +130 -0
- package/dist/contracts/encapsulation.js.map +1 -0
- package/dist/contracts/index.d.ts +10 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +9 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/judgement.d.ts +21 -0
- package/dist/contracts/judgement.d.ts.map +1 -0
- package/dist/contracts/judgement.js +109 -0
- package/dist/contracts/judgement.js.map +1 -0
- package/dist/contracts/layers.d.ts +75 -0
- package/dist/contracts/layers.d.ts.map +1 -0
- package/dist/contracts/layers.js +244 -0
- package/dist/contracts/layers.js.map +1 -0
- package/dist/contracts/naming.d.ts +21 -0
- package/dist/contracts/naming.d.ts.map +1 -0
- package/dist/contracts/naming.js +120 -0
- package/dist/contracts/naming.js.map +1 -0
- package/dist/contracts/registry.d.ts +23 -0
- package/dist/contracts/registry.d.ts.map +1 -0
- package/dist/contracts/registry.js +48 -0
- package/dist/contracts/registry.js.map +1 -0
- package/dist/contracts/required-file.d.ts +29 -0
- package/dist/contracts/required-file.d.ts.map +1 -0
- package/dist/contracts/required-file.js +131 -0
- package/dist/contracts/required-file.js.map +1 -0
- package/dist/contracts/types.d.ts +61 -0
- package/dist/contracts/types.d.ts.map +1 -0
- package/dist/contracts/types.js +2 -0
- package/dist/contracts/types.js.map +1 -0
- package/dist/coverage/index.d.ts +55 -0
- package/dist/coverage/index.d.ts.map +1 -0
- package/dist/coverage/index.js +68 -0
- package/dist/coverage/index.js.map +1 -0
- package/dist/emit/collect.d.ts +20 -0
- package/dist/emit/collect.d.ts.map +1 -0
- package/dist/emit/collect.js +17 -0
- package/dist/emit/collect.js.map +1 -0
- package/dist/emit/header.d.ts +21 -0
- package/dist/emit/header.d.ts.map +1 -0
- package/dist/emit/header.js +20 -0
- package/dist/emit/header.js.map +1 -0
- package/dist/emit/index.d.ts +57 -0
- package/dist/emit/index.d.ts.map +1 -0
- package/dist/emit/index.js +128 -0
- package/dist/emit/index.js.map +1 -0
- package/dist/emit/merge.d.ts +23 -0
- package/dist/emit/merge.d.ts.map +1 -0
- package/dist/emit/merge.js +71 -0
- package/dist/emit/merge.js.map +1 -0
- package/dist/emit/write.d.ts +20 -0
- package/dist/emit/write.d.ts.map +1 -0
- package/dist/emit/write.js +61 -0
- package/dist/emit/write.js.map +1 -0
- package/dist/exceptions/index.d.ts +46 -0
- package/dist/exceptions/index.d.ts.map +1 -0
- package/dist/exceptions/index.js +117 -0
- package/dist/exceptions/index.js.map +1 -0
- package/dist/fs/index.d.ts +12 -0
- package/dist/fs/index.d.ts.map +1 -0
- package/dist/fs/index.js +78 -0
- package/dist/fs/index.js.map +1 -0
- package/dist/graph/cache.d.ts +12 -0
- package/dist/graph/cache.d.ts.map +1 -0
- package/dist/graph/cache.js +76 -0
- package/dist/graph/cache.js.map +1 -0
- package/dist/graph/edges.d.ts +16 -0
- package/dist/graph/edges.d.ts.map +1 -0
- package/dist/graph/edges.js +161 -0
- package/dist/graph/edges.js.map +1 -0
- package/dist/graph/index.d.ts +15 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +90 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/match.d.ts +12 -0
- package/dist/graph/match.d.ts.map +1 -0
- package/dist/graph/match.js +35 -0
- package/dist/graph/match.js.map +1 -0
- package/dist/graph/program.d.ts +12 -0
- package/dist/graph/program.d.ts.map +1 -0
- package/dist/graph/program.js +54 -0
- package/dist/graph/program.js.map +1 -0
- package/dist/graph/types.d.ts +55 -0
- package/dist/graph/types.d.ts.map +1 -0
- package/dist/graph/types.js +2 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/init/index.d.ts +23 -0
- package/dist/init/index.d.ts.map +1 -0
- package/dist/init/index.js +218 -0
- package/dist/init/index.js.map +1 -0
- package/dist/normalize/identity.d.ts +4 -0
- package/dist/normalize/identity.d.ts.map +1 -0
- package/dist/normalize/identity.js +33 -0
- package/dist/normalize/identity.js.map +1 -0
- package/dist/normalize/index.d.ts +3 -0
- package/dist/normalize/index.d.ts.map +1 -0
- package/dist/normalize/index.js +3 -0
- package/dist/normalize/index.js.map +1 -0
- package/dist/normalize/normalize.d.ts +34 -0
- package/dist/normalize/normalize.d.ts.map +1 -0
- package/dist/normalize/normalize.js +91 -0
- package/dist/normalize/normalize.js.map +1 -0
- package/dist/probes/graph.d.ts +19 -0
- package/dist/probes/graph.d.ts.map +1 -0
- package/dist/probes/graph.js +181 -0
- package/dist/probes/graph.js.map +1 -0
- package/dist/probes/index.d.ts +5 -0
- package/dist/probes/index.d.ts.map +1 -0
- package/dist/probes/index.js +5 -0
- package/dist/probes/index.js.map +1 -0
- package/dist/probes/lint.d.ts +17 -0
- package/dist/probes/lint.d.ts.map +1 -0
- package/dist/probes/lint.js +121 -0
- package/dist/probes/lint.js.map +1 -0
- package/dist/probes/model/anthropic.d.ts +47 -0
- package/dist/probes/model/anthropic.d.ts.map +1 -0
- package/dist/probes/model/anthropic.js +100 -0
- package/dist/probes/model/anthropic.js.map +1 -0
- package/dist/probes/model/budget.d.ts +22 -0
- package/dist/probes/model/budget.d.ts.map +1 -0
- package/dist/probes/model/budget.js +27 -0
- package/dist/probes/model/budget.js.map +1 -0
- package/dist/probes/model/cache.d.ts +13 -0
- package/dist/probes/model/cache.d.ts.map +1 -0
- package/dist/probes/model/cache.js +74 -0
- package/dist/probes/model/cache.js.map +1 -0
- package/dist/probes/model/index.d.ts +20 -0
- package/dist/probes/model/index.d.ts.map +1 -0
- package/dist/probes/model/index.js +160 -0
- package/dist/probes/model/index.js.map +1 -0
- package/dist/probes/model/prompt.d.ts +16 -0
- package/dist/probes/model/prompt.d.ts.map +1 -0
- package/dist/probes/model/prompt.js +57 -0
- package/dist/probes/model/prompt.js.map +1 -0
- package/dist/probes/model/provider.d.ts +88 -0
- package/dist/probes/model/provider.d.ts.map +1 -0
- package/dist/probes/model/provider.js +48 -0
- package/dist/probes/model/provider.js.map +1 -0
- package/dist/probes/structure.d.ts +3 -0
- package/dist/probes/structure.d.ts.map +1 -0
- package/dist/probes/structure.js +95 -0
- package/dist/probes/structure.js.map +1 -0
- package/dist/probes/types.d.ts +46 -0
- package/dist/probes/types.d.ts.map +1 -0
- package/dist/probes/types.js +15 -0
- package/dist/probes/types.js.map +1 -0
- package/dist/registry/define.d.ts +40 -0
- package/dist/registry/define.d.ts.map +1 -0
- package/dist/registry/define.js +4 -0
- package/dist/registry/define.js.map +1 -0
- package/dist/registry/ids.d.ts +19 -0
- package/dist/registry/ids.d.ts.map +1 -0
- package/dist/registry/ids.js +86 -0
- package/dist/registry/ids.js.map +1 -0
- package/dist/registry/index.d.ts +45 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +71 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/load.d.ts +14 -0
- package/dist/registry/load.d.ts.map +1 -0
- package/dist/registry/load.js +108 -0
- package/dist/registry/load.js.map +1 -0
- package/dist/registry/validate.d.ts +24 -0
- package/dist/registry/validate.d.ts.map +1 -0
- package/dist/registry/validate.js +160 -0
- package/dist/registry/validate.js.map +1 -0
- package/dist/report/index.d.ts +10 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/index.js +30 -0
- package/dist/report/index.js.map +1 -0
- package/dist/report/json.d.ts +20 -0
- package/dist/report/json.d.ts.map +1 -0
- package/dist/report/json.js +34 -0
- package/dist/report/json.js.map +1 -0
- package/dist/report/sarif.d.ts +21 -0
- package/dist/report/sarif.d.ts.map +1 -0
- package/dist/report/sarif.js +138 -0
- package/dist/report/sarif.js.map +1 -0
- package/dist/report/terminal.d.ts +3 -0
- package/dist/report/terminal.d.ts.map +1 -0
- package/dist/report/terminal.js +117 -0
- package/dist/report/terminal.js.map +1 -0
- package/dist/report/types.d.ts +28 -0
- package/dist/report/types.d.ts.map +1 -0
- package/dist/report/types.js +11 -0
- package/dist/report/types.js.map +1 -0
- package/dist/report/verdict.d.ts +21 -0
- package/dist/report/verdict.d.ts.map +1 -0
- package/dist/report/verdict.js +31 -0
- package/dist/report/verdict.js.map +1 -0
- package/dist/result.d.ts +22 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +19 -0
- package/dist/result.js.map +1 -0
- package/dist/types.d.ts +178 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/dist/util/compilerApi.d.ts +2 -0
- package/dist/util/compilerApi.d.ts.map +1 -0
- package/dist/util/compilerApi.js +28 -0
- package/dist/util/compilerApi.js.map +1 -0
- package/dist/util/glob.d.ts +43 -0
- package/dist/util/glob.d.ts.map +1 -0
- package/dist/util/glob.js +195 -0
- package/dist/util/glob.js.map +1 -0
- package/dist/util/paths.d.ts +6 -0
- package/dist/util/paths.d.ts.map +1 -0
- package/dist/util/paths.js +22 -0
- package/dist/util/paths.js.map +1 -0
- package/dist/version.d.ts +13 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +12 -0
- package/dist/version.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest validation. Total, and it never throws.
|
|
3
|
+
*
|
|
4
|
+
* What rule this enforces: a malformed manifest exits 2, never 1 (LLD §6). An agent must be able
|
|
5
|
+
* to tell "the config is broken" from "the code is broken", or it will spend its turn editing the
|
|
6
|
+
* wrong file. That means every problem is returned together — one pass, every field named —
|
|
7
|
+
* rather than thrown one at a time.
|
|
8
|
+
*
|
|
9
|
+
* What a caller must know: the per-kind params check is INJECTED, not imported. The registry knows
|
|
10
|
+
* that a rule has a kind; only the contract type knows what that kind's params mean. Wiring the
|
|
11
|
+
* two together is the facade's job, which keeps this module testable without any kind at all.
|
|
12
|
+
*/
|
|
13
|
+
import { err, ok } from '../result.js';
|
|
14
|
+
import { CONTRACT_KINDS } from '../types.js';
|
|
15
|
+
import { globProblems } from '../util/glob.js';
|
|
16
|
+
const SEVERITIES = new Set(['error', 'warn', 'advisory', 'off']);
|
|
17
|
+
const CONFIDENCES = new Set(['deterministic', 'proxy', 'model']);
|
|
18
|
+
const ID_SHAPE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
19
|
+
/**
|
|
20
|
+
* Where each kind spells a path glob. A table rather than a per-kind branch, and it is still only
|
|
21
|
+
* WHERE, never what the field means — the registry knows a rule has a kind and nothing more (see
|
|
22
|
+
* this module's header). A kind absent from the table simply names no globs.
|
|
23
|
+
*/
|
|
24
|
+
const GLOB_FIELDS = {
|
|
25
|
+
layers: ['match'],
|
|
26
|
+
encapsulation: ['module'],
|
|
27
|
+
naming: ['appliesTo'],
|
|
28
|
+
'banned-syntax': ['appliesTo'],
|
|
29
|
+
'declared-field': ['appliesTo'],
|
|
30
|
+
judgement: ['appliesTo'],
|
|
31
|
+
'required-file': ['forEach'],
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* A `required-file` `forEach` may be `rule[severity=error]`, which selects RULES, not paths.
|
|
35
|
+
* Recognised here by its prefix rather than by importing `isRuleSelector`
|
|
36
|
+
* (`src/contracts/required-file.ts`), because `registry-knows-only-rules` in
|
|
37
|
+
* `architecture.config.ts` forbids `src/registry` importing `src/contracts` — and duplicating a
|
|
38
|
+
* five-character prefix is far cheaper than the alternative the layer law is protecting against.
|
|
39
|
+
* Deliberately looser than the real selector regex: anything selector-SHAPED is left alone here,
|
|
40
|
+
* and the kind's own validate owns the grammar.
|
|
41
|
+
*/
|
|
42
|
+
const RULE_SELECTOR_PREFIX = 'rule[';
|
|
43
|
+
/** A glob field is a string or a list of them; `layers.match` may instead be `{ implements }`. */
|
|
44
|
+
function globsIn(value) {
|
|
45
|
+
if (typeof value === 'string')
|
|
46
|
+
return [value];
|
|
47
|
+
if (Array.isArray(value))
|
|
48
|
+
return value.filter((entry) => typeof entry === 'string');
|
|
49
|
+
// `{ implements: 'Port' }` names a type, not a path. Any other shape is the kind's to complain
|
|
50
|
+
// about; this pass only checks patterns that really are patterns.
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
function checkGlobs(field, value, push) {
|
|
54
|
+
for (const pattern of globsIn(value)) {
|
|
55
|
+
if (pattern.startsWith(RULE_SELECTOR_PREFIX))
|
|
56
|
+
continue;
|
|
57
|
+
for (const problem of globProblems(pattern)) {
|
|
58
|
+
push(field, `pattern '${pattern}' ${problem}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function at(index, id) {
|
|
63
|
+
return typeof id === 'string' && id.length > 0 ? `rules[${index}] '${id}'` : `rules[${index}]`;
|
|
64
|
+
}
|
|
65
|
+
function validateRule(rule, index, validateParams, allRules) {
|
|
66
|
+
const where = at(index, rule?.id);
|
|
67
|
+
const errors = [];
|
|
68
|
+
const push = (field, problem) => errors.push(`${where}: ${field} ${problem}`);
|
|
69
|
+
if (typeof rule?.id !== 'string' || rule.id.length === 0) {
|
|
70
|
+
push('id', 'is required and must be a non-empty string');
|
|
71
|
+
}
|
|
72
|
+
else if (!ID_SHAPE.test(rule.id)) {
|
|
73
|
+
push('id', `must be kebab-case (got '${rule.id}') — ids appear in baselines and annotations`);
|
|
74
|
+
}
|
|
75
|
+
if (!CONTRACT_KINDS.includes(rule?.kind)) {
|
|
76
|
+
push('kind', `must be one of ${CONTRACT_KINDS.join(', ')} (got ${JSON.stringify(rule?.kind)})`);
|
|
77
|
+
}
|
|
78
|
+
if (typeof rule?.statement !== 'string' || rule.statement.trim().length === 0) {
|
|
79
|
+
push('statement', 'is required — it is the sentence the editor shows');
|
|
80
|
+
}
|
|
81
|
+
if (!SEVERITIES.has(rule?.severity)) {
|
|
82
|
+
push('severity', `must be error | warn | advisory | off (got ${JSON.stringify(rule?.severity)})`);
|
|
83
|
+
}
|
|
84
|
+
if (!CONFIDENCES.has(rule?.confidence)) {
|
|
85
|
+
push('confidence', `must be deterministic | proxy | model (got ${JSON.stringify(rule?.confidence)})`);
|
|
86
|
+
}
|
|
87
|
+
if (typeof rule?.remedy?.summary !== 'string' || rule.remedy.summary.trim().length === 0) {
|
|
88
|
+
push('remedy.summary', 'is required — a finding without a remedy makes the reader guess');
|
|
89
|
+
}
|
|
90
|
+
if (typeof rule?.params !== 'object' || rule.params === null || Array.isArray(rule.params)) {
|
|
91
|
+
push('params', 'must be an object');
|
|
92
|
+
}
|
|
93
|
+
if (rule?.expires !== undefined) {
|
|
94
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(rule.expires)) {
|
|
95
|
+
push('expires', `must be an ISO date, YYYY-MM-DD (got '${rule.expires}')`);
|
|
96
|
+
}
|
|
97
|
+
if (typeof rule.owner !== 'string' || rule.owner.length === 0) {
|
|
98
|
+
push('owner', 'is required whenever expires is set — an unowned allowance is never reviewed');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Only ask the kind about params once the envelope is sound; otherwise the kind's complaint
|
|
102
|
+
// is noise on top of a rule that is already malformed.
|
|
103
|
+
if (errors.length === 0) {
|
|
104
|
+
const kindResult = validateParams(rule, allRules);
|
|
105
|
+
if (!kindResult.ok)
|
|
106
|
+
for (const problem of kindResult.error)
|
|
107
|
+
errors.push(`${where}: ${problem}`);
|
|
108
|
+
}
|
|
109
|
+
// Glob well-formedness, in the SHARED pass rather than in any kind's validate: a malformed
|
|
110
|
+
// pattern matches nothing, and outside `layers.match` a rule that matches nothing is
|
|
111
|
+
// indistinguishable from a rule that passes (AGENTS.md rule 14). Here, every present and future
|
|
112
|
+
// kind gets the check without opting in. Last, so a bad pattern never hides the kind's own
|
|
113
|
+
// complaints. `globProblems` (src/util/glob.ts) decides what counts as malformed.
|
|
114
|
+
if (typeof rule?.params === 'object' && rule.params !== null) {
|
|
115
|
+
const params = rule.params;
|
|
116
|
+
for (const field of GLOB_FIELDS[rule.kind] ?? []) {
|
|
117
|
+
checkGlobs(`params.${field}`, params[field], push);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return errors;
|
|
121
|
+
}
|
|
122
|
+
export function validateManifest(manifest, validateParams) {
|
|
123
|
+
const errors = [];
|
|
124
|
+
if (typeof manifest !== 'object' || manifest === null) {
|
|
125
|
+
return err(['the manifest must export a defineArchitecture({ ... }) object as its default']);
|
|
126
|
+
}
|
|
127
|
+
if (!Array.isArray(manifest.rules)) {
|
|
128
|
+
return err(['manifest.rules must be an array — it is the only place a rule is defined']);
|
|
129
|
+
}
|
|
130
|
+
const seen = new Map();
|
|
131
|
+
for (const [index, rule] of manifest.rules.entries()) {
|
|
132
|
+
errors.push(...validateRule(rule, index, validateParams, manifest.rules));
|
|
133
|
+
const id = rule?.id;
|
|
134
|
+
if (typeof id === 'string') {
|
|
135
|
+
const first = seen.get(id);
|
|
136
|
+
if (first !== undefined) {
|
|
137
|
+
errors.push(`rules[${index}] '${id}': duplicate id — already defined at rules[${first}]`);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
seen.set(id, index);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
for (const [index, rule] of manifest.rules.entries()) {
|
|
145
|
+
if (rule?.replaces !== undefined && seen.has(rule.replaces)) {
|
|
146
|
+
errors.push(`rules[${index}] '${rule.id}': replaces '${rule.replaces}', which still exists — ` +
|
|
147
|
+
'a replaced id must be gone, or nothing was replaced');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// An exception's `path` is a glob like any other, and an exception that matches nothing suppresses
|
|
151
|
+
// nothing while still reading as a granted allowance. The rest of the exception's shape belongs to
|
|
152
|
+
// `validateExceptions` (src/exceptions/index.ts); only the pattern is checked here, so the glob
|
|
153
|
+
// check stays in one place for every field of the manifest.
|
|
154
|
+
const exceptions = Array.isArray(manifest.exceptions) ? manifest.exceptions : [];
|
|
155
|
+
for (const [index, exception] of exceptions.entries()) {
|
|
156
|
+
checkGlobs('path', exception?.path, (field, problem) => errors.push(`exceptions[${index}]: ${field} ${problem}`));
|
|
157
|
+
}
|
|
158
|
+
return errors.length > 0 ? err(errors) : ok(manifest);
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/registry/validate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Reporter } from './types.js';
|
|
2
|
+
export type ReporterName = 'terminal' | 'json' | 'json-pretty' | 'sarif' | 'verdict';
|
|
3
|
+
export declare function reporterFor(name: ReporterName): Reporter;
|
|
4
|
+
export declare function reporterNames(): readonly ReporterName[];
|
|
5
|
+
export { createJsonReporter, type JsonReport, toJsonReport } from './json.js';
|
|
6
|
+
export { createSarifReporter, SARIF_VERSION, toSarif } from './sarif.js';
|
|
7
|
+
export { createTerminalReporter } from './terminal.js';
|
|
8
|
+
export { CONFIDENCE_ORDER, type ReportCtx, type Reporter, visible } from './types.js';
|
|
9
|
+
export { createVerdictReporter, type Verdict, verdictFor } from './verdict.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/report/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,MAAM,GAAG,aAAa,GAAG,OAAO,GAAG,SAAS,CAAC;AAErF,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,CAaxD;AAED,wBAAgB,aAAa,IAAI,SAAS,YAAY,EAAE,CAEvD;AAED,OAAO,EAAE,kBAAkB,EAAE,KAAK,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACtF,OAAO,EAAE,qBAAqB,EAAE,KAAK,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The reporter registry. A new audience is one entry here and nothing else.
|
|
3
|
+
*/
|
|
4
|
+
import { createJsonReporter } from './json.js';
|
|
5
|
+
import { createSarifReporter } from './sarif.js';
|
|
6
|
+
import { createTerminalReporter } from './terminal.js';
|
|
7
|
+
import { createVerdictReporter } from './verdict.js';
|
|
8
|
+
export function reporterFor(name) {
|
|
9
|
+
switch (name) {
|
|
10
|
+
case 'json':
|
|
11
|
+
return createJsonReporter();
|
|
12
|
+
case 'json-pretty':
|
|
13
|
+
return createJsonReporter({ pretty: true });
|
|
14
|
+
case 'sarif':
|
|
15
|
+
return createSarifReporter();
|
|
16
|
+
case 'verdict':
|
|
17
|
+
return createVerdictReporter();
|
|
18
|
+
case 'terminal':
|
|
19
|
+
return createTerminalReporter();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function reporterNames() {
|
|
23
|
+
return ['terminal', 'json', 'json-pretty', 'sarif', 'verdict'];
|
|
24
|
+
}
|
|
25
|
+
export { createJsonReporter, toJsonReport } from './json.js';
|
|
26
|
+
export { createSarifReporter, SARIF_VERSION, toSarif } from './sarif.js';
|
|
27
|
+
export { createTerminalReporter } from './terminal.js';
|
|
28
|
+
export { CONFIDENCE_ORDER, visible } from './types.js';
|
|
29
|
+
export { createVerdictReporter, verdictFor } from './verdict.js';
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/report/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAEvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAIrD,MAAM,UAAU,WAAW,CAAC,IAAkB;IAC5C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,MAAM;YACT,OAAO,kBAAkB,EAAE,CAAC;QAC9B,KAAK,aAAa;YAChB,OAAO,kBAAkB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,KAAK,OAAO;YACV,OAAO,mBAAmB,EAAE,CAAC;QAC/B,KAAK,SAAS;YACZ,OAAO,qBAAqB,EAAE,CAAC;QACjC,KAAK,UAAU;YACb,OAAO,sBAAsB,EAAE,CAAC;IACpC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;AACjE,CAAC;AAED,OAAO,EAAE,kBAAkB,EAAmB,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAiC,OAAO,EAAE,MAAM,YAAY,CAAC;AACtF,OAAO,EAAE,qBAAqB,EAAgB,UAAU,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The JSON reporter — the agent contract.
|
|
3
|
+
*
|
|
4
|
+
* What rule this enforces: three properties, each of which an agent depends on (plan 08).
|
|
5
|
+
* 1. `schemaVersion` is always present, so a consumer knows what it is reading.
|
|
6
|
+
* 2. `skipped` is always present, even when empty — an agent must be able to tell "clean" from
|
|
7
|
+
* "did not run". A missing key is the difference between "no violations" and "no check".
|
|
8
|
+
* 3. Not pretty-printed by default. It is a wire format; a human who wants to read it pipes it.
|
|
9
|
+
*/
|
|
10
|
+
import type { RunResult } from '../types.js';
|
|
11
|
+
import { SCHEMA_VERSION } from '../version.js';
|
|
12
|
+
import type { ReportCtx, Reporter } from './types.js';
|
|
13
|
+
export type JsonReport = RunResult & {
|
|
14
|
+
schemaVersion: typeof SCHEMA_VERSION;
|
|
15
|
+
};
|
|
16
|
+
export declare function toJsonReport(result: RunResult, ctx: ReportCtx): JsonReport;
|
|
17
|
+
export declare function createJsonReporter(options?: {
|
|
18
|
+
pretty?: boolean;
|
|
19
|
+
}): Reporter;
|
|
20
|
+
//# sourceMappingURL=json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/report/json.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtD,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG;IAAE,aAAa,EAAE,OAAO,cAAc,CAAA;CAAE,CAAC;AAE9E,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,UAAU,CAY1E;AAED,wBAAgB,kBAAkB,CAAC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,QAAQ,CAQ/E"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The JSON reporter — the agent contract.
|
|
3
|
+
*
|
|
4
|
+
* What rule this enforces: three properties, each of which an agent depends on (plan 08).
|
|
5
|
+
* 1. `schemaVersion` is always present, so a consumer knows what it is reading.
|
|
6
|
+
* 2. `skipped` is always present, even when empty — an agent must be able to tell "clean" from
|
|
7
|
+
* "did not run". A missing key is the difference between "no violations" and "no check".
|
|
8
|
+
* 3. Not pretty-printed by default. It is a wire format; a human who wants to read it pipes it.
|
|
9
|
+
*/
|
|
10
|
+
import { SCHEMA_VERSION } from '../version.js';
|
|
11
|
+
import { visible } from './types.js';
|
|
12
|
+
export function toJsonReport(result, ctx) {
|
|
13
|
+
return {
|
|
14
|
+
schemaVersion: SCHEMA_VERSION,
|
|
15
|
+
ok: result.ok,
|
|
16
|
+
diagnostics: visible(result, ctx),
|
|
17
|
+
skipped: result.skipped,
|
|
18
|
+
coverage: result.coverage,
|
|
19
|
+
// Both are present even when empty. An agent distinguishing "clean" from "the baseline needs
|
|
20
|
+
// pruning" from "an exception expired" needs all three states, and a missing key gives it one.
|
|
21
|
+
baselineStale: result.baselineStale,
|
|
22
|
+
exceptions: result.exceptions,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function createJsonReporter(options = {}) {
|
|
26
|
+
return {
|
|
27
|
+
name: 'json',
|
|
28
|
+
async report(result, ctx) {
|
|
29
|
+
const report = toJsonReport(result, ctx);
|
|
30
|
+
return `${JSON.stringify(report, null, options.pretty === true ? 2 : undefined)}\n`;
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/report/json.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAIrC,MAAM,UAAU,YAAY,CAAC,MAAiB,EAAE,GAAc;IAC5D,OAAO;QACL,aAAa,EAAE,cAAc;QAC7B,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,WAAW,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;QACjC,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,6FAA6F;QAC7F,+FAA+F;QAC/F,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,UAAU,EAAE,MAAM,CAAC,UAAU;KAC9B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,UAAgC,EAAE;IACnE,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,KAAK,CAAC,MAAM,CAAC,MAAiB,EAAE,GAAc;YAC5C,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YACzC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC;QACtF,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SARIF reporter — the CI and editor format.
|
|
3
|
+
*
|
|
4
|
+
* Why this and not our own shape: code scanning, editor viewers and run-to-run diffing all consume
|
|
5
|
+
* SARIF natively. Writing it once is cheaper than any one of those integrations (LLD §1.10).
|
|
6
|
+
*
|
|
7
|
+
* What rule this enforces: the PROPERTIES BAG carries what SARIF has no field for — confidence,
|
|
8
|
+
* the identity hash, the remedy, and whether a finding is baselined or exempted. Anything reading
|
|
9
|
+
* plain SARIF still gets file, line, rule and message, so nothing is lost either way.
|
|
10
|
+
*
|
|
11
|
+
* Severity mapping is opinionated and deliberate:
|
|
12
|
+
* error → error · warn → warning · advisory → note
|
|
13
|
+
* A model verdict therefore lands as a `note` in CI whatever its rule severity says, which is the
|
|
14
|
+
* advisory-by-default policy expressed where CI can actually see it.
|
|
15
|
+
*/
|
|
16
|
+
import type { RunResult } from '../types.js';
|
|
17
|
+
import type { ReportCtx, Reporter } from './types.js';
|
|
18
|
+
export declare const SARIF_VERSION = "2.1.0";
|
|
19
|
+
export declare function toSarif(result: RunResult, ctx: ReportCtx): Record<string, unknown>;
|
|
20
|
+
export declare function createSarifReporter(): Reporter;
|
|
21
|
+
//# sourceMappingURL=sarif.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.d.ts","sourceRoot":"","sources":["../../src/report/sarif.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAA0B,SAAS,EAAY,MAAM,aAAa,CAAC;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtD,eAAO,MAAM,aAAa,UAAU,CAAC;AAiDrC,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmFlF;AAED,wBAAgB,mBAAmB,IAAI,QAAQ,CAO9C"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { visible } from './types.js';
|
|
2
|
+
export const SARIF_VERSION = '2.1.0';
|
|
3
|
+
const SARIF_SCHEMA = 'https://json.schemastore.org/sarif-2.1.0.json';
|
|
4
|
+
const BY_SEVERITY = {
|
|
5
|
+
error: 'error',
|
|
6
|
+
warn: 'warning',
|
|
7
|
+
advisory: 'note',
|
|
8
|
+
off: 'none',
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* A MODEL verdict is never worse than a note, whatever its rule severity says. Promoting a
|
|
12
|
+
* judgement rule to `error` is allowed — it is policy — but a non-deterministic gate that blocks
|
|
13
|
+
* CI is how a check suite loses trust, so the ceiling is applied here, where CI can see it.
|
|
14
|
+
*/
|
|
15
|
+
function levelFor(severity, confidence) {
|
|
16
|
+
const level = BY_SEVERITY[severity];
|
|
17
|
+
if (confidence !== 'model')
|
|
18
|
+
return level;
|
|
19
|
+
return level === 'error' || level === 'warning' ? 'note' : level;
|
|
20
|
+
}
|
|
21
|
+
function ruleDescriptorsFor(diagnostics) {
|
|
22
|
+
const byRule = new Map();
|
|
23
|
+
for (const diagnostic of diagnostics) {
|
|
24
|
+
if (!byRule.has(diagnostic.ruleId))
|
|
25
|
+
byRule.set(diagnostic.ruleId, diagnostic);
|
|
26
|
+
}
|
|
27
|
+
return [...byRule.entries()]
|
|
28
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
29
|
+
.map(([ruleId, diagnostic]) => ({
|
|
30
|
+
id: ruleId,
|
|
31
|
+
name: ruleId,
|
|
32
|
+
shortDescription: { text: diagnostic.statement },
|
|
33
|
+
fullDescription: { text: `${diagnostic.statement} — ${diagnostic.remedy.summary}` },
|
|
34
|
+
help: {
|
|
35
|
+
text: diagnostic.remedy.summary,
|
|
36
|
+
...(diagnostic.remedy.docs === undefined
|
|
37
|
+
? {}
|
|
38
|
+
: { markdown: `[docs](${diagnostic.remedy.docs})` }),
|
|
39
|
+
},
|
|
40
|
+
defaultConfiguration: { level: levelFor(diagnostic.severity, diagnostic.confidence) },
|
|
41
|
+
properties: {
|
|
42
|
+
confidence: diagnostic.confidence,
|
|
43
|
+
...(diagnostic.remedy.allowed === undefined ? {} : { allowed: diagnostic.remedy.allowed }),
|
|
44
|
+
},
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
export function toSarif(result, ctx) {
|
|
48
|
+
// Baselined and exempted findings are INCLUDED here, marked rather than dropped. CI is where a
|
|
49
|
+
// reviewer looks at suppressed debt, and a format that hides it makes the suppression invisible.
|
|
50
|
+
const diagnostics = visible(result, { ...ctx, showBaselined: true });
|
|
51
|
+
return {
|
|
52
|
+
$schema: SARIF_SCHEMA,
|
|
53
|
+
version: SARIF_VERSION,
|
|
54
|
+
runs: [
|
|
55
|
+
{
|
|
56
|
+
tool: {
|
|
57
|
+
driver: {
|
|
58
|
+
name: 'archcheck',
|
|
59
|
+
informationUri: 'https://github.com/miteshsharma/archcheck',
|
|
60
|
+
rules: ruleDescriptorsFor(diagnostics),
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
results: diagnostics.map((diagnostic) => ({
|
|
64
|
+
ruleId: diagnostic.ruleId,
|
|
65
|
+
level: levelFor(diagnostic.severity, diagnostic.confidence),
|
|
66
|
+
message: { text: `${diagnostic.statement} — ${diagnostic.remedy.summary}` },
|
|
67
|
+
locations: [
|
|
68
|
+
{
|
|
69
|
+
physicalLocation: {
|
|
70
|
+
artifactLocation: { uri: diagnostic.file },
|
|
71
|
+
...(diagnostic.line === undefined
|
|
72
|
+
? {}
|
|
73
|
+
: {
|
|
74
|
+
region: {
|
|
75
|
+
startLine: diagnostic.line,
|
|
76
|
+
...(diagnostic.col === undefined ? {} : { startColumn: diagnostic.col }),
|
|
77
|
+
...(diagnostic.endLine === undefined
|
|
78
|
+
? {}
|
|
79
|
+
: { endLine: diagnostic.endLine }),
|
|
80
|
+
},
|
|
81
|
+
}),
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
// A suppressed finding is reported to SARIF as suppressed, which is the field consumers
|
|
86
|
+
// already understand — rather than being silently absent.
|
|
87
|
+
...(diagnostic.baselined || diagnostic.exempted
|
|
88
|
+
? {
|
|
89
|
+
suppressions: [
|
|
90
|
+
{
|
|
91
|
+
kind: 'external',
|
|
92
|
+
justification: diagnostic.exempted
|
|
93
|
+
? 'covered by a declared exception in architecture.config.ts'
|
|
94
|
+
: 'recorded in .archcheck/baseline.json as pre-existing',
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
}
|
|
98
|
+
: {}),
|
|
99
|
+
partialFingerprints: { archcheckIdentity: diagnostic.identity },
|
|
100
|
+
properties: {
|
|
101
|
+
confidence: diagnostic.confidence,
|
|
102
|
+
severity: diagnostic.severity,
|
|
103
|
+
identity: diagnostic.identity,
|
|
104
|
+
detectedBy: diagnostic.detectedBy,
|
|
105
|
+
baselined: diagnostic.baselined,
|
|
106
|
+
exempted: diagnostic.exempted,
|
|
107
|
+
// SARIF has no field for "this result stands for N of the same thing", so the count
|
|
108
|
+
// and its lines ride in the properties bag like our other extras.
|
|
109
|
+
occurrences: diagnostic.occurrences ?? 1,
|
|
110
|
+
occurrenceLines: diagnostic.occurrenceLines ?? [],
|
|
111
|
+
remedy: diagnostic.remedy,
|
|
112
|
+
evidence: diagnostic.evidence,
|
|
113
|
+
},
|
|
114
|
+
})),
|
|
115
|
+
invocations: [
|
|
116
|
+
{
|
|
117
|
+
executionSuccessful: result.ok,
|
|
118
|
+
properties: {
|
|
119
|
+
coverage: result.coverage,
|
|
120
|
+
skipped: result.skipped,
|
|
121
|
+
baselineStale: result.baselineStale,
|
|
122
|
+
exceptions: result.exceptions,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export function createSarifReporter() {
|
|
131
|
+
return {
|
|
132
|
+
name: 'sarif',
|
|
133
|
+
async report(result, ctx) {
|
|
134
|
+
return `${JSON.stringify(toSarif(result, ctx), null, 2)}\n`;
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=sarif.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.js","sourceRoot":"","sources":["../../src/report/sarif.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/report/terminal.ts"],"names":[],"mappings":"AAWA,OAAO,EAAoC,KAAK,QAAQ,EAAW,MAAM,YAAY,CAAC;AAuDtF,wBAAgB,sBAAsB,IAAI,QAAQ,CAgGjD"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { CONFIDENCE_ORDER, visible } from './types.js';
|
|
2
|
+
const MARK = { deterministic: '✗', proxy: '◑', model: '○' };
|
|
3
|
+
const LABEL = {
|
|
4
|
+
deterministic: 'deterministic',
|
|
5
|
+
proxy: 'proxy · the check is narrower than the rule',
|
|
6
|
+
model: 'advisory',
|
|
7
|
+
};
|
|
8
|
+
function palette(noColor) {
|
|
9
|
+
const wrap = (code) => (text) => noColor ? text : `\u001B[${code}m${text}\u001B[0m`;
|
|
10
|
+
return { dim: wrap('2'), bold: wrap('1'), red: wrap('31'), yellow: wrap('33') };
|
|
11
|
+
}
|
|
12
|
+
function groupOf(diagnostics, confidence) {
|
|
13
|
+
return diagnostics.filter((d) => d.confidence === confidence);
|
|
14
|
+
}
|
|
15
|
+
function renderOne(diagnostic, dim) {
|
|
16
|
+
const position = diagnostic.line === undefined ? '' : `:${diagnostic.line}`;
|
|
17
|
+
// padEnd alone collides when the statement is longer than the column, so the gap is explicit.
|
|
18
|
+
const statement = diagnostic.statement.padEnd(50);
|
|
19
|
+
// Severity is stated per finding, not only per group. Groups are ordered by CONFIDENCE, so a
|
|
20
|
+
// `warn` rule appears under the deterministic heading beside a blocking one — and a reader who
|
|
21
|
+
// sees only the ✗ concludes the build is broken when the exit code says it is not. That
|
|
22
|
+
// misreading lands squarely on the adoption path, where most rules start at `warn`.
|
|
23
|
+
const blocks = diagnostic.severity === 'error';
|
|
24
|
+
const tag = blocks ? '' : ` ${dim(`(${diagnostic.severity})`)}`;
|
|
25
|
+
const lines = [
|
|
26
|
+
` ${statement}${statement.endsWith(' ') ? '' : ' '}${dim(diagnostic.ruleId)}${tag}`,
|
|
27
|
+
` ${diagnostic.file}${position}`,
|
|
28
|
+
` → ${diagnostic.remedy.summary}`,
|
|
29
|
+
];
|
|
30
|
+
if (diagnostic.remedy.allowed && diagnostic.remedy.allowed.length > 0) {
|
|
31
|
+
lines.splice(2, 0, ` ${dim(`allowed: ${diagnostic.remedy.allowed.join(', ')}`)}`);
|
|
32
|
+
}
|
|
33
|
+
// One identity can stand for several violations — three empty catches in one file are one hash
|
|
34
|
+
// (see `Diagnostic.occurrences`). The count and the lines are stated, or a reader fixes the one
|
|
35
|
+
// position printed above and wonders why the run still fails.
|
|
36
|
+
const occurrences = diagnostic.occurrences ?? 1;
|
|
37
|
+
if (occurrences > 1) {
|
|
38
|
+
const where = diagnostic.occurrenceLines === undefined || diagnostic.occurrenceLines.length === 0
|
|
39
|
+
? ''
|
|
40
|
+
: `: lines ${diagnostic.occurrenceLines.join(', ')}`;
|
|
41
|
+
lines.splice(2, 0, ` ${dim(`${occurrences} occurrences${where}`)}`);
|
|
42
|
+
}
|
|
43
|
+
return lines;
|
|
44
|
+
}
|
|
45
|
+
export function createTerminalReporter() {
|
|
46
|
+
return {
|
|
47
|
+
name: 'terminal',
|
|
48
|
+
async report(result, ctx) {
|
|
49
|
+
const { dim, bold, red, yellow } = palette(ctx.noColor === true);
|
|
50
|
+
const shown = visible(result, ctx);
|
|
51
|
+
const out = [];
|
|
52
|
+
for (const confidence of CONFIDENCE_ORDER) {
|
|
53
|
+
const group = groupOf(shown, confidence);
|
|
54
|
+
const mark = MARK[confidence];
|
|
55
|
+
const blockingCount = group.filter((diagnostic) => diagnostic.severity === 'error').length;
|
|
56
|
+
const suffix = group.length === 0 || blockingCount === group.length
|
|
57
|
+
? ''
|
|
58
|
+
: ` (${blockingCount} blocking)`;
|
|
59
|
+
const heading = `${mark} ${group.length} ${LABEL[confidence]}${suffix}`;
|
|
60
|
+
if (group.length === 0) {
|
|
61
|
+
out.push(dim(heading));
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// Red only when something in the group actually blocks.
|
|
65
|
+
const blocking = group.some((diagnostic) => diagnostic.severity === 'error');
|
|
66
|
+
out.push(bold(blocking ? red(heading) : heading));
|
|
67
|
+
out.push('');
|
|
68
|
+
for (const diagnostic of group) {
|
|
69
|
+
out.push(...renderOne(diagnostic, dim));
|
|
70
|
+
out.push('');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const baselined = result.diagnostics.filter((d) => d.baselined && !d.exempted).length;
|
|
74
|
+
const exempted = result.diagnostics.filter((d) => d.exempted).length;
|
|
75
|
+
if ((baselined > 0 || exempted > 0) && ctx.showBaselined !== true) {
|
|
76
|
+
const parts = [
|
|
77
|
+
baselined > 0 ? `${baselined} baselined` : '',
|
|
78
|
+
exempted > 0 ? `${exempted} exempted` : '',
|
|
79
|
+
].filter(Boolean);
|
|
80
|
+
out.push(dim(` ${parts.join(', ')}, not shown --show-baselined to include`));
|
|
81
|
+
}
|
|
82
|
+
// Always, including the exceptions suppressing nothing: a silent exception decays into a
|
|
83
|
+
// permanent one (plan 15).
|
|
84
|
+
if (result.exceptions.length > 0) {
|
|
85
|
+
const expiringSoon = result.exceptions.filter((e) => !e.expired && e.daysLeft <= 30).length;
|
|
86
|
+
const active = result.exceptions.filter((e) => !e.expired).length;
|
|
87
|
+
out.push(dim(` ${active} exception(s) active${expiringSoon > 0 ? `, ${expiringSoon} expiring within 30 days` : ''}`));
|
|
88
|
+
for (const expired of result.exceptions.filter((e) => e.expired)) {
|
|
89
|
+
out.push(red(` ✗ exception expired ${expired.expires} for ${expired.ruleId} — owner ${expired.owner}`));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Exit 4's explanation, in the place a person is already reading.
|
|
93
|
+
if (result.baselineStale.length > 0) {
|
|
94
|
+
out.push(red(` ✗ ${result.baselineStale.length} baselined finding(s) no longer reproduce`));
|
|
95
|
+
for (const entry of result.baselineStale.slice(0, 5)) {
|
|
96
|
+
out.push(dim(` ${entry.ruleId} ${entry.identity} recorded ${entry.addedAt}`));
|
|
97
|
+
}
|
|
98
|
+
out.push(dim(' → run: npx archcheck baseline --prune'));
|
|
99
|
+
}
|
|
100
|
+
for (const skip of result.skipped) {
|
|
101
|
+
out.push(yellow(` - ${skip.probe} did not run: ${skip.reason}`));
|
|
102
|
+
}
|
|
103
|
+
const { total, deterministic, proxy, model, unchecked } = result.coverage;
|
|
104
|
+
out.push(dim(` ${total} rules · ${deterministic} deterministic · ${proxy} proxy · ${model} advisory` +
|
|
105
|
+
(unchecked > 0 ? ` · ${unchecked} unchecked` : '')));
|
|
106
|
+
if (shown.length === 0 &&
|
|
107
|
+
result.skipped.length === 0 &&
|
|
108
|
+
result.baselineStale.length === 0 &&
|
|
109
|
+
!result.exceptions.some((e) => e.expired)) {
|
|
110
|
+
out.push('');
|
|
111
|
+
out.push(' nothing to fix');
|
|
112
|
+
}
|
|
113
|
+
return `${out.join('\n')}\n`;
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/report/terminal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The third seam: a reporter. A new audience changes nothing upstream.
|
|
3
|
+
*
|
|
4
|
+
* What a caller must know: a reporter RETURNS its text rather than printing it. That is what makes
|
|
5
|
+
* the facade usable from an agent hook without capturing stdout, and it is why `report()` may
|
|
6
|
+
* return void only for a reporter whose whole job is a side effect (writing a SARIF file).
|
|
7
|
+
*/
|
|
8
|
+
import type { Diagnostic, RunResult } from '../types.js';
|
|
9
|
+
export type ReportCtx = {
|
|
10
|
+
/** Include findings the baseline already knows about. */
|
|
11
|
+
showBaselined?: boolean;
|
|
12
|
+
/** Suppress colour. Set from `NO_COLOR` or a non-TTY stdout by the CLI, never guessed here. */
|
|
13
|
+
noColor?: boolean;
|
|
14
|
+
root?: string;
|
|
15
|
+
};
|
|
16
|
+
export type Reporter = {
|
|
17
|
+
name: string;
|
|
18
|
+
report(result: RunResult, ctx: ReportCtx): Promise<string | undefined>;
|
|
19
|
+
};
|
|
20
|
+
/** Deterministic failures first, then proxies, then advisories — ordered by authority. */
|
|
21
|
+
export declare const CONFIDENCE_ORDER: readonly ["deterministic", "proxy", "model"];
|
|
22
|
+
/**
|
|
23
|
+
* What a reader should see. Baselined and exempted findings are suppressed by their own mechanisms
|
|
24
|
+
* and counted rather than printed — but they are counted, and the count is always shown, because a
|
|
25
|
+
* suppression nobody can see is one nobody reviews.
|
|
26
|
+
*/
|
|
27
|
+
export declare function visible(result: RunResult, ctx: ReportCtx): Diagnostic[];
|
|
28
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/report/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,MAAM,SAAS,GAAG;IACtB,yDAAyD;IACzD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,+FAA+F;IAC/F,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CACxE,CAAC;AAEF,0FAA0F;AAC1F,eAAO,MAAM,gBAAgB,8CAA+C,CAAC;AAE7E;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,UAAU,EAAE,CAIvE"}
|