@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,61 @@
|
|
|
1
|
+
// Priority sort before pagination/truncation (ADR 007 rule 3): architecture > security > types >
|
|
2
|
+
// lint > format. v1 only ever populates 'architecture', but the ordering contract is fixed now.
|
|
3
|
+
const CATEGORY_PRIORITY = {
|
|
4
|
+
architecture: 0,
|
|
5
|
+
security: 1,
|
|
6
|
+
types: 2,
|
|
7
|
+
lint: 3,
|
|
8
|
+
format: 4,
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Priority sort, cap-per-rule, and page the violations from a `CheckRun` into the structured-only
|
|
12
|
+
* MCP payload shape (ADR 007). Passing gates contribute counts only — never per-item text.
|
|
13
|
+
*/
|
|
14
|
+
export function buildMcpCheckPayload(run, options = {}) {
|
|
15
|
+
const maxPerRule = options.maxPerRule ?? 10;
|
|
16
|
+
const pageSize = options.pageSize ?? 50;
|
|
17
|
+
const offset = options.cursor === undefined ? 0 : Number.parseInt(options.cursor, 10) || 0;
|
|
18
|
+
const allViolations = run.gates.flatMap((g) => g.violations);
|
|
19
|
+
const sorted = sortViolations(allViolations);
|
|
20
|
+
const capped = capPerRule(sorted, maxPerRule);
|
|
21
|
+
const page = capped.slice(offset, offset + pageSize);
|
|
22
|
+
const hasMore = offset + pageSize < capped.length;
|
|
23
|
+
return {
|
|
24
|
+
verdict: run.verdict,
|
|
25
|
+
gates: run.gates.map((g) => ({
|
|
26
|
+
gate: g.gate,
|
|
27
|
+
status: g.status,
|
|
28
|
+
violationCount: g.violations.length,
|
|
29
|
+
baselinedCount: g.baselinedCount,
|
|
30
|
+
...(g.passCount === undefined ? {} : { passCount: g.passCount }),
|
|
31
|
+
})),
|
|
32
|
+
violations: page,
|
|
33
|
+
...(capped.length > pageSize ? { pagination: { cursor: String(offset + pageSize), hasMore } } : {}),
|
|
34
|
+
advisories: run.advisories,
|
|
35
|
+
ungroundedComponents: run.ungroundedComponents,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function sortViolations(violations) {
|
|
39
|
+
return [...violations].sort((a, b) => {
|
|
40
|
+
const byCategory = CATEGORY_PRIORITY[a.category] - CATEGORY_PRIORITY[b.category];
|
|
41
|
+
if (byCategory !== 0)
|
|
42
|
+
return byCategory;
|
|
43
|
+
const byRule = a.ruleId.localeCompare(b.ruleId);
|
|
44
|
+
if (byRule !== 0)
|
|
45
|
+
return byRule;
|
|
46
|
+
return a.file.localeCompare(b.file);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function capPerRule(violations, maxPerRule) {
|
|
50
|
+
const seenPerRule = new Map();
|
|
51
|
+
const out = [];
|
|
52
|
+
for (const v of violations) {
|
|
53
|
+
const count = seenPerRule.get(v.ruleId) ?? 0;
|
|
54
|
+
if (count >= maxPerRule)
|
|
55
|
+
continue;
|
|
56
|
+
seenPerRule.set(v.ruleId, count + 1);
|
|
57
|
+
out.push(v);
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.js","sourceRoot":"","sources":["../../src/payload/builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Violation } from '../types/violation.js';
|
|
2
|
+
/**
|
|
3
|
+
* Fenced Mermaid `graph LR` diagram for one architecture violation instance — an `explain`-only
|
|
4
|
+
* surface (ADR 007: pull-on-demand prose/diagram, never included in the terse `align_check` /
|
|
5
|
+
* `align_violations` machine payloads). One violation's diagram, not the whole rule's violation
|
|
6
|
+
* set — explain answers "why did THIS fire and how do I read it", not a full survey.
|
|
7
|
+
*
|
|
8
|
+
* - `no-cycles`: the cycle's chain, with the suggested break edge drawn as a dashed link labeled
|
|
9
|
+
* `BREAK: <specifier>` instead of a normal solid arrow.
|
|
10
|
+
* - `no-dependency` / `layers`: the two-node offending edge, component/layer names alongside the
|
|
11
|
+
* concrete files, labeled with the forbidden specifier.
|
|
12
|
+
* - `metric`: a single node (there is no offending edge — the violation is file-level, not
|
|
13
|
+
* import-direction-level) labeled with the component, file, and measured-vs-max lines.
|
|
14
|
+
* - `custom`: a single node (a host predicate's violation is whatever shape the predicate chose —
|
|
15
|
+
* there's no structural edge/chain align itself derived) labeled with the file and the
|
|
16
|
+
* predicate's own message.
|
|
17
|
+
* - `manifest-source-hygiene` / `manifest-new-dependency`: a single node (ADR 013 — the manifest
|
|
18
|
+
* scan domain has no import-graph edges to draw) labeled with the declaring manifest, the
|
|
19
|
+
* dependency name, and its specifier.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildViolationMermaid(violation: Violation): string;
|
|
22
|
+
//# sourceMappingURL=mermaid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../../src/payload/mermaid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElE;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,CAuBlE"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fenced Mermaid `graph LR` diagram for one architecture violation instance — an `explain`-only
|
|
3
|
+
* surface (ADR 007: pull-on-demand prose/diagram, never included in the terse `align_check` /
|
|
4
|
+
* `align_violations` machine payloads). One violation's diagram, not the whole rule's violation
|
|
5
|
+
* set — explain answers "why did THIS fire and how do I read it", not a full survey.
|
|
6
|
+
*
|
|
7
|
+
* - `no-cycles`: the cycle's chain, with the suggested break edge drawn as a dashed link labeled
|
|
8
|
+
* `BREAK: <specifier>` instead of a normal solid arrow.
|
|
9
|
+
* - `no-dependency` / `layers`: the two-node offending edge, component/layer names alongside the
|
|
10
|
+
* concrete files, labeled with the forbidden specifier.
|
|
11
|
+
* - `metric`: a single node (there is no offending edge — the violation is file-level, not
|
|
12
|
+
* import-direction-level) labeled with the component, file, and measured-vs-max lines.
|
|
13
|
+
* - `custom`: a single node (a host predicate's violation is whatever shape the predicate chose —
|
|
14
|
+
* there's no structural edge/chain align itself derived) labeled with the file and the
|
|
15
|
+
* predicate's own message.
|
|
16
|
+
* - `manifest-source-hygiene` / `manifest-new-dependency`: a single node (ADR 013 — the manifest
|
|
17
|
+
* scan domain has no import-graph edges to draw) labeled with the declaring manifest, the
|
|
18
|
+
* dependency name, and its specifier.
|
|
19
|
+
*/
|
|
20
|
+
export function buildViolationMermaid(violation) {
|
|
21
|
+
switch (violation.kind) {
|
|
22
|
+
case 'no-cycles':
|
|
23
|
+
return fence(cycleDiagram(violation.chain, violation.suggestedBreakEdge));
|
|
24
|
+
case 'no-dependency':
|
|
25
|
+
return fence(edgeDiagram(violation.fromComponent, violation.fromFile, violation.toComponent, violation.toFile, violation.specifier));
|
|
26
|
+
case 'layers':
|
|
27
|
+
return fence(edgeDiagram(violation.fromLayer, violation.fromFile, violation.toLayer, violation.toFile, violation.specifier));
|
|
28
|
+
case 'metric':
|
|
29
|
+
return fence(metricDiagram(violation.component, violation.file, violation.value, violation.threshold));
|
|
30
|
+
case 'custom':
|
|
31
|
+
return fence(customDiagram(violation.file, violation.hostRuleName, violation.detail));
|
|
32
|
+
case 'manifest-source-hygiene':
|
|
33
|
+
return fence(manifestDiagram(violation.file, violation.depName, violation.specifier));
|
|
34
|
+
case 'manifest-new-dependency':
|
|
35
|
+
return fence(manifestDiagram(violation.file, violation.depName, violation.specifier));
|
|
36
|
+
default: {
|
|
37
|
+
const exhaustive = violation;
|
|
38
|
+
throw new Error(`unhandled violation kind: ${JSON.stringify(exhaustive)}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function fence(body) {
|
|
43
|
+
return `\`\`\`mermaid\n${body}\n\`\`\``;
|
|
44
|
+
}
|
|
45
|
+
function cycleDiagram(chain, suggestedBreakEdge) {
|
|
46
|
+
const ids = new Map();
|
|
47
|
+
const lines = ['graph LR'];
|
|
48
|
+
const idFor = (file) => {
|
|
49
|
+
const existing = ids.get(file);
|
|
50
|
+
if (existing !== undefined)
|
|
51
|
+
return existing;
|
|
52
|
+
const id = `n${ids.size}`;
|
|
53
|
+
ids.set(file, id);
|
|
54
|
+
lines.push(` ${id}["${escapeLabel(file)}"]`);
|
|
55
|
+
return id;
|
|
56
|
+
};
|
|
57
|
+
for (const hop of chain) {
|
|
58
|
+
const fromId = idFor(hop.from);
|
|
59
|
+
const toId = idFor(hop.to);
|
|
60
|
+
lines.push(sameEdge(hop, suggestedBreakEdge)
|
|
61
|
+
? ` ${fromId} -. "BREAK: ${escapeLabel(hop.specifier)}" .-> ${toId}`
|
|
62
|
+
: ` ${fromId} -->|"${escapeLabel(hop.specifier)}"| ${toId}`);
|
|
63
|
+
}
|
|
64
|
+
return lines.join('\n');
|
|
65
|
+
}
|
|
66
|
+
function sameEdge(a, b) {
|
|
67
|
+
return a.from === b.from && a.to === b.to && a.specifier === b.specifier;
|
|
68
|
+
}
|
|
69
|
+
function edgeDiagram(fromLabel, fromFile, toLabel, toFile, specifier) {
|
|
70
|
+
return [
|
|
71
|
+
'graph LR',
|
|
72
|
+
` a["${escapeLabel(fromLabel)}<br/>${escapeLabel(fromFile)}"]`,
|
|
73
|
+
` b["${escapeLabel(toLabel)}<br/>${escapeLabel(toFile)}"]`,
|
|
74
|
+
` a -->|"${escapeLabel(specifier)} (forbidden)"| b`,
|
|
75
|
+
].join('\n');
|
|
76
|
+
}
|
|
77
|
+
function metricDiagram(component, file, value, threshold) {
|
|
78
|
+
return ['graph LR', ` a["${escapeLabel(component)}<br/>${escapeLabel(file)}<br/>${value} lines (max ${threshold})"]`].join('\n');
|
|
79
|
+
}
|
|
80
|
+
function customDiagram(file, hostRuleName, detail) {
|
|
81
|
+
return ['graph LR', ` a["${escapeLabel(file)}<br/>predicate: ${escapeLabel(hostRuleName)}<br/>${escapeLabel(detail)}"]`].join('\n');
|
|
82
|
+
}
|
|
83
|
+
function manifestDiagram(file, depName, specifier) {
|
|
84
|
+
return ['graph LR', ` a["${escapeLabel(file)}<br/>${escapeLabel(depName)}: ${escapeLabel(specifier)}"]`].join('\n');
|
|
85
|
+
}
|
|
86
|
+
function escapeLabel(s) {
|
|
87
|
+
return s.replace(/"/g, '\\"');
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=mermaid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mermaid.js","sourceRoot":"","sources":["../../src/payload/mermaid.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { RepoRelativePath } from '../types/branded.js';
|
|
2
|
+
import type { Scanner } from '../scanner.js';
|
|
3
|
+
export interface LanguagePlugin {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly fileMatch: readonly string[];
|
|
6
|
+
readonly scanner: Scanner;
|
|
7
|
+
}
|
|
8
|
+
export interface PluginRegistry {
|
|
9
|
+
getPluginForFile(file: RepoRelativePath): LanguagePlugin | undefined;
|
|
10
|
+
getAllPlugins(): readonly LanguagePlugin[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* v1's registry implementation is a one-element static list — no cross-plugin file-match
|
|
14
|
+
* conflict resolution, no priority ordering, no merged-graph logic across plugins. Those are
|
|
15
|
+
* real problems only a second `LanguagePlugin` creates; the interface is written generically so
|
|
16
|
+
* adding one is additive at the CLI composition root, not an interface rewrite.
|
|
17
|
+
*/
|
|
18
|
+
export declare class StaticPluginRegistry implements PluginRegistry {
|
|
19
|
+
private readonly plugins;
|
|
20
|
+
constructor(plugins: readonly LanguagePlugin[]);
|
|
21
|
+
getPluginForFile(file: RepoRelativePath): LanguagePlugin | undefined;
|
|
22
|
+
getAllPlugins(): readonly LanguagePlugin[];
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/plugin/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,GAAG,cAAc,GAAG,SAAS,CAAC;IACrE,aAAa,IAAI,SAAS,cAAc,EAAE,CAAC;CAC5C;AAED;;;;;GAKG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,SAAS,cAAc,EAAE;IAE/D,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,GAAG,cAAc,GAAG,SAAS;IAOpE,aAAa,IAAI,SAAS,cAAc,EAAE;CAG3C"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { globMatch } from '../components/glob.js';
|
|
2
|
+
/**
|
|
3
|
+
* v1's registry implementation is a one-element static list — no cross-plugin file-match
|
|
4
|
+
* conflict resolution, no priority ordering, no merged-graph logic across plugins. Those are
|
|
5
|
+
* real problems only a second `LanguagePlugin` creates; the interface is written generically so
|
|
6
|
+
* adding one is additive at the CLI composition root, not an interface rewrite.
|
|
7
|
+
*/
|
|
8
|
+
export class StaticPluginRegistry {
|
|
9
|
+
plugins;
|
|
10
|
+
constructor(plugins) {
|
|
11
|
+
this.plugins = plugins;
|
|
12
|
+
}
|
|
13
|
+
getPluginForFile(file) {
|
|
14
|
+
for (const plugin of this.plugins) {
|
|
15
|
+
if (plugin.fileMatch.some((pattern) => globMatch(pattern, file)))
|
|
16
|
+
return plugin;
|
|
17
|
+
}
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
getAllPlugins() {
|
|
21
|
+
return this.plugins;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/plugin/registry.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAalD;;;;;GAKG;AACH,MAAM,OAAO,oBAAoB;IACF;IAA7B,YAA6B,OAAkC;QAAlC,YAAO,GAAP,OAAO,CAA2B;IAAG,CAAC;IAEnE,gBAAgB,CAAC,IAAsB;QACrC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;gBAAE,OAAO,MAAM,CAAC;QAClF,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;CACF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ComponentName } from '../types/branded.js';
|
|
2
|
+
import type { ComponentDefinitionIR, RuleIR } from '../types/ir.js';
|
|
3
|
+
/** Thrown when a rule (hand-authored in `align.config.ts` or machine-written in
|
|
4
|
+
* `.align/generated-rules.json`) references a component name absent from the current components
|
|
5
|
+
* registry — a config-load-time hard error (ADR 003's empty-selector-fails-by-default doctrine
|
|
6
|
+
* extended to the symmetric case: a *ComponentRef* naming a component that no longer exists).
|
|
7
|
+
* Without this check, `evaluateRule` (`rules/evaluators.ts`) simply never matches a component
|
|
8
|
+
* name absent from the graph, so the rule silently evaluates to zero violations — a false-green,
|
|
9
|
+
* severity-zero bug class (ARCHITECTURE.md's stated invariant), most commonly triggered by a
|
|
10
|
+
* component rename/removal in `align.config.ts` leaving a stale reference behind in generated
|
|
11
|
+
* or hand-authored rules. */
|
|
12
|
+
export declare class UnknownComponentRefError extends Error {
|
|
13
|
+
readonly ruleId: string;
|
|
14
|
+
readonly componentName: string;
|
|
15
|
+
constructor(ruleId: string, componentName: string);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Load-time validation (companion to `components/registry.ts`'s `validateComponents`, which
|
|
19
|
+
* checks components against files; this checks rules against components): every `ComponentRef`
|
|
20
|
+
* embedded in every rule — merged generated + hand-authored alike, since both share one
|
|
21
|
+
* `RuleIR[]` by the time this runs (`build/merge.ts`) — must name a component present in the
|
|
22
|
+
* registry. Throws `UnknownComponentRefError` on the first offender found (fail-fast, same
|
|
23
|
+
* convention as `validateComponents`), not a collected list — one bad ref is enough to halt.
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateRuleComponentRefs(rules: readonly RuleIR[], components: Readonly<Record<ComponentName, ComponentDefinitionIR>>): void;
|
|
26
|
+
//# sourceMappingURL=component-refs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-refs.d.ts","sourceRoot":"","sources":["../../src/rules/component-refs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAEpE;;;;;;;;6BAQ6B;AAC7B,qBAAa,wBAAyB,SAAQ,KAAK;aAI/B,MAAM,EAAE,MAAM;aACd,aAAa,EAAE,MAAM;gBADrB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM;CAUxC;AAwCD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,qBAAqB,CAAC,CAAC,GACjE,IAAI,CAQN"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/** Thrown when a rule (hand-authored in `align.config.ts` or machine-written in
|
|
2
|
+
* `.align/generated-rules.json`) references a component name absent from the current components
|
|
3
|
+
* registry — a config-load-time hard error (ADR 003's empty-selector-fails-by-default doctrine
|
|
4
|
+
* extended to the symmetric case: a *ComponentRef* naming a component that no longer exists).
|
|
5
|
+
* Without this check, `evaluateRule` (`rules/evaluators.ts`) simply never matches a component
|
|
6
|
+
* name absent from the graph, so the rule silently evaluates to zero violations — a false-green,
|
|
7
|
+
* severity-zero bug class (ARCHITECTURE.md's stated invariant), most commonly triggered by a
|
|
8
|
+
* component rename/removal in `align.config.ts` leaving a stale reference behind in generated
|
|
9
|
+
* or hand-authored rules. */
|
|
10
|
+
export class UnknownComponentRefError extends Error {
|
|
11
|
+
ruleId;
|
|
12
|
+
componentName;
|
|
13
|
+
constructor(
|
|
14
|
+
// Matches `RuleIR['id']`'s actual (unbranded) type — same convention as
|
|
15
|
+
// `GeneratedRuleCollisionError` in `build/merge.ts`.
|
|
16
|
+
ruleId, componentName) {
|
|
17
|
+
super(`Rule '${ruleId}' references unknown component '${componentName}', which is not defined in ` +
|
|
18
|
+
`the components registry. Likely cause: the component was renamed or removed since this rule ` +
|
|
19
|
+
`was written — if this is a generated rule, re-run \`align build\` to refresh ` +
|
|
20
|
+
`.align/generated-rules.json; if hand-authored, update align.config.ts.`);
|
|
21
|
+
this.ruleId = ruleId;
|
|
22
|
+
this.componentName = componentName;
|
|
23
|
+
this.name = 'UnknownComponentRefError';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Every `ComponentRef` a rule embeds, in a fixed, deterministic order — one generator per IR
|
|
27
|
+
* kind, exhaustively switched (a new `RuleIR` kind missing a case here is a compile error, same
|
|
28
|
+
* discipline as `evaluateRule`'s dispatcher). `custom.host` carries no `ComponentRef` — it
|
|
29
|
+
* references a host predicate by name (`hostRuleName`), validated by `rules/host-rules.ts`'s
|
|
30
|
+
* `validateHostRules`, not by this component-reference guard. */
|
|
31
|
+
function* componentRefsOf(rule) {
|
|
32
|
+
switch (rule.kind) {
|
|
33
|
+
case 'arch.no-dependency':
|
|
34
|
+
yield rule.from;
|
|
35
|
+
yield rule.to;
|
|
36
|
+
return;
|
|
37
|
+
case 'arch.no-cycles':
|
|
38
|
+
if (rule.scope !== 'repo')
|
|
39
|
+
yield rule.scope;
|
|
40
|
+
return;
|
|
41
|
+
case 'arch.layers':
|
|
42
|
+
for (const layerDef of rule.layers) {
|
|
43
|
+
yield layerDef.layer;
|
|
44
|
+
yield* layerDef.canDependOn;
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
47
|
+
case 'arch.metric':
|
|
48
|
+
yield rule.target;
|
|
49
|
+
return;
|
|
50
|
+
case 'custom.host':
|
|
51
|
+
return;
|
|
52
|
+
// `security.manifest.*` (ADR 013) carries no `ComponentRef` — the manifest scan domain has no
|
|
53
|
+
// notion of align's file-classified components (same no-op shape as `custom.host`, which
|
|
54
|
+
// references a host predicate by name instead).
|
|
55
|
+
case 'security.manifest.source-hygiene':
|
|
56
|
+
case 'security.manifest.new-dependency':
|
|
57
|
+
return;
|
|
58
|
+
default: {
|
|
59
|
+
const exhaustive = rule;
|
|
60
|
+
throw new Error(`unhandled rule kind: ${JSON.stringify(exhaustive)}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Load-time validation (companion to `components/registry.ts`'s `validateComponents`, which
|
|
66
|
+
* checks components against files; this checks rules against components): every `ComponentRef`
|
|
67
|
+
* embedded in every rule — merged generated + hand-authored alike, since both share one
|
|
68
|
+
* `RuleIR[]` by the time this runs (`build/merge.ts`) — must name a component present in the
|
|
69
|
+
* registry. Throws `UnknownComponentRefError` on the first offender found (fail-fast, same
|
|
70
|
+
* convention as `validateComponents`), not a collected list — one bad ref is enough to halt.
|
|
71
|
+
*/
|
|
72
|
+
export function validateRuleComponentRefs(rules, components) {
|
|
73
|
+
for (const rule of rules) {
|
|
74
|
+
for (const ref of componentRefsOf(rule)) {
|
|
75
|
+
if (!(ref in components)) {
|
|
76
|
+
throw new UnknownComponentRefError(rule.id, ref);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=component-refs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-refs.js","sourceRoot":"","sources":["../../src/rules/component-refs.ts"],"names":[],"mappings":"AAGA;;;;;;;;6BAQ6B;AAC7B,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IAI/B;IACA;IAJlB;IACE,wEAAwE;IACxE,qDAAqD;IACrC,MAAc,EACd,aAAqB;QAErC,KAAK,CACH,SAAS,MAAM,mCAAmC,aAAa,6BAA6B;YAC1F,8FAA8F;YAC9F,+EAA+E;YAC/E,wEAAwE,CAC3E,CAAC;QARc,WAAM,GAAN,MAAM,CAAQ;QACd,kBAAa,GAAb,aAAa,CAAQ;QAQrC,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AAED;;;;iEAIiE;AACjE,QAAQ,CAAC,CAAC,eAAe,CAAC,IAAY;IACpC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,oBAAoB;YACvB,MAAM,IAAI,CAAC,IAAI,CAAC;YAChB,MAAM,IAAI,CAAC,EAAE,CAAC;YACd,OAAO;QACT,KAAK,gBAAgB;YACnB,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM;gBAAE,MAAM,IAAI,CAAC,KAAK,CAAC;YAC5C,OAAO;QACT,KAAK,aAAa;YAChB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACnC,MAAM,QAAQ,CAAC,KAAK,CAAC;gBACrB,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;YAC9B,CAAC;YACD,OAAO;QACT,KAAK,aAAa;YAChB,MAAM,IAAI,CAAC,MAAM,CAAC;YAClB,OAAO;QACT,KAAK,aAAa;YAChB,OAAO;QACT,8FAA8F;QAC9F,yFAAyF;QACzF,gDAAgD;QAChD,KAAK,kCAAkC,CAAC;QACxC,KAAK,kCAAkC;YACrC,OAAO;QACT,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,UAAU,GAAU,IAAI,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB,CACvC,KAAwB,EACxB,UAAkE;IAElE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;YACxC,IAAI,CAAC,CAAC,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC;gBACzB,MAAM,IAAI,wBAAwB,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ComponentName } from '../types/branded.js';
|
|
2
|
+
import type { ArchLayersRule, ArchMetricRule, ArchNoCyclesRule, ArchNoDependencyRule, ComponentDefinitionIR, RuleIR } from '../types/ir.js';
|
|
3
|
+
import type { DependencyGraph } from '../types/graph.js';
|
|
4
|
+
import type { Violation } from '../types/violation.js';
|
|
5
|
+
import { type HostPredicateRegistry } from './host-rules.js';
|
|
6
|
+
/**
|
|
7
|
+
* Pure function: (rule, graph, components) -> violations. No I/O, no mutation, fully testable
|
|
8
|
+
* with plain data (CODING_BEST_PRACTICES.md §14). One evaluator per RuleIR kind; `evaluateRule`
|
|
9
|
+
* dispatches by `kind` through an exhaustive switch so a new IR kind missing an evaluator is a
|
|
10
|
+
* compile error, not a silent no-op.
|
|
11
|
+
*/
|
|
12
|
+
export type RuleEvaluator<TRule extends RuleIR = RuleIR> = (rule: TRule, graph: DependencyGraph, components: Readonly<Record<ComponentName, ComponentDefinitionIR>>) => readonly Violation[];
|
|
13
|
+
export declare function becauseField(because: string | undefined): {
|
|
14
|
+
readonly because: string;
|
|
15
|
+
} | Record<string, never>;
|
|
16
|
+
export declare const evaluateNoDependency: RuleEvaluator<ArchNoDependencyRule>;
|
|
17
|
+
export declare const evaluateNoCycles: RuleEvaluator<ArchNoCyclesRule>;
|
|
18
|
+
export declare const evaluateLayers: RuleEvaluator<ArchLayersRule>;
|
|
19
|
+
/**
|
|
20
|
+
* `arch.metric` (max-LOC only, promoted 2026-07-12 on kluster ruleset evidence —
|
|
21
|
+
* IMPLEMENTATION_PLAN.md's Promotion log: two 2,100+-line files were structurally invisible to
|
|
22
|
+
* every dependency/cycle rule). One violation per file classified to `rule.target` whose `loc`
|
|
23
|
+
* exceeds `rule.max` — `loc` is already on every `DependencyGraphNode` (no new scanning).
|
|
24
|
+
*/
|
|
25
|
+
export declare const evaluateMetric: RuleEvaluator<ArchMetricRule>;
|
|
26
|
+
/**
|
|
27
|
+
* Exhaustive dispatcher: a new `RuleIR` discriminant without a case here is a compile error
|
|
28
|
+
* (never-check, CODING_BEST_PRACTICES.md §17.2), not a silent no-op.
|
|
29
|
+
*
|
|
30
|
+
* `hostPredicates` defaults to the empty registry — every `arch.*` evaluator ignores it entirely
|
|
31
|
+
* (they never took it before registration existed); only `custom.host` reads it. A predicate that
|
|
32
|
+
* throws propagates out of this function uncaught (`HostPredicateExecutionError`) exactly like a
|
|
33
|
+
* malformed rule would — the orchestrator's evaluation-loop guard is what turns that into gate
|
|
34
|
+
* `error` (`orchestrator.ts`), not this function, which stays a pure dispatcher.
|
|
35
|
+
*
|
|
36
|
+
* `security.manifest.*` kinds (ADR 013) return `[]` here, deliberately: this dispatcher only ever
|
|
37
|
+
* receives a `DependencyGraph` (TS-source scan output), and manifest rules evaluate against a
|
|
38
|
+
* disjoint scan domain (`ManifestInventory`) that this function never has access to. They are real
|
|
39
|
+
* `RuleIR` members (needed so the DSL/tier-2/build pipeline can author and round-trip them), but
|
|
40
|
+
* their actual evaluation always goes through `evaluateManifestRule`
|
|
41
|
+
* (`rules/manifest-evaluators.ts`) against real manifest data — `GateOrchestrator`'s `security` gate
|
|
42
|
+
* calls it directly and never routes these kinds through this function (`ruleCategoryOf` partitions
|
|
43
|
+
* `RulesetIR.rules` before either dispatcher runs, `rules/rule-category.ts`). Returning `[]` here
|
|
44
|
+
* (rather than throwing) keeps `align build`/`align explain`'s generic graph-based preview paths
|
|
45
|
+
* working without a manifest scan available to them — see ADR 013's follow-up ladder for the known
|
|
46
|
+
* gap this leaves (their impact-delta preview under-reports manifest-rule violations; `align check`
|
|
47
|
+
* remains authoritative).
|
|
48
|
+
*/
|
|
49
|
+
export declare function evaluateRule(rule: RuleIR, graph: DependencyGraph, components: Readonly<Record<ComponentName, ComponentDefinitionIR>>, hostPredicates?: HostPredicateRegistry): readonly Violation[];
|
|
50
|
+
//# sourceMappingURL=evaluators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluators.d.ts","sourceRoot":"","sources":["../../src/rules/evaluators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAoB,MAAM,qBAAqB,CAAC;AAE3E,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC5I,OAAO,KAAK,EAAE,eAAe,EAAiC,MAAM,mBAAmB,CAAC;AACxF,OAAO,KAAK,EAAa,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGlE,OAAO,EAAsB,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAQjF;;;;;GAKG;AACH,MAAM,MAAM,aAAa,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,IAAI,CACzD,IAAI,EAAE,KAAK,EACX,KAAK,EAAE,eAAe,EACtB,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,qBAAqB,CAAC,CAAC,KAC/D,SAAS,SAAS,EAAE,CAAC;AAO1B,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAE9G;AAED,eAAO,MAAM,oBAAoB,EAAE,aAAa,CAAC,oBAAoB,CA8BpE,CAAC;AAKF,eAAO,MAAM,gBAAgB,EAAE,aAAa,CAAC,gBAAgB,CA+D5D,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,cAAc,CAoCxD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,cAAc,CAyBxD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,eAAe,EACtB,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,qBAAqB,CAAC,CAAC,EAClE,cAAc,GAAE,qBAA0C,GACzD,SAAS,SAAS,EAAE,CAoBtB"}
|