@nielspeter/eess 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 (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -0
  3. package/dist/ansi.d.ts +7 -0
  4. package/dist/ansi.d.ts.map +1 -0
  5. package/dist/ansi.js +16 -0
  6. package/dist/ansi.js.map +1 -0
  7. package/dist/apply-fixes.d.ts +25 -0
  8. package/dist/apply-fixes.d.ts.map +1 -0
  9. package/dist/apply-fixes.js +52 -0
  10. package/dist/apply-fixes.js.map +1 -0
  11. package/dist/baseline-generator.d.ts +18 -0
  12. package/dist/baseline-generator.d.ts.map +1 -0
  13. package/dist/baseline-generator.js +29 -0
  14. package/dist/baseline-generator.js.map +1 -0
  15. package/dist/baseline.d.ts +83 -0
  16. package/dist/baseline.d.ts.map +1 -0
  17. package/dist/baseline.js +115 -0
  18. package/dist/baseline.js.map +1 -0
  19. package/dist/check-options.d.ts +47 -0
  20. package/dist/check-options.d.ts.map +1 -0
  21. package/dist/check-options.js +2 -0
  22. package/dist/check-options.js.map +1 -0
  23. package/dist/code-frame.d.ts +17 -0
  24. package/dist/code-frame.d.ts.map +1 -0
  25. package/dist/code-frame.js +32 -0
  26. package/dist/code-frame.js.map +1 -0
  27. package/dist/combinators.d.ts +55 -0
  28. package/dist/combinators.d.ts.map +1 -0
  29. package/dist/combinators.js +43 -0
  30. package/dist/combinators.js.map +1 -0
  31. package/dist/condition.d.ts +56 -0
  32. package/dist/condition.d.ts.map +1 -0
  33. package/dist/condition.js +2 -0
  34. package/dist/condition.js.map +1 -0
  35. package/dist/correspondence.d.ts +78 -0
  36. package/dist/correspondence.d.ts.map +1 -0
  37. package/dist/correspondence.js +127 -0
  38. package/dist/correspondence.js.map +1 -0
  39. package/dist/define.d.ts +41 -0
  40. package/dist/define.d.ts.map +1 -0
  41. package/dist/define.js +42 -0
  42. package/dist/define.js.map +1 -0
  43. package/dist/diff-aware.d.ts +37 -0
  44. package/dist/diff-aware.d.ts.map +1 -0
  45. package/dist/diff-aware.js +69 -0
  46. package/dist/diff-aware.js.map +1 -0
  47. package/dist/environment.d.ts +14 -0
  48. package/dist/environment.d.ts.map +1 -0
  49. package/dist/environment.js +20 -0
  50. package/dist/environment.js.map +1 -0
  51. package/dist/errors.d.ts +14 -0
  52. package/dist/errors.d.ts.map +1 -0
  53. package/dist/errors.js +21 -0
  54. package/dist/errors.js.map +1 -0
  55. package/dist/exclusion-comments.d.ts +52 -0
  56. package/dist/exclusion-comments.d.ts.map +1 -0
  57. package/dist/exclusion-comments.js +183 -0
  58. package/dist/exclusion-comments.js.map +1 -0
  59. package/dist/execute-rule.d.ts +31 -0
  60. package/dist/execute-rule.d.ts.map +1 -0
  61. package/dist/execute-rule.js +119 -0
  62. package/dist/execute-rule.js.map +1 -0
  63. package/dist/format-github.d.ts +25 -0
  64. package/dist/format-github.d.ts.map +1 -0
  65. package/dist/format-github.js +44 -0
  66. package/dist/format-github.js.map +1 -0
  67. package/dist/format-json.d.ts +12 -0
  68. package/dist/format-json.d.ts.map +1 -0
  69. package/dist/format-json.js +30 -0
  70. package/dist/format-json.js.map +1 -0
  71. package/dist/format.d.ts +25 -0
  72. package/dist/format.d.ts.map +1 -0
  73. package/dist/format.js +66 -0
  74. package/dist/format.js.map +1 -0
  75. package/dist/index.d.ts +42 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +40 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/matching.d.ts +31 -0
  80. package/dist/matching.d.ts.map +1 -0
  81. package/dist/matching.js +64 -0
  82. package/dist/matching.js.map +1 -0
  83. package/dist/predicate.d.ts +11 -0
  84. package/dist/predicate.d.ts.map +1 -0
  85. package/dist/predicate.js +2 -0
  86. package/dist/predicate.js.map +1 -0
  87. package/dist/preset-dispatch.d.ts +46 -0
  88. package/dist/preset-dispatch.d.ts.map +1 -0
  89. package/dist/preset-dispatch.js +48 -0
  90. package/dist/preset-dispatch.js.map +1 -0
  91. package/dist/report.d.ts +38 -0
  92. package/dist/report.d.ts.map +1 -0
  93. package/dist/report.js +45 -0
  94. package/dist/report.js.map +1 -0
  95. package/dist/rule-builder.d.ts +188 -0
  96. package/dist/rule-builder.d.ts.map +1 -0
  97. package/dist/rule-builder.js +323 -0
  98. package/dist/rule-builder.js.map +1 -0
  99. package/dist/rule-description.d.ts +12 -0
  100. package/dist/rule-description.d.ts.map +1 -0
  101. package/dist/rule-description.js +2 -0
  102. package/dist/rule-description.js.map +1 -0
  103. package/dist/rule-metadata.d.ts +17 -0
  104. package/dist/rule-metadata.d.ts.map +1 -0
  105. package/dist/rule-metadata.js +2 -0
  106. package/dist/rule-metadata.js.map +1 -0
  107. package/dist/silent-exclusion.d.ts +38 -0
  108. package/dist/silent-exclusion.d.ts.map +1 -0
  109. package/dist/silent-exclusion.js +30 -0
  110. package/dist/silent-exclusion.js.map +1 -0
  111. package/dist/terminal-builder.d.ts +83 -0
  112. package/dist/terminal-builder.d.ts.map +1 -0
  113. package/dist/terminal-builder.js +134 -0
  114. package/dist/terminal-builder.js.map +1 -0
  115. package/dist/violation.d.ts +51 -0
  116. package/dist/violation.d.ts.map +1 -0
  117. package/dist/violation.js +2 -0
  118. package/dist/violation.js.map +1 -0
  119. package/package.json +40 -0
@@ -0,0 +1,52 @@
1
+ import type { ArchViolation } from './violation.js';
2
+ /**
3
+ * Exclusion comment parsed from source code.
4
+ */
5
+ export interface ExclusionComment {
6
+ /** Rule ID being excluded */
7
+ ruleId: string;
8
+ /** Required reason for the exclusion */
9
+ reason: string;
10
+ /** File path where the comment was found */
11
+ file: string;
12
+ /** Line number of the comment */
13
+ line: number;
14
+ /** Whether this is a block exclusion (start/end) */
15
+ isBlock: boolean;
16
+ /** End line for block exclusions */
17
+ endLine?: number;
18
+ }
19
+ /**
20
+ * Validation warning for exclusion comments.
21
+ */
22
+ export interface ExclusionWarning {
23
+ /** Warning message */
24
+ message: string;
25
+ /** File path */
26
+ file: string;
27
+ /** Line number */
28
+ line: number;
29
+ }
30
+ /**
31
+ * Result of parsing exclusion comments from a source file.
32
+ */
33
+ export interface ParseResult {
34
+ /** Successfully parsed exclusion comments */
35
+ exclusions: ExclusionComment[];
36
+ /** Warnings about malformed comments */
37
+ warnings: ExclusionWarning[];
38
+ }
39
+ /**
40
+ * Scan a source file for eess-exclude comments.
41
+ *
42
+ * Supported formats:
43
+ * // eess-exclude <rule-id>: <reason>
44
+ * // eess-exclude-start <rule-id>: <reason>
45
+ * // eess-exclude-end
46
+ * // eess-exclude <rule-a>, <rule-b>: <reason>
47
+ * <!-- eess-exclude <rule-id>: <reason> --> (markdown / text dialects)
48
+ * <!-- eess-exclude-start <rule-id>: <reason> --> / <!-- eess-exclude-end -->
49
+ */
50
+ export declare function parseExclusionComments(sourceText: string, filePath: string): ParseResult;
51
+ export declare function isExcludedByComment(violation: ArchViolation, comments: ExclusionComment[]): boolean;
52
+ //# sourceMappingURL=exclusion-comments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exclusion-comments.d.ts","sourceRoot":"","sources":["../src/exclusion-comments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAA;IACZ,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,oDAAoD;IACpD,OAAO,EAAE,OAAO,CAAA;IAChB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAA;CACb;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,6CAA6C;IAC7C,UAAU,EAAE,gBAAgB,EAAE,CAAA;IAC9B,wCAAwC;IACxC,QAAQ,EAAE,gBAAgB,EAAE,CAAA;CAC7B;AAuJD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,CAyCxF;AAuBD,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,aAAa,EACxB,QAAQ,EAAE,gBAAgB,EAAE,GAC3B,OAAO,CAUT"}
@@ -0,0 +1,183 @@
1
+ // Single-line: // eess-exclude <rule-id>[, <rule-id>]: <reason>
2
+ // Single-line without reason: // eess-exclude <rule-id>
3
+ const SINGLE_LINE_RE = /\/\/\s*eess-exclude\s+(.+)/;
4
+ // Block start: // eess-exclude-start <rule-id>[, <rule-id>]: <reason>
5
+ const BLOCK_START_RE = /\/\/\s*eess-exclude-start\s+(.+)/;
6
+ // Block end: // eess-exclude-end
7
+ const BLOCK_END_RE = /\/\/\s*eess-exclude-end\b/;
8
+ // HTML-comment forms — the same directives for text dialects whose sources
9
+ // aren't `//`-commented (markdown corpora, and any future text dialect).
10
+ // <!-- eess-exclude <rule-id>[, <rule-id>]: <reason> -->
11
+ const HTML_SINGLE_RE = /<!--\s*eess-exclude\s+(.+?)\s*-->/;
12
+ const HTML_BLOCK_START_RE = /<!--\s*eess-exclude-start\s+(.+?)\s*-->/;
13
+ const HTML_BLOCK_END_RE = /<!--\s*eess-exclude-end\s*-->/;
14
+ /**
15
+ * Parse rule IDs and reason from the content after the directive keyword.
16
+ *
17
+ * Format: `rule-a, rule-b: reason text`
18
+ * If no colon is present, all content is treated as rule IDs and reason is empty.
19
+ */
20
+ function parseRuleIdsAndReason(content) {
21
+ const colonIndex = content.indexOf(':');
22
+ if (colonIndex < 0) {
23
+ // No colon — all content is rule IDs, no reason
24
+ const ruleIds = content
25
+ .split(',')
26
+ .map((s) => s.trim())
27
+ .filter((s) => s.length > 0);
28
+ return { ruleIds, reason: '' };
29
+ }
30
+ const idsPart = content.slice(0, colonIndex);
31
+ const reason = content.slice(colonIndex + 1).trim();
32
+ const ruleIds = idsPart
33
+ .split(',')
34
+ .map((s) => s.trim())
35
+ .filter((s) => s.length > 0);
36
+ return { ruleIds, reason };
37
+ }
38
+ /** Handle a block-end directive line. */
39
+ function handleBlockEnd(openBlocks, exclusions, warnings, filePath, lineNum) {
40
+ if (openBlocks.size === 0) {
41
+ warnings.push({
42
+ message: `eess-exclude-end without matching start`,
43
+ file: filePath,
44
+ line: lineNum,
45
+ });
46
+ return;
47
+ }
48
+ for (const [, comment] of openBlocks) {
49
+ comment.endLine = lineNum;
50
+ exclusions.push(comment);
51
+ }
52
+ openBlocks.clear();
53
+ }
54
+ /** Emit undocumented-exclusion warnings for each rule ID when no reason is given. */
55
+ function warnUndocumented(warnings, ruleIds, directive, filePath, lineNum) {
56
+ for (const ruleId of ruleIds) {
57
+ warnings.push({
58
+ message: `Undocumented exclusion at ${filePath}:${String(lineNum)} — ` +
59
+ `// ${directive} ${ruleId}\n` +
60
+ ` Fix: Add a reason — // ${directive} ${ruleId}: <why>`,
61
+ file: filePath,
62
+ line: lineNum,
63
+ });
64
+ }
65
+ }
66
+ /** Handle a block-start directive line. */
67
+ function handleBlockStart(content, openBlocks, warnings, filePath, lineNum) {
68
+ if (openBlocks.size > 0) {
69
+ warnings.push({
70
+ message: `Nested eess-exclude-start — close existing block first`,
71
+ file: filePath,
72
+ line: lineNum,
73
+ });
74
+ return;
75
+ }
76
+ const { ruleIds, reason } = parseRuleIdsAndReason(content);
77
+ if (reason === '') {
78
+ warnUndocumented(warnings, ruleIds, 'eess-exclude-start', filePath, lineNum);
79
+ }
80
+ for (const ruleId of ruleIds) {
81
+ openBlocks.set(ruleId, {
82
+ ruleId,
83
+ reason,
84
+ file: filePath,
85
+ line: lineNum,
86
+ isBlock: true,
87
+ });
88
+ }
89
+ }
90
+ /** Handle a single-line exclude directive. */
91
+ function handleSingleLine(content, exclusions, warnings, filePath, lineNum) {
92
+ // Skip if this was a block start or end (already handled above, but guard)
93
+ if (content.startsWith('-start') || content.startsWith('-end'))
94
+ return;
95
+ const { ruleIds, reason } = parseRuleIdsAndReason(content);
96
+ if (reason === '') {
97
+ warnUndocumented(warnings, ruleIds, 'eess-exclude', filePath, lineNum);
98
+ }
99
+ for (const ruleId of ruleIds) {
100
+ exclusions.push({
101
+ ruleId,
102
+ reason,
103
+ file: filePath,
104
+ line: lineNum,
105
+ isBlock: false,
106
+ });
107
+ }
108
+ }
109
+ /**
110
+ * Scan a source file for eess-exclude comments.
111
+ *
112
+ * Supported formats:
113
+ * // eess-exclude <rule-id>: <reason>
114
+ * // eess-exclude-start <rule-id>: <reason>
115
+ * // eess-exclude-end
116
+ * // eess-exclude <rule-a>, <rule-b>: <reason>
117
+ * <!-- eess-exclude <rule-id>: <reason> --> (markdown / text dialects)
118
+ * <!-- eess-exclude-start <rule-id>: <reason> --> / <!-- eess-exclude-end -->
119
+ */
120
+ export function parseExclusionComments(sourceText, filePath) {
121
+ const lines = sourceText.split('\n');
122
+ const exclusions = [];
123
+ const warnings = [];
124
+ const openBlocks = new Map();
125
+ for (let i = 0; i < lines.length; i++) {
126
+ const line = lines[i];
127
+ if (line === undefined)
128
+ continue;
129
+ const lineNum = i + 1;
130
+ // Check block end first (before start/single so we don't match -start as single)
131
+ if (BLOCK_END_RE.test(line) || HTML_BLOCK_END_RE.test(line)) {
132
+ handleBlockEnd(openBlocks, exclusions, warnings, filePath, lineNum);
133
+ continue;
134
+ }
135
+ // Check block start
136
+ const startMatch = BLOCK_START_RE.exec(line) ?? HTML_BLOCK_START_RE.exec(line);
137
+ if (startMatch?.[1]) {
138
+ handleBlockStart(startMatch[1], openBlocks, warnings, filePath, lineNum);
139
+ continue;
140
+ }
141
+ // Check single-line exclude (must not match block directives)
142
+ const singleMatch = SINGLE_LINE_RE.exec(line) ?? HTML_SINGLE_RE.exec(line);
143
+ if (singleMatch?.[1]) {
144
+ handleSingleLine(singleMatch[1], exclusions, warnings, filePath, lineNum);
145
+ }
146
+ }
147
+ // Any unclosed blocks are errors
148
+ for (const [, comment] of openBlocks) {
149
+ warnings.push({
150
+ message: `eess-exclude-start without matching end for rule '${comment.ruleId}'`,
151
+ file: filePath,
152
+ line: comment.line,
153
+ });
154
+ }
155
+ return { exclusions, warnings };
156
+ }
157
+ /**
158
+ * Check if a violation is covered by an exclusion comment.
159
+ *
160
+ * For single-line comments: the violation must be in the same file and
161
+ * on the line immediately after the comment.
162
+ *
163
+ * For block comments: the violation must be in the same file and
164
+ * within the line range (start line, end line) inclusive.
165
+ */
166
+ /** Check if a single comment covers the given violation. */
167
+ function commentCoversViolation(comment, violationLine) {
168
+ if (comment.isBlock) {
169
+ return (comment.endLine !== undefined &&
170
+ violationLine >= comment.line &&
171
+ violationLine <= comment.endLine);
172
+ }
173
+ return violationLine === comment.line + 1;
174
+ }
175
+ export function isExcludedByComment(violation, comments) {
176
+ const ruleId = violation.ruleId;
177
+ if (!ruleId)
178
+ return false;
179
+ return comments.some((comment) => comment.ruleId === ruleId &&
180
+ comment.file === violation.file &&
181
+ commentCoversViolation(comment, violation.line));
182
+ }
183
+ //# sourceMappingURL=exclusion-comments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exclusion-comments.js","sourceRoot":"","sources":["../src/exclusion-comments.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { ArchViolation } from './violation.js';
2
+ import type { CheckOptions } from './check-options.js';
3
+ import type { RuleMetadata } from './rule-metadata.js';
4
+ /**
5
+ * Context for executing a rule's terminal methods.
6
+ * Shared across all builder types (RuleBuilder, SliceRuleBuilder,
7
+ * SchemaRuleBuilder, ResolverRuleBuilder, PairFinalBuilder, SmellBuilder).
8
+ */
9
+ export interface ExecuteRuleContext {
10
+ reason?: string;
11
+ metadata?: RuleMetadata;
12
+ exclusions?: (string | RegExp)[];
13
+ silentIndices?: Set<number>;
14
+ }
15
+ /**
16
+ * Apply exclusion patterns, inline exclusion comments, baseline,
17
+ * and diff filtering to a set of violations, then execute the
18
+ * terminal action (throw or warn).
19
+ *
20
+ * Extracted to eliminate terminal-method duplication across builders.
21
+ */
22
+ export declare function applyFilters(violations: ArchViolation[], ctx: ExecuteRuleContext): ArchViolation[];
23
+ /**
24
+ * Execute the terminal "check" action: apply options, format, throw on violations.
25
+ */
26
+ export declare function executeCheck(violations: ArchViolation[], ctx: ExecuteRuleContext, options?: CheckOptions): void;
27
+ /**
28
+ * Execute the terminal "warn" action: apply options, format, log to stderr.
29
+ */
30
+ export declare function executeWarn(violations: ArchViolation[], ctx: ExecuteRuleContext, options?: CheckOptions): void;
31
+ //# sourceMappingURL=execute-rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execute-rule.d.ts","sourceRoot":"","sources":["../src/execute-rule.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAQtD;;;;GAIG;AAEH,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,UAAU,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAA;IAChC,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,aAAa,EAAE,EAC3B,GAAG,EAAE,kBAAkB,GACtB,aAAa,EAAE,CAsEjB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,aAAa,EAAE,EAC3B,GAAG,EAAE,kBAAkB,EACvB,OAAO,CAAC,EAAE,YAAY,GACrB,IAAI,CAeN;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,aAAa,EAAE,EAC3B,GAAG,EAAE,kBAAkB,EACvB,OAAO,CAAC,EAAE,YAAY,GACrB,IAAI,CAmBN"}
@@ -0,0 +1,119 @@
1
+ import fs from 'node:fs';
2
+ import { ArchRuleError } from './errors.js';
3
+ import { formatViolations } from './format.js';
4
+ import { formatViolationsJson } from './format-json.js';
5
+ import { formatViolationsGitHub } from './format-github.js';
6
+ import { reportViolations } from './report.js';
7
+ import { parseExclusionComments, isExcludedByComment } from './exclusion-comments.js';
8
+ /**
9
+ * Apply exclusion patterns, inline exclusion comments, baseline,
10
+ * and diff filtering to a set of violations, then execute the
11
+ * terminal action (throw or warn).
12
+ *
13
+ * Extracted to eliminate terminal-method duplication across builders.
14
+ */
15
+ export function applyFilters(violations, ctx) {
16
+ let result = violations;
17
+ // Apply .excluding() chain exclusions
18
+ const exclusions = ctx.exclusions ?? [];
19
+ if (exclusions.length > 0) {
20
+ const matchedPatterns = new Set();
21
+ result = result.filter((v) => {
22
+ // Match against element, file, or message — so that custom conditions
23
+ // using createViolation() can be excluded by file path or message content,
24
+ // not just by element name (which may be a generic AST node kind).
25
+ const targets = [v.element, v.file, v.message];
26
+ const matchIndex = exclusions.findIndex((pattern) => typeof pattern === 'string'
27
+ ? targets.some((t) => t === pattern)
28
+ : targets.some((t) => pattern.test(t)));
29
+ if (matchIndex >= 0) {
30
+ matchedPatterns.add(matchIndex);
31
+ return false;
32
+ }
33
+ return true;
34
+ });
35
+ const ruleId = ctx.metadata?.id ?? 'unnamed';
36
+ const silentIndices = ctx.silentIndices ?? new Set();
37
+ exclusions.forEach((pattern, index) => {
38
+ if (!matchedPatterns.has(index) && !silentIndices.has(index)) {
39
+ console.warn(`[eess] Unused exclusion '${String(pattern)}' in rule '${ruleId}'. ` +
40
+ `It matched zero violations — it may be stale after a rename.`);
41
+ }
42
+ });
43
+ }
44
+ // Scan source files for inline exclusion comments (when rule has an ID)
45
+ if (ctx.metadata?.id && result.length > 0) {
46
+ // Tag violations with the rule id so inline exclusion comments (which match
47
+ // on ruleId) work for every condition — not just the few that stamp it
48
+ // themselves. The id is a property of the rule, so this is its single
49
+ // source of truth; conditions that already set it are left untouched.
50
+ const ruleId = ctx.metadata.id;
51
+ for (const v of result) {
52
+ if (v.ruleId === undefined)
53
+ v.ruleId = ruleId;
54
+ }
55
+ const filePaths = new Set(result.map((v) => v.file));
56
+ const allComments = [...filePaths].flatMap((filePath) => {
57
+ try {
58
+ const sourceText = fs.readFileSync(filePath, 'utf-8');
59
+ const parseResult = parseExclusionComments(sourceText, filePath);
60
+ for (const warning of parseResult.warnings) {
61
+ console.warn(`[eess] ${warning.message}`);
62
+ }
63
+ return parseResult.exclusions;
64
+ }
65
+ catch (err) {
66
+ // Deliberate discard: violation paths may be virtual (synthetic
67
+ // elements, fixtures) — an unreadable file simply has no exclusion
68
+ // comments; warning here would spam every synthetic-element rule.
69
+ void err;
70
+ return [];
71
+ }
72
+ });
73
+ if (allComments.length > 0) {
74
+ result = result.filter((v) => !isExcludedByComment(v, allComments));
75
+ }
76
+ }
77
+ return result;
78
+ }
79
+ /**
80
+ * Execute the terminal "check" action: apply options, format, throw on violations.
81
+ */
82
+ export function executeCheck(violations, ctx, options) {
83
+ let filtered = applyFilters(violations, ctx);
84
+ if (options?.baseline) {
85
+ filtered = options.baseline.filterNew(filtered);
86
+ }
87
+ if (options?.diff) {
88
+ filtered = options.diff.filterToChanged(filtered);
89
+ }
90
+ if (filtered.length > 0) {
91
+ // One emitter for both paths (plan 0070) — text/json/github, then throw.
92
+ reportViolations(filtered, { format: options?.format, reason: ctx.reason });
93
+ throw new ArchRuleError(filtered, ctx.reason);
94
+ }
95
+ }
96
+ /**
97
+ * Execute the terminal "warn" action: apply options, format, log to stderr.
98
+ */
99
+ export function executeWarn(violations, ctx, options) {
100
+ let filtered = applyFilters(violations, ctx);
101
+ if (options?.baseline) {
102
+ filtered = options.baseline.filterNew(filtered);
103
+ }
104
+ if (options?.diff) {
105
+ filtered = options.diff.filterToChanged(filtered);
106
+ }
107
+ if (filtered.length > 0) {
108
+ if (options?.format === 'json') {
109
+ console.warn(formatViolationsJson(filtered, ctx.reason));
110
+ }
111
+ else if (options?.format === 'github') {
112
+ process.stdout.write(formatViolationsGitHub(filtered, 'warning') + '\n');
113
+ }
114
+ else {
115
+ console.warn(formatViolations(filtered, ctx.reason));
116
+ }
117
+ }
118
+ }
119
+ //# sourceMappingURL=execute-rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execute-rule.js","sourceRoot":"","sources":["../src/execute-rule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { ArchViolation } from './violation.js';
2
+ /**
3
+ * Format violations as GitHub Actions annotation commands.
4
+ *
5
+ * Each violation becomes an `::error` or `::warning` command that GitHub
6
+ * renders as an inline annotation on the PR diff.
7
+ *
8
+ * File paths are converted to relative (GitHub needs relative paths from repo root).
9
+ *
10
+ * @param violations - The violations to format
11
+ * @param severity - 'error' for ::error, 'warning' for ::warning
12
+ *
13
+ * @example
14
+ * // In a GitHub Actions workflow step:
15
+ * // - run: npm run test 2>&1 | tee test-output.txt
16
+ * // Or directly from the test file:
17
+ * console.log(formatViolationsGitHub(violations))
18
+ */
19
+ export declare function formatViolationsGitHub(violations: ArchViolation[], severity?: 'error' | 'warning'): string;
20
+ /**
21
+ * Escape special characters for GitHub Actions commands.
22
+ * GitHub uses % encoding for newlines and other control chars.
23
+ */
24
+ export declare function escapeGitHub(text: string): string;
25
+ //# sourceMappingURL=format-github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-github.d.ts","sourceRoot":"","sources":["../src/format-github.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,aAAa,EAAE,EAC3B,QAAQ,GAAE,OAAO,GAAG,SAAmB,GACtC,MAAM,CAiBR;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD"}
@@ -0,0 +1,44 @@
1
+ import path from 'node:path';
2
+ /**
3
+ * Format violations as GitHub Actions annotation commands.
4
+ *
5
+ * Each violation becomes an `::error` or `::warning` command that GitHub
6
+ * renders as an inline annotation on the PR diff.
7
+ *
8
+ * File paths are converted to relative (GitHub needs relative paths from repo root).
9
+ *
10
+ * @param violations - The violations to format
11
+ * @param severity - 'error' for ::error, 'warning' for ::warning
12
+ *
13
+ * @example
14
+ * // In a GitHub Actions workflow step:
15
+ * // - run: npm run test 2>&1 | tee test-output.txt
16
+ * // Or directly from the test file:
17
+ * console.log(formatViolationsGitHub(violations))
18
+ */
19
+ export function formatViolationsGitHub(violations, severity = 'error') {
20
+ const cwd = process.cwd();
21
+ return violations
22
+ .map((v) => {
23
+ const relativePath = path.relative(cwd, v.file);
24
+ const title = v.ruleId
25
+ ? `Architecture Violation: ${v.ruleId}`
26
+ : `Architecture Violation: ${v.rule}`;
27
+ let message = v.because ? `${v.message} (${v.because})` : v.message;
28
+ if (v.suggestion)
29
+ message += `. Fix: ${v.suggestion}`;
30
+ if (v.docs)
31
+ message += `. Docs: ${v.docs}`;
32
+ // GitHub annotation format: ::level file=path,line=N,title=T::message
33
+ return `::${severity} file=${relativePath},line=${String(v.line)},title=${escapeGitHub(title)}::${escapeGitHub(message)}`;
34
+ })
35
+ .join('\n');
36
+ }
37
+ /**
38
+ * Escape special characters for GitHub Actions commands.
39
+ * GitHub uses % encoding for newlines and other control chars.
40
+ */
41
+ export function escapeGitHub(text) {
42
+ return text.replace(/%/g, '%25').replace(/\r/g, '%0D').replace(/\n/g, '%0A');
43
+ }
44
+ //# sourceMappingURL=format-github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-github.js","sourceRoot":"","sources":["../src/format-github.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAG5B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,sBAAsB,CACpC,UAA2B,EAC3B,WAAgC,OAAO;IAEvC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAA;IAEzB,OAAO,UAAU;SACd,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;QAC/C,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM;YACpB,CAAC,CAAC,2BAA2B,CAAC,CAAC,MAAM,EAAE;YACvC,CAAC,CAAC,2BAA2B,CAAC,CAAC,IAAI,EAAE,CAAA;QACvC,IAAI,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;QACnE,IAAI,CAAC,CAAC,UAAU;YAAE,OAAO,IAAI,UAAU,CAAC,CAAC,UAAU,EAAE,CAAA;QACrD,IAAI,CAAC,CAAC,IAAI;YAAE,OAAO,IAAI,WAAW,CAAC,CAAC,IAAI,EAAE,CAAA;QAE1C,sEAAsE;QACtE,OAAO,KAAK,QAAQ,SAAS,YAAY,SAAS,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,YAAY,CAAC,KAAK,CAAC,KAAK,YAAY,CAAC,OAAO,CAAC,EAAE,CAAA;IAC3H,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAA;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;AAC9E,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { ArchViolation } from './violation.js';
2
+ /**
3
+ * Format violations as a JSON string.
4
+ *
5
+ * Useful for CI pipelines, custom dashboards, or piping to other tools.
6
+ *
7
+ * @example
8
+ * const violations = collectViolations(rule1, rule2)
9
+ * console.log(formatViolationsJson(violations))
10
+ */
11
+ export declare function formatViolationsJson(violations: ArchViolation[], reason?: string): string;
12
+ //# sourceMappingURL=format-json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-json.d.ts","sourceRoot":"","sources":["../src/format-json.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,aAAa,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAmBzF"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Format violations as a JSON string.
3
+ *
4
+ * Useful for CI pipelines, custom dashboards, or piping to other tools.
5
+ *
6
+ * @example
7
+ * const violations = collectViolations(rule1, rule2)
8
+ * console.log(formatViolationsJson(violations))
9
+ */
10
+ export function formatViolationsJson(violations, reason) {
11
+ const output = {
12
+ summary: {
13
+ total: violations.length,
14
+ reason: reason ?? null,
15
+ },
16
+ violations: violations.map((v) => ({
17
+ rule: v.rule,
18
+ ruleId: v.ruleId ?? null,
19
+ element: v.element,
20
+ file: v.file,
21
+ line: v.line,
22
+ message: v.message,
23
+ because: v.because ?? null,
24
+ suggestion: v.suggestion ?? null,
25
+ docs: v.docs ?? null,
26
+ })),
27
+ };
28
+ return JSON.stringify(output, null, 2);
29
+ }
30
+ //# sourceMappingURL=format-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-json.js","sourceRoot":"","sources":["../src/format-json.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAA2B,EAAE,MAAe;IAC/E,MAAM,MAAM,GAAG;QACb,OAAO,EAAE;YACP,KAAK,EAAE,UAAU,CAAC,MAAM;YACxB,MAAM,EAAE,MAAM,IAAI,IAAI;SACvB;QACD,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACjC,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,IAAI;YACxB,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,IAAI;YAC1B,UAAU,EAAE,CAAC,CAAC,UAAU,IAAI,IAAI;YAChC,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI;SACrB,CAAC,CAAC;KACJ,CAAA;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;AACxC,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { ArchViolation } from './violation.js';
2
+ /**
3
+ * Format options for violation output.
4
+ */
5
+ export interface FormatOptions {
6
+ /** Working directory for relative path display. Default: process.cwd() */
7
+ cwd?: string;
8
+ /** Whether to include code frames in output. Default: true */
9
+ codeFrames?: boolean;
10
+ }
11
+ /**
12
+ * Format violations into a rich, readable terminal string.
13
+ *
14
+ * Groups violations by rule, shows a counter ("Architecture Violation [1 of 3]"),
15
+ * displays code frames and suggestions, and uses ANSI colors for emphasis.
16
+ */
17
+ export declare function formatViolations(violations: ArchViolation[], reason?: string, options?: FormatOptions): string;
18
+ /**
19
+ * Format violations into a plain-text string (no ANSI codes).
20
+ *
21
+ * Used by ArchRuleError.message — error messages should be plain text
22
+ * since they may be captured by test runners, serialized, or logged to files.
23
+ */
24
+ export declare function formatViolationsPlain(violations: ArchViolation[], reason?: string): string;
25
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAInD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,8DAA8D;IAC9D,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAgCD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,aAAa,EAAE,EAC3B,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,aAAa,GACtB,MAAM,CAYR;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,aAAa,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAkB1F"}
package/dist/format.js ADDED
@@ -0,0 +1,66 @@
1
+ import { bold, red, cyan, dim } from './ansi.js';
2
+ import path from 'node:path';
3
+ /** Format a single violation into a rich terminal section. */
4
+ function formatSingleViolation(v, index, total, cwd, showCodeFrames, reason) {
5
+ const counter = bold(red(`Architecture Violation [${String(index + 1)} of ${String(total)}]`));
6
+ const ruleLine = ` ${dim('Rule:')} ${v.rule}`;
7
+ const relativePath = path.relative(cwd, v.file);
8
+ const locationRef = cyan(relativePath + ':' + String(v.line));
9
+ const location = ` ${locationRef} ${dim('—')} ${v.element}`;
10
+ const codeLine = showCodeFrames && v.codeFrame ? `\n${v.codeFrame}` : '';
11
+ const whyText = v.because ?? reason;
12
+ const whyLine = whyText ? ` ${dim('Why:')} ${whyText}` : '';
13
+ const fixLine = v.suggestion ? ` ${dim('Fix:')} ${v.suggestion}` : '';
14
+ const docsLine = v.docs ? ` ${dim('Docs:')} ${v.docs}` : '';
15
+ const parts = [counter, '', ruleLine, '', location];
16
+ if (codeLine)
17
+ parts.push(codeLine);
18
+ if (whyLine)
19
+ parts.push(whyLine);
20
+ if (fixLine)
21
+ parts.push(fixLine);
22
+ if (docsLine)
23
+ parts.push(docsLine);
24
+ return parts.join('\n');
25
+ }
26
+ /**
27
+ * Format violations into a rich, readable terminal string.
28
+ *
29
+ * Groups violations by rule, shows a counter ("Architecture Violation [1 of 3]"),
30
+ * displays code frames and suggestions, and uses ANSI colors for emphasis.
31
+ */
32
+ export function formatViolations(violations, reason, options) {
33
+ if (violations.length === 0)
34
+ return '';
35
+ const cwd = options?.cwd ?? process.cwd();
36
+ const showCodeFrames = options?.codeFrames ?? true;
37
+ const total = violations.length;
38
+ const sections = violations.map((v, i) => formatSingleViolation(v, i, total, cwd, showCodeFrames, reason));
39
+ return sections.join('\n\n');
40
+ }
41
+ /**
42
+ * Format violations into a plain-text string (no ANSI codes).
43
+ *
44
+ * Used by ArchRuleError.message — error messages should be plain text
45
+ * since they may be captured by test runners, serialized, or logged to files.
46
+ */
47
+ export function formatViolationsPlain(violations, reason) {
48
+ if (violations.length === 0)
49
+ return '';
50
+ const header = `Architecture violation${violations.length === 1 ? '' : 's'} (${String(violations.length)} found)`;
51
+ const reasonLine = reason ? `\nReason: ${reason}` : '';
52
+ const details = violations
53
+ .map((v, i) => {
54
+ const parts = [
55
+ ` [${String(i + 1)}/${String(violations.length)}] ${v.element}: ${v.message} (${v.file}:${String(v.line)})`,
56
+ ];
57
+ if (v.codeFrame)
58
+ parts.push(v.codeFrame);
59
+ if (v.suggestion)
60
+ parts.push(` Fix: ${v.suggestion}`);
61
+ return parts.join('\n');
62
+ })
63
+ .join('\n\n');
64
+ return `${header}${reasonLine}\n\n${details}`;
65
+ }
66
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,IAAI,MAAM,WAAW,CAAA;AAY5B,8DAA8D;AAC9D,SAAS,qBAAqB,CAC5B,CAAgB,EAChB,KAAa,EACb,KAAa,EACb,GAAW,EACX,cAAuB,EACvB,MAA0B;IAE1B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,2BAA2B,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;IAC9F,MAAM,QAAQ,GAAG,KAAK,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAA;IAC9C,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;IAC/C,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,GAAG,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;IAC7D,MAAM,QAAQ,GAAG,KAAK,WAAW,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAA;IAC5D,MAAM,QAAQ,GAAG,cAAc,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAExE,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI,MAAM,CAAA;IACnC,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAC5D,MAAM,OAAO,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IACtE,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAE5D,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAA;IACnD,IAAI,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAClC,IAAI,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAChC,IAAI,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAChC,IAAI,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAElC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAA2B,EAC3B,MAAe,EACf,OAAuB;IAEvB,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IAEtC,MAAM,GAAG,GAAG,OAAO,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAA;IACzC,MAAM,cAAc,GAAG,OAAO,EAAE,UAAU,IAAI,IAAI,CAAA;IAClD,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAA;IAE/B,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACvC,qBAAqB,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,CAAC,CAChE,CAAA;IAED,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;AAC9B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,UAA2B,EAAE,MAAe;IAChF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IAEtC,MAAM,MAAM,GAAG,yBAAyB,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAA;IACjH,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,aAAa,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAEtD,MAAM,OAAO,GAAG,UAAU;SACvB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACZ,MAAM,KAAK,GAAG;YACZ,MAAM,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG;SAC7G,CAAA;QACD,IAAI,CAAC,CAAC,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;QACxC,IAAI,CAAC,CAAC,UAAU;YAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,UAAU,EAAE,CAAC,CAAA;QACtD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC,CAAC;SACD,IAAI,CAAC,MAAM,CAAC,CAAA;IAEf,OAAO,GAAG,MAAM,GAAG,UAAU,OAAO,OAAO,EAAE,CAAA;AAC/C,CAAC"}