@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.
Files changed (203) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +27 -0
  3. package/dist/baseline/fingerprint.d.ts +19 -0
  4. package/dist/baseline/fingerprint.d.ts.map +1 -0
  5. package/dist/baseline/fingerprint.js +0 -0
  6. package/dist/baseline/fingerprint.js.map +1 -0
  7. package/dist/baseline/store.d.ts +81 -0
  8. package/dist/baseline/store.d.ts.map +1 -0
  9. package/dist/baseline/store.js +111 -0
  10. package/dist/baseline/store.js.map +1 -0
  11. package/dist/build/diff.d.ts +22 -0
  12. package/dist/build/diff.d.ts.map +1 -0
  13. package/dist/build/diff.js +61 -0
  14. package/dist/build/diff.js.map +1 -0
  15. package/dist/build/export-ir.d.ts +11 -0
  16. package/dist/build/export-ir.d.ts.map +1 -0
  17. package/dist/build/export-ir.js +4 -0
  18. package/dist/build/export-ir.js.map +1 -0
  19. package/dist/build/ground.d.ts +32 -0
  20. package/dist/build/ground.d.ts.map +1 -0
  21. package/dist/build/ground.js +162 -0
  22. package/dist/build/ground.js.map +1 -0
  23. package/dist/build/hash.d.ts +10 -0
  24. package/dist/build/hash.d.ts.map +1 -0
  25. package/dist/build/hash.js +13 -0
  26. package/dist/build/hash.js.map +1 -0
  27. package/dist/build/impact.d.ts +20 -0
  28. package/dist/build/impact.d.ts.map +1 -0
  29. package/dist/build/impact.js +15 -0
  30. package/dist/build/impact.js.map +1 -0
  31. package/dist/build/index.d.ts +14 -0
  32. package/dist/build/index.d.ts.map +1 -0
  33. package/dist/build/index.js +14 -0
  34. package/dist/build/index.js.map +1 -0
  35. package/dist/build/merge.d.ts +27 -0
  36. package/dist/build/merge.d.ts.map +1 -0
  37. package/dist/build/merge.js +85 -0
  38. package/dist/build/merge.js.map +1 -0
  39. package/dist/build/propose.d.ts +16 -0
  40. package/dist/build/propose.d.ts.map +1 -0
  41. package/dist/build/propose.js +126 -0
  42. package/dist/build/propose.js.map +1 -0
  43. package/dist/build/provenance.d.ts +19 -0
  44. package/dist/build/provenance.d.ts.map +1 -0
  45. package/dist/build/provenance.js +23 -0
  46. package/dist/build/provenance.js.map +1 -0
  47. package/dist/build/schema.d.ts +1825 -0
  48. package/dist/build/schema.d.ts.map +1 -0
  49. package/dist/build/schema.js +128 -0
  50. package/dist/build/schema.js.map +1 -0
  51. package/dist/build/sections.d.ts +30 -0
  52. package/dist/build/sections.d.ts.map +1 -0
  53. package/dist/build/sections.js +58 -0
  54. package/dist/build/sections.js.map +1 -0
  55. package/dist/build/tier1.d.ts +24 -0
  56. package/dist/build/tier1.d.ts.map +1 -0
  57. package/dist/build/tier1.js +68 -0
  58. package/dist/build/tier1.js.map +1 -0
  59. package/dist/build/tier2.d.ts +42 -0
  60. package/dist/build/tier2.d.ts.map +1 -0
  61. package/dist/build/tier2.js +187 -0
  62. package/dist/build/tier2.js.map +1 -0
  63. package/dist/build/types.d.ts +42 -0
  64. package/dist/build/types.d.ts.map +1 -0
  65. package/dist/build/types.js +2 -0
  66. package/dist/build/types.js.map +1 -0
  67. package/dist/components/glob.d.ts +10 -0
  68. package/dist/components/glob.d.ts.map +1 -0
  69. package/dist/components/glob.js +52 -0
  70. package/dist/components/glob.js.map +1 -0
  71. package/dist/components/registry.d.ts +51 -0
  72. package/dist/components/registry.d.ts.map +1 -0
  73. package/dist/components/registry.js +121 -0
  74. package/dist/components/registry.js.map +1 -0
  75. package/dist/dsl/factories.d.ts +86 -0
  76. package/dist/dsl/factories.d.ts.map +1 -0
  77. package/dist/dsl/factories.js +145 -0
  78. package/dist/dsl/factories.js.map +1 -0
  79. package/dist/dsl/index.d.ts +46 -0
  80. package/dist/dsl/index.d.ts.map +1 -0
  81. package/dist/dsl/index.js +67 -0
  82. package/dist/dsl/index.js.map +1 -0
  83. package/dist/dsl/reserved-names.typecheck.d.ts +2 -0
  84. package/dist/dsl/reserved-names.typecheck.d.ts.map +1 -0
  85. package/dist/dsl/reserved-names.typecheck.js +16 -0
  86. package/dist/dsl/reserved-names.typecheck.js.map +1 -0
  87. package/dist/dsl/verb-manifest.d.ts +12 -0
  88. package/dist/dsl/verb-manifest.d.ts.map +1 -0
  89. package/dist/dsl/verb-manifest.js +101 -0
  90. package/dist/dsl/verb-manifest.js.map +1 -0
  91. package/dist/fix/apply.d.ts +67 -0
  92. package/dist/fix/apply.d.ts.map +1 -0
  93. package/dist/fix/apply.js +163 -0
  94. package/dist/fix/apply.js.map +1 -0
  95. package/dist/fix/index.d.ts +3 -0
  96. package/dist/fix/index.d.ts.map +1 -0
  97. package/dist/fix/index.js +3 -0
  98. package/dist/fix/index.js.map +1 -0
  99. package/dist/fix/schema.d.ts +180 -0
  100. package/dist/fix/schema.d.ts.map +1 -0
  101. package/dist/fix/schema.js +41 -0
  102. package/dist/fix/schema.js.map +1 -0
  103. package/dist/gates/advisories.d.ts +11 -0
  104. package/dist/gates/advisories.d.ts.map +1 -0
  105. package/dist/gates/advisories.js +27 -0
  106. package/dist/gates/advisories.js.map +1 -0
  107. package/dist/gates/types.d.ts +36 -0
  108. package/dist/gates/types.d.ts.map +1 -0
  109. package/dist/gates/types.js +8 -0
  110. package/dist/gates/types.js.map +1 -0
  111. package/dist/index.d.ts +17 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +17 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/orchestrator.d.ts +51 -0
  116. package/dist/orchestrator.d.ts.map +1 -0
  117. package/dist/orchestrator.js +304 -0
  118. package/dist/orchestrator.js.map +1 -0
  119. package/dist/payload/builder.d.ts +49 -0
  120. package/dist/payload/builder.d.ts.map +1 -0
  121. package/dist/payload/builder.js +61 -0
  122. package/dist/payload/builder.js.map +1 -0
  123. package/dist/payload/mermaid.d.ts +22 -0
  124. package/dist/payload/mermaid.d.ts.map +1 -0
  125. package/dist/payload/mermaid.js +89 -0
  126. package/dist/payload/mermaid.js.map +1 -0
  127. package/dist/plugin/registry.d.ts +24 -0
  128. package/dist/plugin/registry.d.ts.map +1 -0
  129. package/dist/plugin/registry.js +24 -0
  130. package/dist/plugin/registry.js.map +1 -0
  131. package/dist/rules/component-refs.d.ts +26 -0
  132. package/dist/rules/component-refs.d.ts.map +1 -0
  133. package/dist/rules/component-refs.js +81 -0
  134. package/dist/rules/component-refs.js.map +1 -0
  135. package/dist/rules/evaluators.d.ts +50 -0
  136. package/dist/rules/evaluators.d.ts.map +1 -0
  137. package/dist/rules/evaluators.js +236 -0
  138. package/dist/rules/evaluators.js.map +1 -0
  139. package/dist/rules/host-rules.d.ts +109 -0
  140. package/dist/rules/host-rules.d.ts.map +1 -0
  141. package/dist/rules/host-rules.js +152 -0
  142. package/dist/rules/host-rules.js.map +1 -0
  143. package/dist/rules/index.d.ts +7 -0
  144. package/dist/rules/index.d.ts.map +1 -0
  145. package/dist/rules/index.js +7 -0
  146. package/dist/rules/index.js.map +1 -0
  147. package/dist/rules/manifest-evaluators.d.ts +32 -0
  148. package/dist/rules/manifest-evaluators.d.ts.map +1 -0
  149. package/dist/rules/manifest-evaluators.js +124 -0
  150. package/dist/rules/manifest-evaluators.js.map +1 -0
  151. package/dist/rules/rule-category.d.ts +13 -0
  152. package/dist/rules/rule-category.d.ts.map +1 -0
  153. package/dist/rules/rule-category.js +27 -0
  154. package/dist/rules/rule-category.js.map +1 -0
  155. package/dist/rules/tarjan.d.ts +9 -0
  156. package/dist/rules/tarjan.d.ts.map +1 -0
  157. package/dist/rules/tarjan.js +102 -0
  158. package/dist/rules/tarjan.js.map +1 -0
  159. package/dist/scanner.d.ts +12 -0
  160. package/dist/scanner.d.ts.map +1 -0
  161. package/dist/scanner.js +2 -0
  162. package/dist/scanner.js.map +1 -0
  163. package/dist/telemetry/diff.d.ts +27 -0
  164. package/dist/telemetry/diff.d.ts.map +1 -0
  165. package/dist/telemetry/diff.js +54 -0
  166. package/dist/telemetry/diff.js.map +1 -0
  167. package/dist/telemetry/index.d.ts +4 -0
  168. package/dist/telemetry/index.d.ts.map +1 -0
  169. package/dist/telemetry/index.js +4 -0
  170. package/dist/telemetry/index.js.map +1 -0
  171. package/dist/telemetry/serialize.d.ts +10 -0
  172. package/dist/telemetry/serialize.d.ts.map +1 -0
  173. package/dist/telemetry/serialize.js +11 -0
  174. package/dist/telemetry/serialize.js.map +1 -0
  175. package/dist/telemetry/types.d.ts +138 -0
  176. package/dist/telemetry/types.d.ts.map +1 -0
  177. package/dist/telemetry/types.js +3 -0
  178. package/dist/telemetry/types.js.map +1 -0
  179. package/dist/types/branded.d.ts +26 -0
  180. package/dist/types/branded.d.ts.map +1 -0
  181. package/dist/types/branded.js +24 -0
  182. package/dist/types/branded.js.map +1 -0
  183. package/dist/types/graph.d.ts +46 -0
  184. package/dist/types/graph.d.ts.map +1 -0
  185. package/dist/types/graph.js +2 -0
  186. package/dist/types/graph.js.map +1 -0
  187. package/dist/types/index.d.ts +6 -0
  188. package/dist/types/index.d.ts.map +1 -0
  189. package/dist/types/index.js +6 -0
  190. package/dist/types/index.js.map +1 -0
  191. package/dist/types/ir.d.ts +1747 -0
  192. package/dist/types/ir.d.ts.map +1 -0
  193. package/dist/types/ir.js +120 -0
  194. package/dist/types/ir.js.map +1 -0
  195. package/dist/types/manifest.d.ts +58 -0
  196. package/dist/types/manifest.d.ts.map +1 -0
  197. package/dist/types/manifest.js +2 -0
  198. package/dist/types/manifest.js.map +1 -0
  199. package/dist/types/violation.d.ts +94 -0
  200. package/dist/types/violation.d.ts.map +1 -0
  201. package/dist/types/violation.js +56 -0
  202. package/dist/types/violation.js.map +1 -0
  203. 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,7 @@
1
+ export * from './evaluators.js';
2
+ export * from './manifest-evaluators.js';
3
+ export * from './rule-category.js';
4
+ export * from './component-refs.js';
5
+ export * from './host-rules.js';
6
+ export * from './tarjan.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -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,7 @@
1
+ export * from './evaluators.js';
2
+ export * from './manifest-evaluators.js';
3
+ export * from './rule-category.js';
4
+ export * from './component-refs.js';
5
+ export * from './host-rules.js';
6
+ export * from './tarjan.js';
7
+ //# sourceMappingURL=index.js.map
@@ -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"}