@spikedpunch/align-core 0.1.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 +27 -0
- package/dist/baseline/fingerprint.d.ts +19 -0
- package/dist/baseline/fingerprint.d.ts.map +1 -0
- package/dist/baseline/fingerprint.js +0 -0
- package/dist/baseline/fingerprint.js.map +1 -0
- package/dist/baseline/store.d.ts +81 -0
- package/dist/baseline/store.d.ts.map +1 -0
- package/dist/baseline/store.js +111 -0
- package/dist/baseline/store.js.map +1 -0
- package/dist/build/diff.d.ts +22 -0
- package/dist/build/diff.d.ts.map +1 -0
- package/dist/build/diff.js +61 -0
- package/dist/build/diff.js.map +1 -0
- package/dist/build/export-ir.d.ts +11 -0
- package/dist/build/export-ir.d.ts.map +1 -0
- package/dist/build/export-ir.js +4 -0
- package/dist/build/export-ir.js.map +1 -0
- package/dist/build/ground.d.ts +32 -0
- package/dist/build/ground.d.ts.map +1 -0
- package/dist/build/ground.js +162 -0
- package/dist/build/ground.js.map +1 -0
- package/dist/build/hash.d.ts +10 -0
- package/dist/build/hash.d.ts.map +1 -0
- package/dist/build/hash.js +13 -0
- package/dist/build/hash.js.map +1 -0
- package/dist/build/impact.d.ts +20 -0
- package/dist/build/impact.d.ts.map +1 -0
- package/dist/build/impact.js +15 -0
- package/dist/build/impact.js.map +1 -0
- package/dist/build/index.d.ts +14 -0
- package/dist/build/index.d.ts.map +1 -0
- package/dist/build/index.js +14 -0
- package/dist/build/index.js.map +1 -0
- package/dist/build/merge.d.ts +27 -0
- package/dist/build/merge.d.ts.map +1 -0
- package/dist/build/merge.js +85 -0
- package/dist/build/merge.js.map +1 -0
- package/dist/build/propose.d.ts +16 -0
- package/dist/build/propose.d.ts.map +1 -0
- package/dist/build/propose.js +126 -0
- package/dist/build/propose.js.map +1 -0
- package/dist/build/provenance.d.ts +19 -0
- package/dist/build/provenance.d.ts.map +1 -0
- package/dist/build/provenance.js +23 -0
- package/dist/build/provenance.js.map +1 -0
- package/dist/build/schema.d.ts +1825 -0
- package/dist/build/schema.d.ts.map +1 -0
- package/dist/build/schema.js +128 -0
- package/dist/build/schema.js.map +1 -0
- package/dist/build/sections.d.ts +30 -0
- package/dist/build/sections.d.ts.map +1 -0
- package/dist/build/sections.js +58 -0
- package/dist/build/sections.js.map +1 -0
- package/dist/build/tier1.d.ts +24 -0
- package/dist/build/tier1.d.ts.map +1 -0
- package/dist/build/tier1.js +68 -0
- package/dist/build/tier1.js.map +1 -0
- package/dist/build/tier2.d.ts +42 -0
- package/dist/build/tier2.d.ts.map +1 -0
- package/dist/build/tier2.js +187 -0
- package/dist/build/tier2.js.map +1 -0
- package/dist/build/types.d.ts +42 -0
- package/dist/build/types.d.ts.map +1 -0
- package/dist/build/types.js +2 -0
- package/dist/build/types.js.map +1 -0
- package/dist/components/glob.d.ts +10 -0
- package/dist/components/glob.d.ts.map +1 -0
- package/dist/components/glob.js +52 -0
- package/dist/components/glob.js.map +1 -0
- package/dist/components/registry.d.ts +51 -0
- package/dist/components/registry.d.ts.map +1 -0
- package/dist/components/registry.js +121 -0
- package/dist/components/registry.js.map +1 -0
- package/dist/dsl/factories.d.ts +86 -0
- package/dist/dsl/factories.d.ts.map +1 -0
- package/dist/dsl/factories.js +145 -0
- package/dist/dsl/factories.js.map +1 -0
- package/dist/dsl/index.d.ts +46 -0
- package/dist/dsl/index.d.ts.map +1 -0
- package/dist/dsl/index.js +67 -0
- package/dist/dsl/index.js.map +1 -0
- package/dist/dsl/reserved-names.typecheck.d.ts +2 -0
- package/dist/dsl/reserved-names.typecheck.d.ts.map +1 -0
- package/dist/dsl/reserved-names.typecheck.js +16 -0
- package/dist/dsl/reserved-names.typecheck.js.map +1 -0
- package/dist/dsl/verb-manifest.d.ts +12 -0
- package/dist/dsl/verb-manifest.d.ts.map +1 -0
- package/dist/dsl/verb-manifest.js +101 -0
- package/dist/dsl/verb-manifest.js.map +1 -0
- package/dist/fix/apply.d.ts +67 -0
- package/dist/fix/apply.d.ts.map +1 -0
- package/dist/fix/apply.js +163 -0
- package/dist/fix/apply.js.map +1 -0
- package/dist/fix/index.d.ts +3 -0
- package/dist/fix/index.d.ts.map +1 -0
- package/dist/fix/index.js +3 -0
- package/dist/fix/index.js.map +1 -0
- package/dist/fix/schema.d.ts +180 -0
- package/dist/fix/schema.d.ts.map +1 -0
- package/dist/fix/schema.js +41 -0
- package/dist/fix/schema.js.map +1 -0
- package/dist/gates/advisories.d.ts +11 -0
- package/dist/gates/advisories.d.ts.map +1 -0
- package/dist/gates/advisories.js +27 -0
- package/dist/gates/advisories.js.map +1 -0
- package/dist/gates/types.d.ts +36 -0
- package/dist/gates/types.d.ts.map +1 -0
- package/dist/gates/types.js +8 -0
- package/dist/gates/types.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator.d.ts +51 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +304 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/payload/builder.d.ts +49 -0
- package/dist/payload/builder.d.ts.map +1 -0
- package/dist/payload/builder.js +61 -0
- package/dist/payload/builder.js.map +1 -0
- package/dist/payload/mermaid.d.ts +22 -0
- package/dist/payload/mermaid.d.ts.map +1 -0
- package/dist/payload/mermaid.js +89 -0
- package/dist/payload/mermaid.js.map +1 -0
- package/dist/plugin/registry.d.ts +24 -0
- package/dist/plugin/registry.d.ts.map +1 -0
- package/dist/plugin/registry.js +24 -0
- package/dist/plugin/registry.js.map +1 -0
- package/dist/rules/component-refs.d.ts +26 -0
- package/dist/rules/component-refs.d.ts.map +1 -0
- package/dist/rules/component-refs.js +81 -0
- package/dist/rules/component-refs.js.map +1 -0
- package/dist/rules/evaluators.d.ts +50 -0
- package/dist/rules/evaluators.d.ts.map +1 -0
- package/dist/rules/evaluators.js +236 -0
- package/dist/rules/evaluators.js.map +1 -0
- package/dist/rules/host-rules.d.ts +109 -0
- package/dist/rules/host-rules.d.ts.map +1 -0
- package/dist/rules/host-rules.js +152 -0
- package/dist/rules/host-rules.js.map +1 -0
- package/dist/rules/index.d.ts +7 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +7 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/manifest-evaluators.d.ts +32 -0
- package/dist/rules/manifest-evaluators.d.ts.map +1 -0
- package/dist/rules/manifest-evaluators.js +124 -0
- package/dist/rules/manifest-evaluators.js.map +1 -0
- package/dist/rules/rule-category.d.ts +13 -0
- package/dist/rules/rule-category.d.ts.map +1 -0
- package/dist/rules/rule-category.js +27 -0
- package/dist/rules/rule-category.js.map +1 -0
- package/dist/rules/tarjan.d.ts +9 -0
- package/dist/rules/tarjan.d.ts.map +1 -0
- package/dist/rules/tarjan.js +102 -0
- package/dist/rules/tarjan.js.map +1 -0
- package/dist/scanner.d.ts +12 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +2 -0
- package/dist/scanner.js.map +1 -0
- package/dist/telemetry/diff.d.ts +27 -0
- package/dist/telemetry/diff.d.ts.map +1 -0
- package/dist/telemetry/diff.js +54 -0
- package/dist/telemetry/diff.js.map +1 -0
- package/dist/telemetry/index.d.ts +4 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +4 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/serialize.d.ts +10 -0
- package/dist/telemetry/serialize.d.ts.map +1 -0
- package/dist/telemetry/serialize.js +11 -0
- package/dist/telemetry/serialize.js.map +1 -0
- package/dist/telemetry/types.d.ts +138 -0
- package/dist/telemetry/types.d.ts.map +1 -0
- package/dist/telemetry/types.js +3 -0
- package/dist/telemetry/types.js.map +1 -0
- package/dist/types/branded.d.ts +26 -0
- package/dist/types/branded.d.ts.map +1 -0
- package/dist/types/branded.js +24 -0
- package/dist/types/branded.js.map +1 -0
- package/dist/types/graph.d.ts +46 -0
- package/dist/types/graph.d.ts.map +1 -0
- package/dist/types/graph.js +2 -0
- package/dist/types/graph.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/ir.d.ts +1747 -0
- package/dist/types/ir.d.ts.map +1 -0
- package/dist/types/ir.js +120 -0
- package/dist/types/ir.js.map +1 -0
- package/dist/types/manifest.d.ts +58 -0
- package/dist/types/manifest.d.ts.map +1 -0
- package/dist/types/manifest.js +2 -0
- package/dist/types/manifest.js.map +1 -0
- package/dist/types/violation.d.ts +94 -0
- package/dist/types/violation.d.ts.map +1 -0
- package/dist/types/violation.js +56 -0
- package/dist/types/violation.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { toRuleId } from '../types/branded.js';
|
|
2
|
+
import { computeFingerprint } from '../baseline/fingerprint.js';
|
|
3
|
+
import { extractCycleChainNodes, tarjanScc } from './tarjan.js';
|
|
4
|
+
import { evaluateCustomHost } from './host-rules.js';
|
|
5
|
+
/** No predicates registered — the default for callers that don't pass a registry (most tests, and
|
|
6
|
+
* any evaluation path that only exercises portable `arch.*` kinds). A `custom.host` rule
|
|
7
|
+
* evaluated against this empty registry throws `UnknownHostRuleError`, same as an unregistered
|
|
8
|
+
* name against a real registry — there is no silent-zero-violations path (ADR 008 amendment). */
|
|
9
|
+
const NO_HOST_PREDICATES = new Map();
|
|
10
|
+
// `exactOptionalPropertyTypes` (CODING_BEST_PRACTICES.md §9) forbids assigning `undefined` to an
|
|
11
|
+
// optional field explicitly — spread this in rather than writing `because: rule.provenance.because`.
|
|
12
|
+
// Exported: `rules/manifest-evaluators.ts`'s manifest-domain evaluators share this exact helper
|
|
13
|
+
// rather than duplicating it (CODING_BEST_PRACTICES.md's rule-of-three) — both evaluator families
|
|
14
|
+
// build the same `ViolationBase` shape from the same `RuleProvenance`.
|
|
15
|
+
export function becauseField(because) {
|
|
16
|
+
return because === undefined ? {} : { because };
|
|
17
|
+
}
|
|
18
|
+
export const evaluateNoDependency = (rule, graph) => {
|
|
19
|
+
const nodeByFile = new Map(graph.nodes.map((n) => [n.file, n]));
|
|
20
|
+
const violations = [];
|
|
21
|
+
for (const edge of graph.edges) {
|
|
22
|
+
const fromNode = nodeByFile.get(edge.from);
|
|
23
|
+
const toNode = nodeByFile.get(edge.to);
|
|
24
|
+
if (fromNode === undefined || toNode === undefined)
|
|
25
|
+
continue;
|
|
26
|
+
if (fromNode.component !== rule.from || toNode.component !== rule.to)
|
|
27
|
+
continue;
|
|
28
|
+
const id = computeFingerprint(['no-dependency', rule.id, edge.from, edge.to, edge.specifier]);
|
|
29
|
+
violations.push({
|
|
30
|
+
id,
|
|
31
|
+
ruleId: toRuleId(rule.id),
|
|
32
|
+
category: 'architecture',
|
|
33
|
+
severity: 'error',
|
|
34
|
+
file: edge.from,
|
|
35
|
+
range: { startLine: edge.line, endLine: edge.line },
|
|
36
|
+
snippet: edge.snippet,
|
|
37
|
+
fixHint: { code: 'remove-import', file: edge.from, line: edge.line },
|
|
38
|
+
...becauseField(rule.provenance.because),
|
|
39
|
+
kind: 'no-dependency',
|
|
40
|
+
fromFile: edge.from,
|
|
41
|
+
toFile: edge.to,
|
|
42
|
+
fromComponent: fromNode.component,
|
|
43
|
+
toComponent: toNode.component,
|
|
44
|
+
specifier: edge.specifier,
|
|
45
|
+
line: edge.line,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return violations;
|
|
49
|
+
};
|
|
50
|
+
const RUNTIME_KINDS = ['import', 'reexport', 'dynamic'];
|
|
51
|
+
const ALL_KINDS = ['import', 'reexport', 'dynamic', 'type-only'];
|
|
52
|
+
export const evaluateNoCycles = (rule, graph) => {
|
|
53
|
+
const nodeByFile = new Map(graph.nodes.map((n) => [n.file, n]));
|
|
54
|
+
const inScope = (file) => rule.scope === 'repo' ? true : nodeByFile.get(file)?.component === rule.scope;
|
|
55
|
+
const allowedKinds = new Set(rule.includeTypeOnly ? ALL_KINDS : RUNTIME_KINDS);
|
|
56
|
+
const adjacency = new Map();
|
|
57
|
+
for (const node of graph.nodes)
|
|
58
|
+
if (inScope(node.file))
|
|
59
|
+
adjacency.set(node.file, []);
|
|
60
|
+
const edgeByPair = new Map();
|
|
61
|
+
for (const edge of graph.edges) {
|
|
62
|
+
if (!allowedKinds.has(edge.kind))
|
|
63
|
+
continue;
|
|
64
|
+
if (!adjacency.has(edge.from) || !adjacency.has(edge.to))
|
|
65
|
+
continue;
|
|
66
|
+
adjacency.get(edge.from)?.push(edge.to);
|
|
67
|
+
// First edge wins for a given (from, to) pair when the chain is rendered — enough to name a
|
|
68
|
+
// concrete importable specifier/line without enumerating every parallel edge.
|
|
69
|
+
const key = `${edge.from} ${edge.to}`;
|
|
70
|
+
if (!edgeByPair.has(key))
|
|
71
|
+
edgeByPair.set(key, edge);
|
|
72
|
+
}
|
|
73
|
+
const sccs = tarjanScc(adjacency);
|
|
74
|
+
const violations = [];
|
|
75
|
+
for (const scc of sccs) {
|
|
76
|
+
const isSelfLoop = scc.length === 1 && (scc[0] === undefined ? false : (adjacency.get(scc[0]) ?? []).includes(scc[0]));
|
|
77
|
+
if (scc.length <= 1 && !isSelfLoop)
|
|
78
|
+
continue;
|
|
79
|
+
const chainNodes = extractCycleChainNodes(scc, adjacency);
|
|
80
|
+
const chain = [];
|
|
81
|
+
for (let i = 0; i < chainNodes.length - 1; i += 1) {
|
|
82
|
+
const from = chainNodes[i];
|
|
83
|
+
const to = chainNodes[i + 1];
|
|
84
|
+
if (from === undefined || to === undefined)
|
|
85
|
+
continue;
|
|
86
|
+
const edge = edgeByPair.get(`${from} ${to}`);
|
|
87
|
+
if (edge === undefined)
|
|
88
|
+
continue;
|
|
89
|
+
chain.push({ from, to, specifier: edge.specifier, line: edge.line });
|
|
90
|
+
}
|
|
91
|
+
if (chain.length === 0)
|
|
92
|
+
continue;
|
|
93
|
+
const breakHop = chain[chain.length - 1];
|
|
94
|
+
if (breakHop === undefined)
|
|
95
|
+
continue;
|
|
96
|
+
const breakEdgeRaw = edgeByPair.get(`${breakHop.from} ${breakHop.to}`);
|
|
97
|
+
if (breakEdgeRaw === undefined)
|
|
98
|
+
continue;
|
|
99
|
+
const id = computeFingerprint(['no-cycles', rule.id, ...chain.map((e) => `${e.from}>${e.to}:${e.specifier}`)]);
|
|
100
|
+
const firstFile = chainNodes[0];
|
|
101
|
+
if (firstFile === undefined)
|
|
102
|
+
continue;
|
|
103
|
+
violations.push({
|
|
104
|
+
id,
|
|
105
|
+
ruleId: toRuleId(rule.id),
|
|
106
|
+
category: 'architecture',
|
|
107
|
+
severity: 'error',
|
|
108
|
+
file: firstFile,
|
|
109
|
+
range: { startLine: breakHop.line, endLine: breakHop.line },
|
|
110
|
+
snippet: breakEdgeRaw.snippet,
|
|
111
|
+
fixHint: { code: 'break-cycle-edge', suggestedEdge: breakHop },
|
|
112
|
+
...becauseField(rule.provenance.because),
|
|
113
|
+
kind: 'no-cycles',
|
|
114
|
+
chain,
|
|
115
|
+
suggestedBreakEdge: breakHop,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return violations;
|
|
119
|
+
};
|
|
120
|
+
export const evaluateLayers = (rule, graph) => {
|
|
121
|
+
const nodeByFile = new Map(graph.nodes.map((n) => [n.file, n]));
|
|
122
|
+
const violations = [];
|
|
123
|
+
for (const layerDef of rule.layers) {
|
|
124
|
+
const allowed = new Set(layerDef.canDependOn);
|
|
125
|
+
for (const edge of graph.edges) {
|
|
126
|
+
const fromNode = nodeByFile.get(edge.from);
|
|
127
|
+
const toNode = nodeByFile.get(edge.to);
|
|
128
|
+
if (fromNode === undefined || toNode === undefined)
|
|
129
|
+
continue;
|
|
130
|
+
if (fromNode.component !== layerDef.layer)
|
|
131
|
+
continue;
|
|
132
|
+
if (toNode.component === fromNode.component)
|
|
133
|
+
continue; // intra-layer is always fine
|
|
134
|
+
if (allowed.has(toNode.component))
|
|
135
|
+
continue;
|
|
136
|
+
const id = computeFingerprint(['layers', rule.id, edge.from, edge.to, edge.specifier]);
|
|
137
|
+
violations.push({
|
|
138
|
+
id,
|
|
139
|
+
ruleId: toRuleId(rule.id),
|
|
140
|
+
category: 'architecture',
|
|
141
|
+
severity: 'error',
|
|
142
|
+
file: edge.from,
|
|
143
|
+
range: { startLine: edge.line, endLine: edge.line },
|
|
144
|
+
snippet: edge.snippet,
|
|
145
|
+
fixHint: { code: 'remove-import', file: edge.from, line: edge.line },
|
|
146
|
+
...becauseField(rule.provenance.because),
|
|
147
|
+
kind: 'layers',
|
|
148
|
+
fromLayer: fromNode.component,
|
|
149
|
+
toLayer: toNode.component,
|
|
150
|
+
fromFile: edge.from,
|
|
151
|
+
toFile: edge.to,
|
|
152
|
+
specifier: edge.specifier,
|
|
153
|
+
line: edge.line,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return violations;
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* `arch.metric` (max-LOC only, promoted 2026-07-12 on kluster ruleset evidence —
|
|
161
|
+
* IMPLEMENTATION_PLAN.md's Promotion log: two 2,100+-line files were structurally invisible to
|
|
162
|
+
* every dependency/cycle rule). One violation per file classified to `rule.target` whose `loc`
|
|
163
|
+
* exceeds `rule.max` — `loc` is already on every `DependencyGraphNode` (no new scanning).
|
|
164
|
+
*/
|
|
165
|
+
export const evaluateMetric = (rule, graph) => {
|
|
166
|
+
const violations = [];
|
|
167
|
+
for (const node of graph.nodes) {
|
|
168
|
+
if (node.component !== rule.target)
|
|
169
|
+
continue;
|
|
170
|
+
if (node.loc <= rule.max)
|
|
171
|
+
continue;
|
|
172
|
+
const id = computeFingerprint(['metric', rule.id, node.file]);
|
|
173
|
+
violations.push({
|
|
174
|
+
id,
|
|
175
|
+
ruleId: toRuleId(rule.id),
|
|
176
|
+
category: 'architecture',
|
|
177
|
+
severity: 'error',
|
|
178
|
+
file: node.file,
|
|
179
|
+
range: { startLine: 1, endLine: 1 },
|
|
180
|
+
snippet: node.snippet,
|
|
181
|
+
fixHint: { code: 'split-file', file: node.file },
|
|
182
|
+
...becauseField(rule.provenance.because),
|
|
183
|
+
kind: 'metric',
|
|
184
|
+
metric: rule.metric,
|
|
185
|
+
component: node.component,
|
|
186
|
+
value: node.loc,
|
|
187
|
+
threshold: rule.max,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return violations;
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* Exhaustive dispatcher: a new `RuleIR` discriminant without a case here is a compile error
|
|
194
|
+
* (never-check, CODING_BEST_PRACTICES.md §17.2), not a silent no-op.
|
|
195
|
+
*
|
|
196
|
+
* `hostPredicates` defaults to the empty registry — every `arch.*` evaluator ignores it entirely
|
|
197
|
+
* (they never took it before registration existed); only `custom.host` reads it. A predicate that
|
|
198
|
+
* throws propagates out of this function uncaught (`HostPredicateExecutionError`) exactly like a
|
|
199
|
+
* malformed rule would — the orchestrator's evaluation-loop guard is what turns that into gate
|
|
200
|
+
* `error` (`orchestrator.ts`), not this function, which stays a pure dispatcher.
|
|
201
|
+
*
|
|
202
|
+
* `security.manifest.*` kinds (ADR 013) return `[]` here, deliberately: this dispatcher only ever
|
|
203
|
+
* receives a `DependencyGraph` (TS-source scan output), and manifest rules evaluate against a
|
|
204
|
+
* disjoint scan domain (`ManifestInventory`) that this function never has access to. They are real
|
|
205
|
+
* `RuleIR` members (needed so the DSL/tier-2/build pipeline can author and round-trip them), but
|
|
206
|
+
* their actual evaluation always goes through `evaluateManifestRule`
|
|
207
|
+
* (`rules/manifest-evaluators.ts`) against real manifest data — `GateOrchestrator`'s `security` gate
|
|
208
|
+
* calls it directly and never routes these kinds through this function (`ruleCategoryOf` partitions
|
|
209
|
+
* `RulesetIR.rules` before either dispatcher runs, `rules/rule-category.ts`). Returning `[]` here
|
|
210
|
+
* (rather than throwing) keeps `align build`/`align explain`'s generic graph-based preview paths
|
|
211
|
+
* working without a manifest scan available to them — see ADR 013's follow-up ladder for the known
|
|
212
|
+
* gap this leaves (their impact-delta preview under-reports manifest-rule violations; `align check`
|
|
213
|
+
* remains authoritative).
|
|
214
|
+
*/
|
|
215
|
+
export function evaluateRule(rule, graph, components, hostPredicates = NO_HOST_PREDICATES) {
|
|
216
|
+
switch (rule.kind) {
|
|
217
|
+
case 'arch.no-dependency':
|
|
218
|
+
return evaluateNoDependency(rule, graph, components);
|
|
219
|
+
case 'arch.no-cycles':
|
|
220
|
+
return evaluateNoCycles(rule, graph, components);
|
|
221
|
+
case 'arch.layers':
|
|
222
|
+
return evaluateLayers(rule, graph, components);
|
|
223
|
+
case 'arch.metric':
|
|
224
|
+
return evaluateMetric(rule, graph, components);
|
|
225
|
+
case 'custom.host':
|
|
226
|
+
return evaluateCustomHost(rule, graph, hostPredicates);
|
|
227
|
+
case 'security.manifest.source-hygiene':
|
|
228
|
+
case 'security.manifest.new-dependency':
|
|
229
|
+
return [];
|
|
230
|
+
default: {
|
|
231
|
+
const exhaustive = rule;
|
|
232
|
+
throw new Error(`unhandled rule kind: ${JSON.stringify(exhaustive)}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=evaluators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluators.js","sourceRoot":"","sources":["../../src/rules/evaluators.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { ComponentName, RepoRelativePath } from '../types/branded.js';
|
|
2
|
+
import type { CustomHostRule, RuleIR } from '../types/ir.js';
|
|
3
|
+
import type { DependencyGraph } from '../types/graph.js';
|
|
4
|
+
import type { SourceRange, Violation } from '../types/violation.js';
|
|
5
|
+
/** Thrown when a `custom.host` rule names a host predicate that is not registered — the third
|
|
6
|
+
* member of the vacuous-green family (`rules/component-refs.ts`, `components/registry.ts`'s
|
|
7
|
+
* `validateClassifiedComponents`): pre-registration, `evaluateRule` returned zero violations for
|
|
8
|
+
* every `custom.host` rule unconditionally, so an unevaluatable rule would otherwise sit in the
|
|
9
|
+
* ruleset reporting green — and even count toward `passCount` — while enforcing nothing. Now that
|
|
10
|
+
* predicates are registrable (`align.config.ts`'s `hostRules` export, `docs/adr/002`), this only
|
|
11
|
+
* fires for a genuinely unregistered name — a typo, a removed predicate a rule still references,
|
|
12
|
+
* or (rare) a rule reaching `evaluateCustomHost` without having gone through the orchestrator's
|
|
13
|
+
* guard step first (defense in depth, see `evaluateCustomHost` below). */
|
|
14
|
+
export declare class UnknownHostRuleError extends Error {
|
|
15
|
+
readonly ruleId: string;
|
|
16
|
+
readonly hostRuleName: string;
|
|
17
|
+
constructor(ruleId: string, hostRuleName: string);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Load-time validation, run in `GateOrchestrator.check`'s vacuous-green guard step: every
|
|
21
|
+
* `custom.host` rule's `hostRuleName` must name a registered host predicate. The CLI composition
|
|
22
|
+
* root derives `registeredHostPredicates` from the loaded config's `hostRules` export (core stays
|
|
23
|
+
* framework-free — it only ever sees the name set, never the config-loading mechanism) and passes
|
|
24
|
+
* the real set to `GateOrchestrator`'s constructor; a repo with no `hostRules` export passes an
|
|
25
|
+
* empty set here, same as before registration existed. Fail-fast on the first offender, same
|
|
26
|
+
* convention as the sibling validators.
|
|
27
|
+
*/
|
|
28
|
+
export declare function validateHostRules(rules: readonly RuleIR[], registeredHostPredicates: ReadonlySet<string>): void;
|
|
29
|
+
/**
|
|
30
|
+
* Narrow, typed input a host predicate receives — pure data, no I/O (CODING_BEST_PRACTICES.md
|
|
31
|
+
* §14/15's "functional core" discipline applies to predicates too): the freshly re-scanned graph
|
|
32
|
+
* (ADR 005 — always fresh, never cached), a convenience component lookup, and the flat file list.
|
|
33
|
+
* Predicates read this and return `HostViolation[]`; they never touch the filesystem, network, or
|
|
34
|
+
* clock themselves, and `evaluateCustomHost` (below) is itself I/O-free, matching every other
|
|
35
|
+
* `RuleEvaluator` in this package.
|
|
36
|
+
*/
|
|
37
|
+
export interface HostRuleContext {
|
|
38
|
+
readonly graph: DependencyGraph;
|
|
39
|
+
readonly componentOf: (file: RepoRelativePath) => ComponentName | undefined;
|
|
40
|
+
readonly files: readonly RepoRelativePath[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* What a predicate returns — deliberately minimal so authoring one stays a few lines
|
|
44
|
+
* (docs/proposals/rule-expansion-evaluation.md §B.0's "cost to ship: low"). `range`/`snippet`
|
|
45
|
+
* default to the violating file's line 1 / scanned first-line snippet when omitted — core (never
|
|
46
|
+
* the predicate) fingerprints the violation, defaults `fixHint` to `'manual-review'`, and hoists
|
|
47
|
+
* the rule's `.because()` text, mirroring exactly what every other `RuleEvaluator` does for its
|
|
48
|
+
* own violations.
|
|
49
|
+
*/
|
|
50
|
+
export interface HostViolation {
|
|
51
|
+
readonly file: RepoRelativePath;
|
|
52
|
+
readonly range?: SourceRange;
|
|
53
|
+
readonly snippet?: string;
|
|
54
|
+
readonly message: string;
|
|
55
|
+
}
|
|
56
|
+
/** A registered host predicate: pure `(ctx) -> violations`, no I/O — same doctrine as
|
|
57
|
+
* `RuleEvaluator` (`rules/evaluators.ts`), scoped to one rule kind's worth of host-defined logic.
|
|
58
|
+
* Authored in `align.config.ts`'s `hostRules` export, keyed by the name a `custom.host` rule's
|
|
59
|
+
* `hostRuleName` references (`c.custom.host('name')`, `dsl/index.ts`). */
|
|
60
|
+
export type HostPredicate = (ctx: HostRuleContext) => readonly HostViolation[];
|
|
61
|
+
/** The registered-predicate map, keyed by `hostRuleName`. `ReadonlyMap` (not a plain object) —
|
|
62
|
+
* the CLI composition root builds this once from `align.config.ts`'s `hostRules` export and
|
|
63
|
+
* injects it into `GateOrchestrator`; core never constructs one itself. */
|
|
64
|
+
export type HostPredicateRegistry = ReadonlyMap<string, HostPredicate>;
|
|
65
|
+
/** Thrown when a registered predicate itself throws while evaluating — the reference-validity
|
|
66
|
+
* invariant's sibling (ADR 008 amendment): a buggy predicate must surface as gate `error`, never a
|
|
67
|
+
* silent pass (an uncaught exception mid-evaluation would otherwise abort the whole check with an
|
|
68
|
+
* unattributed stack trace) and never a silently-dropped violation set. Caught here, re-thrown
|
|
69
|
+
* with rule/predicate attribution so the orchestrator's evaluation-loop guard (`orchestrator.ts`)
|
|
70
|
+
* can turn it into a `GateResult` the same way it already does for scan/reference-validity
|
|
71
|
+
* failures. */
|
|
72
|
+
export declare class HostPredicateExecutionError extends Error {
|
|
73
|
+
readonly ruleId: string;
|
|
74
|
+
readonly hostRuleName: string;
|
|
75
|
+
readonly predicateError: unknown;
|
|
76
|
+
constructor(ruleId: string, hostRuleName: string, predicateError: unknown);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Untrusted-mode's custom.host refusal (ADR 014). `--untrusted`/`--ir-only` never registers any
|
|
80
|
+
* host predicates (there is nothing to register them FROM — align.config.ts is never imported in
|
|
81
|
+
* that mode), so every `custom.host` rule is structurally unevaluatable there, permanently, not
|
|
82
|
+
* just until someone fixes a registration typo. Distinct from `UnknownHostRuleError` (a fixable
|
|
83
|
+
* config bug: register the predicate, fix the name, or remove the rule) — this error tells the
|
|
84
|
+
* truth about *why* it can't be fixed by editing `align.config.ts`: that file is never read under
|
|
85
|
+
* `--untrusted` at all. Refusing outright (never silently skipping the rule) follows the same
|
|
86
|
+
* false-green doctrine as `UnknownHostRuleError` and the reference-validity invariant (ADR 008
|
|
87
|
+
* amendment) — a silently-dropped rule would report green while enforcing nothing.
|
|
88
|
+
*/
|
|
89
|
+
export declare class UntrustedCustomHostRuleError extends Error {
|
|
90
|
+
readonly ruleIds: readonly string[];
|
|
91
|
+
constructor(ruleIds: readonly string[]);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* `--untrusted`'s pre-flight guard (ADR 014), called by the CLI before constructing the
|
|
95
|
+
* orchestrator — mirrors `validateHostRules`'s fail-fast-on-first-offender convention but collects
|
|
96
|
+
* every offending rule id in one error instead of stopping at the first, since there is no
|
|
97
|
+
* "register the missing one and re-run" loop to support here (registration is categorically
|
|
98
|
+
* unavailable, not just incomplete).
|
|
99
|
+
*/
|
|
100
|
+
export declare function assertNoCustomHostRules(rules: readonly RuleIR[]): void;
|
|
101
|
+
/**
|
|
102
|
+
* `custom.host`'s `RuleEvaluator` (dispatched from `rules/evaluators.ts`'s exhaustive switch,
|
|
103
|
+
* ADR 002). Looks up the rule's predicate in the injected registry, builds its `HostRuleContext`
|
|
104
|
+
* from the already-scanned graph (zero extra I/O), runs it, and normalizes every `HostViolation`
|
|
105
|
+
* into a full `Violation` — the same fingerprint/baseline/fix-hint machinery every other rule kind
|
|
106
|
+
* gets, so a custom.host violation is baseline-able and dedupes exactly like any other.
|
|
107
|
+
*/
|
|
108
|
+
export declare function evaluateCustomHost(rule: CustomHostRule, graph: DependencyGraph, predicates: HostPredicateRegistry): readonly Violation[];
|
|
109
|
+
//# sourceMappingURL=host-rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-rules.d.ts","sourceRoot":"","sources":["../../src/rules/host-rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGpE;;;;;;;;0EAQ0E;AAC1E,qBAAa,oBAAqB,SAAQ,KAAK;aAE3B,MAAM,EAAE,MAAM;aACd,YAAY,EAAE,MAAM;gBADpB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM;CAUvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAO/G;AAMD;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,aAAa,GAAG,SAAS,CAAC;IAC5E,QAAQ,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAC;CAC7C;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;0EAG0E;AAC1E,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,eAAe,KAAK,SAAS,aAAa,EAAE,CAAC;AAE/E;;2EAE2E;AAC3E,MAAM,MAAM,qBAAqB,GAAG,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAEvE;;;;;;eAMe;AACf,qBAAa,2BAA4B,SAAQ,KAAK;aAElC,MAAM,EAAE,MAAM;aACd,YAAY,EAAE,MAAM;aACpB,cAAc,EAAE,OAAO;gBAFvB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,OAAO;CAU1C;AAED;;;;;;;;;;GAUG;AACH,qBAAa,4BAA6B,SAAQ,KAAK;aACzB,OAAO,EAAE,SAAS,MAAM,EAAE;gBAA1B,OAAO,EAAE,SAAS,MAAM,EAAE;CAWvD;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAGtE;AA8BD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,eAAe,EACtB,UAAU,EAAE,qBAAqB,GAChC,SAAS,SAAS,EAAE,CAsBtB"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { toRuleId } from '../types/branded.js';
|
|
2
|
+
import { computeFingerprint } from '../baseline/fingerprint.js';
|
|
3
|
+
/** Thrown when a `custom.host` rule names a host predicate that is not registered — the third
|
|
4
|
+
* member of the vacuous-green family (`rules/component-refs.ts`, `components/registry.ts`'s
|
|
5
|
+
* `validateClassifiedComponents`): pre-registration, `evaluateRule` returned zero violations for
|
|
6
|
+
* every `custom.host` rule unconditionally, so an unevaluatable rule would otherwise sit in the
|
|
7
|
+
* ruleset reporting green — and even count toward `passCount` — while enforcing nothing. Now that
|
|
8
|
+
* predicates are registrable (`align.config.ts`'s `hostRules` export, `docs/adr/002`), this only
|
|
9
|
+
* fires for a genuinely unregistered name — a typo, a removed predicate a rule still references,
|
|
10
|
+
* or (rare) a rule reaching `evaluateCustomHost` without having gone through the orchestrator's
|
|
11
|
+
* guard step first (defense in depth, see `evaluateCustomHost` below). */
|
|
12
|
+
export class UnknownHostRuleError extends Error {
|
|
13
|
+
ruleId;
|
|
14
|
+
hostRuleName;
|
|
15
|
+
constructor(ruleId, hostRuleName) {
|
|
16
|
+
super(`Rule '${ruleId}' (custom.host) references host predicate '${hostRuleName}', which is not ` +
|
|
17
|
+
`registered in align.config.ts's 'hostRules' export. This rule cannot be evaluated and ` +
|
|
18
|
+
`would silently report green. Register a predicate named '${hostRuleName}' in 'hostRules', ` +
|
|
19
|
+
`fix the typo, or remove the rule (and re-run \`align build\` if it came from a doc).`);
|
|
20
|
+
this.ruleId = ruleId;
|
|
21
|
+
this.hostRuleName = hostRuleName;
|
|
22
|
+
this.name = 'UnknownHostRuleError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Load-time validation, run in `GateOrchestrator.check`'s vacuous-green guard step: every
|
|
27
|
+
* `custom.host` rule's `hostRuleName` must name a registered host predicate. The CLI composition
|
|
28
|
+
* root derives `registeredHostPredicates` from the loaded config's `hostRules` export (core stays
|
|
29
|
+
* framework-free — it only ever sees the name set, never the config-loading mechanism) and passes
|
|
30
|
+
* the real set to `GateOrchestrator`'s constructor; a repo with no `hostRules` export passes an
|
|
31
|
+
* empty set here, same as before registration existed. Fail-fast on the first offender, same
|
|
32
|
+
* convention as the sibling validators.
|
|
33
|
+
*/
|
|
34
|
+
export function validateHostRules(rules, registeredHostPredicates) {
|
|
35
|
+
for (const rule of rules) {
|
|
36
|
+
if (rule.kind !== 'custom.host')
|
|
37
|
+
continue;
|
|
38
|
+
if (!registeredHostPredicates.has(rule.hostRuleName)) {
|
|
39
|
+
throw new UnknownHostRuleError(rule.id, rule.hostRuleName);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Thrown when a registered predicate itself throws while evaluating — the reference-validity
|
|
44
|
+
* invariant's sibling (ADR 008 amendment): a buggy predicate must surface as gate `error`, never a
|
|
45
|
+
* silent pass (an uncaught exception mid-evaluation would otherwise abort the whole check with an
|
|
46
|
+
* unattributed stack trace) and never a silently-dropped violation set. Caught here, re-thrown
|
|
47
|
+
* with rule/predicate attribution so the orchestrator's evaluation-loop guard (`orchestrator.ts`)
|
|
48
|
+
* can turn it into a `GateResult` the same way it already does for scan/reference-validity
|
|
49
|
+
* failures. */
|
|
50
|
+
export class HostPredicateExecutionError extends Error {
|
|
51
|
+
ruleId;
|
|
52
|
+
hostRuleName;
|
|
53
|
+
predicateError;
|
|
54
|
+
constructor(ruleId, hostRuleName, predicateError) {
|
|
55
|
+
super(`Rule '${ruleId}' (custom.host) predicate '${hostRuleName}' threw while evaluating: ` +
|
|
56
|
+
`${predicateError instanceof Error ? predicateError.message : String(predicateError)} — ` +
|
|
57
|
+
`a host predicate must be a pure function over its HostRuleContext (no I/O); fix it in ` +
|
|
58
|
+
`align.config.ts's 'hostRules' export.`);
|
|
59
|
+
this.ruleId = ruleId;
|
|
60
|
+
this.hostRuleName = hostRuleName;
|
|
61
|
+
this.predicateError = predicateError;
|
|
62
|
+
this.name = 'HostPredicateExecutionError';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Untrusted-mode's custom.host refusal (ADR 014). `--untrusted`/`--ir-only` never registers any
|
|
67
|
+
* host predicates (there is nothing to register them FROM — align.config.ts is never imported in
|
|
68
|
+
* that mode), so every `custom.host` rule is structurally unevaluatable there, permanently, not
|
|
69
|
+
* just until someone fixes a registration typo. Distinct from `UnknownHostRuleError` (a fixable
|
|
70
|
+
* config bug: register the predicate, fix the name, or remove the rule) — this error tells the
|
|
71
|
+
* truth about *why* it can't be fixed by editing `align.config.ts`: that file is never read under
|
|
72
|
+
* `--untrusted` at all. Refusing outright (never silently skipping the rule) follows the same
|
|
73
|
+
* false-green doctrine as `UnknownHostRuleError` and the reference-validity invariant (ADR 008
|
|
74
|
+
* amendment) — a silently-dropped rule would report green while enforcing nothing.
|
|
75
|
+
*/
|
|
76
|
+
export class UntrustedCustomHostRuleError extends Error {
|
|
77
|
+
ruleIds;
|
|
78
|
+
constructor(ruleIds) {
|
|
79
|
+
super(`--untrusted refuses to evaluate ${ruleIds.length} custom.host rule(s): ${ruleIds.join(', ')}. ` +
|
|
80
|
+
`A custom.host predicate is host-side code by definition, and --untrusted's entire guarantee ` +
|
|
81
|
+
`is that no repo-controlled code executes — there is no predicate registry to consult because ` +
|
|
82
|
+
`align.config.ts is never imported in this mode. Options: run \`align check\` without ` +
|
|
83
|
+
`--untrusted on a repo you trust to execute code, or remove/replace these rules with a ` +
|
|
84
|
+
`portable arch.*/security.manifest.* kind before running \`align export-ir\` again.`);
|
|
85
|
+
this.ruleIds = ruleIds;
|
|
86
|
+
this.name = 'UntrustedCustomHostRuleError';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* `--untrusted`'s pre-flight guard (ADR 014), called by the CLI before constructing the
|
|
91
|
+
* orchestrator — mirrors `validateHostRules`'s fail-fast-on-first-offender convention but collects
|
|
92
|
+
* every offending rule id in one error instead of stopping at the first, since there is no
|
|
93
|
+
* "register the missing one and re-run" loop to support here (registration is categorically
|
|
94
|
+
* unavailable, not just incomplete).
|
|
95
|
+
*/
|
|
96
|
+
export function assertNoCustomHostRules(rules) {
|
|
97
|
+
const ids = rules.filter((r) => r.kind === 'custom.host').map((r) => r.id);
|
|
98
|
+
if (ids.length > 0)
|
|
99
|
+
throw new UntrustedCustomHostRuleError(ids);
|
|
100
|
+
}
|
|
101
|
+
function normalizeHostViolation(rule, hv, nodeByFile) {
|
|
102
|
+
const range = hv.range ?? { startLine: 1, endLine: 1 };
|
|
103
|
+
// No I/O here either (evaluateCustomHost stays as pure as every other RuleEvaluator) — the
|
|
104
|
+
// fallback reuses the node's already-scanned first-line snippet (DependencyGraphNode.snippet,
|
|
105
|
+
// captured once at scan time, same field `arch.metric` reuses for its own file-level
|
|
106
|
+
// violations) rather than re-reading the file.
|
|
107
|
+
const snippet = hv.snippet ?? nodeByFile.get(hv.file)?.snippet ?? hv.message;
|
|
108
|
+
const id = computeFingerprint(['custom', rule.id, hv.file, String(range.startLine), hv.message]);
|
|
109
|
+
return {
|
|
110
|
+
id,
|
|
111
|
+
ruleId: toRuleId(rule.id),
|
|
112
|
+
category: 'architecture',
|
|
113
|
+
severity: 'error',
|
|
114
|
+
file: hv.file,
|
|
115
|
+
range,
|
|
116
|
+
snippet,
|
|
117
|
+
fixHint: { code: 'manual-review' },
|
|
118
|
+
...(rule.provenance.because === undefined ? {} : { because: rule.provenance.because }),
|
|
119
|
+
kind: 'custom',
|
|
120
|
+
hostRuleName: rule.hostRuleName,
|
|
121
|
+
detail: hv.message,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* `custom.host`'s `RuleEvaluator` (dispatched from `rules/evaluators.ts`'s exhaustive switch,
|
|
126
|
+
* ADR 002). Looks up the rule's predicate in the injected registry, builds its `HostRuleContext`
|
|
127
|
+
* from the already-scanned graph (zero extra I/O), runs it, and normalizes every `HostViolation`
|
|
128
|
+
* into a full `Violation` — the same fingerprint/baseline/fix-hint machinery every other rule kind
|
|
129
|
+
* gets, so a custom.host violation is baseline-able and dedupes exactly like any other.
|
|
130
|
+
*/
|
|
131
|
+
export function evaluateCustomHost(rule, graph, predicates) {
|
|
132
|
+
const predicate = predicates.get(rule.hostRuleName);
|
|
133
|
+
if (predicate === undefined) {
|
|
134
|
+
// Defense in depth: `validateHostRules` (the orchestrator's pre-evaluation guard step) should
|
|
135
|
+
// already have caught this — reachable only if a caller evaluates a rule without running the
|
|
136
|
+
// guard first (e.g. `align explain`/`align build`'s dry-run passes call `evaluateRule`
|
|
137
|
+
// directly). Same error type either way, so the message is identical.
|
|
138
|
+
throw new UnknownHostRuleError(rule.id, rule.hostRuleName);
|
|
139
|
+
}
|
|
140
|
+
const nodeByFile = new Map(graph.nodes.map((n) => [n.file, n]));
|
|
141
|
+
const componentOf = (file) => nodeByFile.get(file)?.component;
|
|
142
|
+
const ctx = { graph, componentOf, files: graph.nodes.map((n) => n.file) };
|
|
143
|
+
let results;
|
|
144
|
+
try {
|
|
145
|
+
results = predicate(ctx);
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
throw new HostPredicateExecutionError(rule.id, rule.hostRuleName, err);
|
|
149
|
+
}
|
|
150
|
+
return results.map((hv) => normalizeHostViolation(rule, hv, nodeByFile));
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=host-rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-rules.js","sourceRoot":"","sources":["../../src/rules/host-rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { SecurityManifestNewDependencyRule, SecurityManifestSourceHygieneRule } from '../types/ir.js';
|
|
2
|
+
import type { ManifestInventory } from '../types/manifest.js';
|
|
3
|
+
import type { Violation } from '../types/violation.js';
|
|
4
|
+
export type SecurityManifestRule = SecurityManifestSourceHygieneRule | SecurityManifestNewDependencyRule;
|
|
5
|
+
/**
|
|
6
|
+
* `security.manifest.source-hygiene` (ADR 013, probe Rule 1): any dependency specifier resolving
|
|
7
|
+
* to git/http(s)/file/link, not registry/workspace protocol. Fingerprint keyed on
|
|
8
|
+
* (manifest file, dep name) only — never the specifier value or a line number — so a git-ref bump
|
|
9
|
+
* or manifest reformatting doesn't reset baseline consent (ADR 013's name-level doctrine, mirrored
|
|
10
|
+
* from `security.manifest.new-dependency`); the current specifier is still reported as a
|
|
11
|
+
* structured field so a reviewer sees exactly what changed.
|
|
12
|
+
*/
|
|
13
|
+
export declare const evaluateSourceHygiene: (rule: SecurityManifestSourceHygieneRule, inventory: ManifestInventory) => Violation[];
|
|
14
|
+
/**
|
|
15
|
+
* `security.manifest.new-dependency` (ADR 013, probe Rule 7): a stateless, fingerprint-driven
|
|
16
|
+
* re-expression of the probe's git-diff mechanism through align's existing baseline-consent
|
|
17
|
+
* machinery (ADR 006), not a git-history diff itself — this evaluator flags EVERY current
|
|
18
|
+
* runtime/dev dependency as a fingerprinted candidate on every run; baseline consent (`align
|
|
19
|
+
* init`/`baseline accept`) is what turns "all of today's deps" into "nothing," so only a
|
|
20
|
+
* genuinely new dependency (new name, not previously baselined) ever shows red. Name-level only
|
|
21
|
+
* (`optionalDependencies`/`peerDependencies` excluded) — version-level gating is a documented
|
|
22
|
+
* follow-up (ADR 013), not built here: it would fire on every routine version bump / renovate PR.
|
|
23
|
+
*/
|
|
24
|
+
export declare const evaluateNewDependency: (rule: SecurityManifestNewDependencyRule, inventory: ManifestInventory) => Violation[];
|
|
25
|
+
/**
|
|
26
|
+
* Exhaustive dispatcher for the manifest scan domain — the sibling of `evaluators.ts`'s
|
|
27
|
+
* `evaluateRule`, over the disjoint `SecurityManifestRule` union instead of the full `RuleIR`
|
|
28
|
+
* union. `GateOrchestrator`'s `security` gate is the only real caller (`ruleCategoryOf` partitions
|
|
29
|
+
* `RulesetIR.rules` before this ever runs, `rules/rule-category.ts`).
|
|
30
|
+
*/
|
|
31
|
+
export declare function evaluateManifestRule(rule: SecurityManifestRule, inventory: ManifestInventory): readonly Violation[];
|
|
32
|
+
//# sourceMappingURL=manifest-evaluators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-evaluators.d.ts","sourceRoot":"","sources":["../../src/rules/manifest-evaluators.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iCAAiC,EAAE,iCAAiC,EAAE,MAAM,gBAAgB,CAAC;AAC3G,OAAO,KAAK,EAAsB,iBAAiB,EAAkB,MAAM,sBAAsB,CAAC;AAClG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAIvD,MAAM,MAAM,oBAAoB,GAAG,iCAAiC,GAAG,iCAAiC,CAAC;AAwBzG;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAAI,MAAM,iCAAiC,EAAE,WAAW,iBAAiB,KAAG,SAAS,EA2BtH,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,GAAI,MAAM,iCAAiC,EAAE,WAAW,iBAAiB,KAAG,SAAS,EA0BtH,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,oBAAoB,EAAE,SAAS,EAAE,iBAAiB,GAAG,SAAS,SAAS,EAAE,CAWnH"}
|