@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,323 @@
1
+ import { isSilent } from './silent-exclusion.js';
2
+ import { executeCheck, executeWarn, applyFilters } from './execute-rule.js';
3
+ /**
4
+ * Abstract base class for all rule builders.
5
+ *
6
+ * Concrete entry points (plans 0007+) extend this and:
7
+ * 1. Implement `getElements()` to return the elements to check
8
+ * 2. Add predicate methods that call `addPredicate()`
9
+ * 3. Add condition methods that call `addCondition()`
10
+ *
11
+ * The builder accumulates predicates and conditions. Nothing executes
12
+ * until a terminal method (`.check()`, `.warn()`, `.severity()`) is called.
13
+ */
14
+ export class RuleBuilder {
15
+ project;
16
+ _predicates = [];
17
+ _conditions = [];
18
+ _reason;
19
+ _metadata;
20
+ _exclusions = [];
21
+ _silentIndices = new Set();
22
+ _phase = 'predicate';
23
+ constructor(project) {
24
+ this.project = project;
25
+ }
26
+ // --- Chain methods (grammar transitions) ---
27
+ /**
28
+ * Begin the predicate phase. Returns `this` for chaining.
29
+ * Purely a readability marker — `.that().haveNameMatching(...)` reads like English.
30
+ * Explicitly resets phase to 'predicate' — defensive against `.should().that()` misuse.
31
+ */
32
+ that() {
33
+ this._phase = 'predicate';
34
+ return this;
35
+ }
36
+ /**
37
+ * Add another predicate (AND). Returns `this` for chaining.
38
+ * `.that().extend('Base').and().resideInFolder('src/repos/**')` means both must match.
39
+ */
40
+ and() {
41
+ return this;
42
+ }
43
+ /**
44
+ * Begin the condition phase. Returns a forked builder for named selection safety.
45
+ * Creates a fresh builder with the same predicates but empty conditions.
46
+ * Sets phase to 'condition' so dual-use methods dispatch correctly.
47
+ */
48
+ should() {
49
+ const fork = this.fork();
50
+ fork._phase = 'condition';
51
+ return fork;
52
+ }
53
+ /**
54
+ * Add another condition that must ALSO pass (AND).
55
+ * `.should().notContain(call('x')).andShould().notContain(call('y'))` means both must hold.
56
+ */
57
+ andShould() {
58
+ return this;
59
+ }
60
+ /**
61
+ * Plug in a custom predicate or condition.
62
+ *
63
+ * After `.that()` — pass a `Predicate<T>` to filter elements.
64
+ * After `.should()` — pass a `Condition<T>` to assert against filtered elements.
65
+ *
66
+ * Dispatch is structural: if the object has a `test` method it is treated
67
+ * as a predicate; if it has `evaluate` it is treated as a condition.
68
+ */
69
+ satisfy(custom) {
70
+ if ('test' in custom) {
71
+ return this.addPredicate(custom);
72
+ }
73
+ return this.addCondition(custom);
74
+ }
75
+ /**
76
+ * Attach a human-readable rationale to the rule.
77
+ * Included in violation messages when `.check()` throws.
78
+ */
79
+ because(reason) {
80
+ this._reason = reason;
81
+ return this;
82
+ }
83
+ /**
84
+ * Attach rich metadata to the rule.
85
+ * Provides educational context in violation output: why, how to fix, docs link.
86
+ *
87
+ * If `metadata.because` is set, it also sets the reason (same as `.because()`).
88
+ */
89
+ rule(metadata) {
90
+ this._metadata = metadata;
91
+ if (metadata.because) {
92
+ this._reason = metadata.because;
93
+ }
94
+ return this;
95
+ }
96
+ /**
97
+ * Exclude specific violations from reporting by matching against
98
+ * the violation's `element`, `file`, or `message` fields.
99
+ *
100
+ * Matched violations are silently suppressed. Use for permanent,
101
+ * intentional exceptions — not for temporary violations (use baseline for those).
102
+ *
103
+ * Patterns are matched against all three fields. Prefer anchored regexes
104
+ * or full string matches over short substrings, especially for `message`
105
+ * matching, to avoid accidentally suppressing unrelated violations whose
106
+ * messages happen to contain the same text.
107
+ *
108
+ * Emits a warning if an exclusion matches zero violations — so renamed
109
+ * or deleted exceptions don't silently stay in the rule.
110
+ *
111
+ * For narrowing a rule's scope at the predicate phase (so the rule never
112
+ * evaluates the excluded element), use `satisfy(not(<predicate>))` instead.
113
+ * See the "Excluding a file from a rule's scope" recipe in docs/recipes.md.
114
+ *
115
+ * @example
116
+ * // Exclude by element name (fully qualified)
117
+ * .excluding('Asset.getImageUrl')
118
+ *
119
+ * @example
120
+ * // Exclude by file path (regex anchored to suffix)
121
+ * .excluding(/repositories\/index\.ts$/)
122
+ *
123
+ * @example
124
+ * // Multiple exclusions, mixed forms
125
+ * .excluding('Asset.getImageUrl', /\/legacy\//, /generated/)
126
+ */
127
+ excluding(...patterns) {
128
+ for (const p of patterns) {
129
+ if (isSilent(p)) {
130
+ this._exclusions.push(p.pattern);
131
+ this._silentIndices.add(this._exclusions.length - 1);
132
+ }
133
+ else {
134
+ this._exclusions.push(p);
135
+ }
136
+ }
137
+ return this;
138
+ }
139
+ // --- Terminal methods ---
140
+ /**
141
+ * Return the predicate-filtered elements as a labelled `Selection<T>` for
142
+ * cross-validation (`correspondence()`). The set is exactly the elements this
143
+ * rule would evaluate — the same filtering `.check()` applies.
144
+ *
145
+ * @param opts.label - human-readable name of this side ("diagram class")
146
+ * @param opts.identify - map an element to its message metadata (name/file/line)
147
+ */
148
+ select(opts) {
149
+ const filtered = this.getElements().filter((element) => this._predicates.every((predicate) => predicate.test(element)));
150
+ return { elements: filtered, label: opts.label, identify: opts.identify };
151
+ }
152
+ /**
153
+ * Return a structured description of this rule without executing it.
154
+ * Used by the `explain` CLI subcommand.
155
+ */
156
+ describeRule() {
157
+ return {
158
+ rule: this.buildRuleDescription(),
159
+ id: this._metadata?.id,
160
+ because: this._reason,
161
+ suggestion: this._metadata?.suggestion,
162
+ docs: this._metadata?.docs,
163
+ };
164
+ }
165
+ /**
166
+ * Execute the rule and return violations after exclusion filtering.
167
+ * Does not throw — use for programmatic access (presets, aggregation).
168
+ */
169
+ violations() {
170
+ const raw = this.evaluate();
171
+ return applyFilters(raw, {
172
+ reason: this._reason,
173
+ metadata: this._metadata,
174
+ exclusions: this._exclusions,
175
+ silentIndices: this._silentIndices,
176
+ });
177
+ }
178
+ /**
179
+ * Execute the rule and throw `ArchRuleError` if any violations are found.
180
+ * This is the primary terminal method — use in test assertions.
181
+ *
182
+ * @param options - Optional baseline and diff filtering
183
+ */
184
+ check(options) {
185
+ const violations = this.evaluate();
186
+ executeCheck(violations, {
187
+ reason: this._reason,
188
+ metadata: this._metadata,
189
+ exclusions: this._exclusions,
190
+ silentIndices: this._silentIndices,
191
+ }, options);
192
+ }
193
+ /**
194
+ * Execute the rule and log violations to stderr. Does not throw.
195
+ * Use for rules that should warn but not fail CI.
196
+ *
197
+ * @param options - Optional baseline and diff filtering
198
+ */
199
+ warn(options) {
200
+ const violations = this.evaluate();
201
+ executeWarn(violations, {
202
+ reason: this._reason,
203
+ metadata: this._metadata,
204
+ exclusions: this._exclusions,
205
+ silentIndices: this._silentIndices,
206
+ }, options);
207
+ }
208
+ /**
209
+ * Execute the rule with the given severity.
210
+ * `.severity('error')` is equivalent to `.check()`.
211
+ * `.severity('warn')` is equivalent to `.warn()`.
212
+ */
213
+ severity(level) {
214
+ if (level === 'error') {
215
+ this.check();
216
+ }
217
+ else {
218
+ this.warn();
219
+ }
220
+ }
221
+ // --- Protected: for subclasses ---
222
+ /**
223
+ * Register a predicate. Called by concrete builder methods like
224
+ * `.haveNameMatching()`, `.extend()`, etc.
225
+ */
226
+ addPredicate(predicate) {
227
+ this._predicates.push(predicate);
228
+ return this;
229
+ }
230
+ /**
231
+ * Register a condition. Called by concrete builder methods like
232
+ * `.notContain()`, `.notExist()`, etc.
233
+ */
234
+ addCondition(condition) {
235
+ this._conditions.push(condition);
236
+ return this;
237
+ }
238
+ /**
239
+ * Create a fork of this builder with the same predicates but empty conditions.
240
+ * Used by `.should()` to support named selections without mutation.
241
+ *
242
+ * Subclasses with additional constructor args MUST override this method.
243
+ */
244
+ fork() {
245
+ // Object.create/getPrototypeOf return untyped — casts unavoidable at JS interop boundary
246
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
247
+ const proto = Object.getPrototypeOf(this);
248
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
249
+ const fork = Object.create(proto);
250
+ Object.assign(fork, this);
251
+ fork._predicates = [...this._predicates];
252
+ fork._conditions = [];
253
+ fork._exclusions = [...this._exclusions];
254
+ fork._silentIndices = new Set(this._silentIndices);
255
+ fork._metadata = this._metadata ? { ...this._metadata } : undefined;
256
+ fork._reason = fork._metadata?.because ?? this._reason;
257
+ return fork;
258
+ }
259
+ // --- Private: execution engine ---
260
+ /**
261
+ * Build the rule description from predicates and conditions.
262
+ */
263
+ buildRuleDescription() {
264
+ const predicateDesc = this._predicates.map((p) => p.description).join(' and ');
265
+ const conditionDesc = this._conditions.map((c) => c.description).join(' and ');
266
+ const parts = [];
267
+ if (predicateDesc)
268
+ parts.push(`that ${predicateDesc}`);
269
+ if (conditionDesc)
270
+ parts.push(`should ${conditionDesc}`);
271
+ return parts.join(' ');
272
+ }
273
+ /**
274
+ * Execute the full pipeline: filter elements with predicates,
275
+ * evaluate conditions, return violations.
276
+ */
277
+ evaluate() {
278
+ // Step 1: Get all elements from the concrete builder
279
+ const allElements = this.getElements();
280
+ // Step 2: Filter with predicates (AND — all predicates must match)
281
+ const filtered = allElements.filter((element) => this._predicates.every((predicate) => predicate.test(element)));
282
+ // Step 3: If no elements match predicates, no violations
283
+ if (filtered.length === 0) {
284
+ return [];
285
+ }
286
+ // Step 3b: Warn if no conditions were added and phase is still 'predicate'
287
+ // — likely a predicate-only method was called after .should().
288
+ // Phase-aware methods dispatch correctly, so this only fires for predicate-only methods.
289
+ if (this._conditions.length === 0 && this._phase === 'predicate') {
290
+ const ruleId = this._metadata?.id ?? (this.buildRuleDescription() || 'unnamed');
291
+ console.warn(`[eess] Rule '${ruleId}' has predicates but no conditions. ` +
292
+ `Did you use a predicate-only method after .should()? ` +
293
+ `Predicate-only methods (e.g. areExported, areAsync) filter elements; ` +
294
+ `use a condition method or .satisfy() after .should().`);
295
+ return [];
296
+ }
297
+ // Step 4: Build context for conditions
298
+ const context = this.buildConditionContext();
299
+ // Step 5: Evaluate all conditions (AND — all must pass)
300
+ const violations = [];
301
+ for (const condition of this._conditions) {
302
+ violations.push(...condition.evaluate(filtered, context));
303
+ }
304
+ return violations;
305
+ }
306
+ /**
307
+ * Build the `ConditionContext` passed to each condition.
308
+ *
309
+ * Subclasses with builder-specific context fields (e.g. `CallRuleBuilder`'s
310
+ * `_identifyByArgument`) override this to extend the base context.
311
+ * Call `super.buildConditionContext()` and spread the result.
312
+ */
313
+ buildConditionContext() {
314
+ return {
315
+ rule: this.buildRuleDescription(),
316
+ because: this._reason,
317
+ ruleId: this._metadata?.id,
318
+ suggestion: this._metadata?.suggestion,
319
+ docs: this._metadata?.docs,
320
+ };
321
+ }
322
+ }
323
+ //# sourceMappingURL=rule-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-builder.js","sourceRoot":"","sources":["../src/rule-builder.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAG3E;;;;;;;;;;GAUG;AACH,MAAM,OAAgB,WAAW;IASA;IARrB,WAAW,GAAmB,EAAE,CAAA;IAChC,WAAW,GAAmB,EAAE,CAAA;IAChC,OAAO,CAAS;IAChB,SAAS,CAAe;IACxB,WAAW,GAAwB,EAAE,CAAA;IACrC,cAAc,GAAgB,IAAI,GAAG,EAAE,CAAA;IACvC,MAAM,GAA8B,WAAW,CAAA;IAEzD,YAA+B,OAAU;QAAV,YAAO,GAAP,OAAO,CAAG;IAAG,CAAC;IAE7C,8CAA8C;IAE9C;;;;OAIG;IACH,IAAI;QACF,IAAI,CAAC,MAAM,GAAG,WAAW,CAAA;QACzB,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;OAGG;IACH,GAAG;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;;OAIG;IACH,MAAM;QACJ,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QACxB,IAAI,CAAC,MAAM,GAAG,WAAW,CAAA;QACzB,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;OAGG;IACH,SAAS;QACP,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;;;;;;OAQG;IACH,OAAO,CAAC,MAAmC;QACzC,IAAI,MAAM,IAAI,MAAM,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;QAClC,CAAC;QACD,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;IAClC,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,MAAc;QACpB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAA;QACrB,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;;;OAKG;IACH,IAAI,CAAC,QAAsB;QACzB,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAA;QACzB,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACrB,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAA;QACjC,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,SAAS,CAAC,GAAG,QAA+C;QAC1D,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;gBAChC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;YACtD,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YAC1B,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,2BAA2B;IAE3B;;;;;;;OAOG;IACH,MAAM,CAAC,IAA8D;QACnE,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CACrD,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAC/D,CAAA;QACD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAA;IAC3E,CAAC;IAED;;;OAGG;IACH,YAAY;QACV,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,oBAAoB,EAAE;YACjC,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE;YACtB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,UAAU,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU;YACtC,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI;SAC3B,CAAA;IACH,CAAC;IAED;;;OAGG;IACH,UAAU;QACR,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAA;QAC3B,OAAO,YAAY,CAAC,GAAG,EAAE;YACvB,MAAM,EAAE,IAAI,CAAC,OAAO;YACpB,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,UAAU,EAAE,IAAI,CAAC,WAAW;YAC5B,aAAa,EAAE,IAAI,CAAC,cAAc;SACnC,CAAC,CAAA;IACJ,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,OAAsB;QAC1B,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAA;QAClC,YAAY,CACV,UAAU,EACV;YACE,MAAM,EAAE,IAAI,CAAC,OAAO;YACpB,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,UAAU,EAAE,IAAI,CAAC,WAAW;YAC5B,aAAa,EAAE,IAAI,CAAC,cAAc;SACnC,EACD,OAAO,CACR,CAAA;IACH,CAAC;IAED;;;;;OAKG;IACH,IAAI,CAAC,OAAsB;QACzB,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAA;QAClC,WAAW,CACT,UAAU,EACV;YACE,MAAM,EAAE,IAAI,CAAC,OAAO;YACpB,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,UAAU,EAAE,IAAI,CAAC,WAAW;YAC5B,aAAa,EAAE,IAAI,CAAC,cAAc;SACnC,EACD,OAAO,CACR,CAAA;IACH,CAAC;IAED;;;;OAIG;IACH,QAAQ,CAAC,KAAuB;QAC9B,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;YACtB,IAAI,CAAC,KAAK,EAAE,CAAA;QACd,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,EAAE,CAAA;QACb,CAAC;IACH,CAAC;IAED,oCAAoC;IAEpC;;;OAGG;IACO,YAAY,CAAC,SAAuB;QAC5C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAChC,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;OAGG;IACO,YAAY,CAAC,SAAuB;QAC5C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAChC,OAAO,IAAI,CAAA;IACb,CAAC;IAQD;;;;;OAKG;IACO,IAAI;QACZ,yFAAyF;QACzF,mEAAmE;QACnE,MAAM,KAAK,GAAW,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAA;QACjD,mEAAmE;QACnE,MAAM,IAAI,GAAS,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACvC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QACzB,IAAI,CAAC,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,CAAA;QACxC,IAAI,CAAC,WAAW,GAAG,EAAE,CAAA;QACrB,IAAI,CAAC,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,CAAA;QACxC,IAAI,CAAC,cAAc,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;QAClD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;QACnE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,IAAI,IAAI,CAAC,OAAO,CAAA;QACtD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,oCAAoC;IAEpC;;OAEG;IACK,oBAAoB;QAC1B,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC9E,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC9E,MAAM,KAAK,GAAa,EAAE,CAAA;QAC1B,IAAI,aAAa;YAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,aAAa,EAAE,CAAC,CAAA;QACtD,IAAI,aAAa;YAAE,KAAK,CAAC,IAAI,CAAC,UAAU,aAAa,EAAE,CAAC,CAAA;QACxD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACxB,CAAC;IAED;;;OAGG;IACK,QAAQ;QACd,qDAAqD;QACrD,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;QAEtC,mEAAmE;QACnE,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAC9C,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAC/D,CAAA;QAED,yDAAyD;QACzD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,EAAE,CAAA;QACX,CAAC;QAED,2EAA2E;QAC3E,+DAA+D;QAC/D,yFAAyF;QACzF,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YACjE,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,IAAI,SAAS,CAAC,CAAA;YAC/E,OAAO,CAAC,IAAI,CACV,gBAAgB,MAAM,sCAAsC;gBAC1D,uDAAuD;gBACvD,uEAAuE;gBACvE,uDAAuD,CAC1D,CAAA;YACD,OAAO,EAAE,CAAA;QACX,CAAC;QAED,uCAAuC;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,qBAAqB,EAAE,CAAA;QAE5C,wDAAwD;QACxD,MAAM,UAAU,GAAoB,EAAE,CAAA;QACtC,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACzC,UAAU,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAA;QAC3D,CAAC;QAED,OAAO,UAAU,CAAA;IACnB,CAAC;IAED;;;;;;OAMG;IACO,qBAAqB;QAC7B,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,oBAAoB,EAAE;YACjC,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE;YAC1B,UAAU,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU;YACtC,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI;SAC3B,CAAA;IACH,CAAC;CACF"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Structured description of a rule, returned by `.describe()`.
3
+ * Used by the `explain` CLI subcommand to dump active rules as JSON.
4
+ */
5
+ export interface RuleDescription {
6
+ rule: string;
7
+ id?: string;
8
+ because?: string;
9
+ suggestion?: string;
10
+ docs?: string;
11
+ }
12
+ //# sourceMappingURL=rule-description.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-description.d.ts","sourceRoot":"","sources":["../src/rule-description.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rule-description.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-description.js","sourceRoot":"","sources":["../src/rule-description.ts"],"names":[],"mappings":""}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Rich metadata for an architecture rule.
3
+ *
4
+ * Provides educational context beyond the violation message:
5
+ * why the rule exists, how to fix violations, where to learn more.
6
+ */
7
+ export interface RuleMetadata {
8
+ /** Unique rule identifier, e.g. 'repo/typed-errors' */
9
+ id?: string;
10
+ /** Why this rule exists — the risk or impact */
11
+ because?: string;
12
+ /** How to fix — actionable suggestion with code example */
13
+ suggestion?: string;
14
+ /** Link to documentation — ADR, wiki, style guide */
15
+ docs?: string;
16
+ }
17
+ //# sourceMappingURL=rule-metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-metadata.d.ts","sourceRoot":"","sources":["../src/rule-metadata.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,uDAAuD;IACvD,EAAE,CAAC,EAAE,MAAM,CAAA;IAEX,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAA;IAEhB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAA;CACd"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rule-metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-metadata.js","sourceRoot":"","sources":["../src/rule-metadata.ts"],"names":[],"mappings":""}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Symbol used to tag silent exclusion patterns.
3
+ * Not exported — only isSilent() should inspect this.
4
+ */
5
+ declare const SILENT: unique symbol;
6
+ /**
7
+ * A wrapped exclusion pattern that suppresses the "unused exclusion" warning.
8
+ *
9
+ * Use `silent()` to wrap patterns that intentionally match zero violations in
10
+ * some configurations — for example, shared exclusions across monorepo workspaces
11
+ * where not every workspace triggers every pattern.
12
+ */
13
+ export interface SilentExclusion {
14
+ readonly pattern: string | RegExp;
15
+ readonly [SILENT]: true;
16
+ }
17
+ /**
18
+ * Wrap an exclusion pattern to suppress the "unused exclusion" warning.
19
+ *
20
+ * By default, `.excluding()` warns when a pattern matches zero violations
21
+ * (stale-exclusion detection). Use `silent()` for patterns that are
22
+ * intentionally broad and may legitimately match nothing in some contexts.
23
+ *
24
+ * @example
25
+ * import { silent } from '\@nielspeter/eess-ts'
26
+ *
27
+ * modules(p)
28
+ * .should().satisfy(noDeadModules())
29
+ * .excluding(silent(/\.d\.ts$/), 'index.ts')
30
+ * .check()
31
+ */
32
+ export declare function silent(pattern: string | RegExp): SilentExclusion;
33
+ /**
34
+ * Check whether a value is a SilentExclusion.
35
+ */
36
+ export declare function isSilent(value: unknown): value is SilentExclusion;
37
+ export {};
38
+ //# sourceMappingURL=silent-exclusion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"silent-exclusion.d.ts","sourceRoot":"","sources":["../src/silent-exclusion.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,QAAA,MAAM,MAAM,EAAE,OAAO,MAAmC,CAAA;AAExD;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;IACjC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,CAAA;CACxB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,eAAe,CAEhE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAEjE"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Symbol used to tag silent exclusion patterns.
3
+ * Not exported — only isSilent() should inspect this.
4
+ */
5
+ const SILENT = Symbol('silent-exclusion');
6
+ /**
7
+ * Wrap an exclusion pattern to suppress the "unused exclusion" warning.
8
+ *
9
+ * By default, `.excluding()` warns when a pattern matches zero violations
10
+ * (stale-exclusion detection). Use `silent()` for patterns that are
11
+ * intentionally broad and may legitimately match nothing in some contexts.
12
+ *
13
+ * @example
14
+ * import { silent } from '\@nielspeter/eess-ts'
15
+ *
16
+ * modules(p)
17
+ * .should().satisfy(noDeadModules())
18
+ * .excluding(silent(/\.d\.ts$/), 'index.ts')
19
+ * .check()
20
+ */
21
+ export function silent(pattern) {
22
+ return { pattern, [SILENT]: true };
23
+ }
24
+ /**
25
+ * Check whether a value is a SilentExclusion.
26
+ */
27
+ export function isSilent(value) {
28
+ return typeof value === 'object' && value !== null && SILENT in value;
29
+ }
30
+ //# sourceMappingURL=silent-exclusion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"silent-exclusion.js","sourceRoot":"","sources":["../src/silent-exclusion.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,GAAkB,MAAM,CAAC,kBAAkB,CAAC,CAAA;AAcxD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,MAAM,CAAC,OAAwB;IAC7C,OAAO,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAA;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,CAAA;AACvE,CAAC"}
@@ -0,0 +1,83 @@
1
+ import type { ArchViolation } from './violation.js';
2
+ import type { CheckOptions } from './check-options.js';
3
+ import type { RuleMetadata } from './rule-metadata.js';
4
+ import type { RuleDescription } from './rule-description.js';
5
+ import type { SilentExclusion } from './silent-exclusion.js';
6
+ /**
7
+ * Abstract base class for builders that share the terminal method pattern
8
+ * (because, rule, excluding, check, warn, severity) but have different
9
+ * element collection and evaluation models.
10
+ *
11
+ * Used by SliceRuleBuilder, SchemaRuleBuilder, ResolverRuleBuilder,
12
+ * PairFinalBuilder, and SmellBuilder — all of which implement the same
13
+ * terminal methods but differ in how they collect and evaluate elements.
14
+ *
15
+ * RuleBuilder<T> does NOT extend this because it predates it and has
16
+ * additional concerns (predicate/condition pipeline, fork semantics).
17
+ */
18
+ export declare abstract class TerminalBuilder {
19
+ protected _reason?: string;
20
+ protected _metadata?: RuleMetadata;
21
+ private readonly _exclusions;
22
+ private readonly _silentIndices;
23
+ /**
24
+ * Attach a human-readable rationale to the rule.
25
+ * Included in violation messages when `.check()` throws.
26
+ */
27
+ because(reason: string): this;
28
+ /**
29
+ * Attach rich metadata to the rule.
30
+ * Provides educational context in violation output: why, how to fix, docs link.
31
+ *
32
+ * If `metadata.because` is set, it also sets the reason (same as `.because()`).
33
+ */
34
+ rule(metadata: RuleMetadata): this;
35
+ /**
36
+ * Exclude specific elements from violation reporting.
37
+ *
38
+ * Matched violations are silently suppressed. Use for permanent,
39
+ * intentional exceptions — not for temporary violations (use baseline for those).
40
+ *
41
+ * Matches against the violation's `element` field.
42
+ * Supports exact strings and regex patterns.
43
+ *
44
+ * Emits a warning if an exclusion matches zero violations (stale exclusion).
45
+ */
46
+ excluding(...patterns: (string | RegExp | SilentExclusion)[]): this;
47
+ /**
48
+ * Return a structured description of this rule without executing it.
49
+ * Used by the `explain` CLI subcommand.
50
+ */
51
+ describeRule(): RuleDescription;
52
+ /**
53
+ * Execute the rule and return violations after exclusion filtering.
54
+ * Does not throw — use for programmatic access (presets, aggregation).
55
+ */
56
+ violations(): ArchViolation[];
57
+ /**
58
+ * Execute the rule and throw `ArchRuleError` if any violations are found.
59
+ * This is the primary terminal method — use in test assertions.
60
+ *
61
+ * @param options - Optional baseline, diff filtering, and output format
62
+ */
63
+ check(options?: CheckOptions): void;
64
+ /**
65
+ * Execute the rule and log violations to stderr. Does not throw.
66
+ * Use for rules that should warn but not fail CI.
67
+ *
68
+ * @param options - Optional baseline, diff filtering, and output format
69
+ */
70
+ warn(options?: CheckOptions): void;
71
+ /**
72
+ * Execute the rule with the given severity.
73
+ * `.severity('error')` is equivalent to `.check()`.
74
+ * `.severity('warn')` is equivalent to `.warn()`.
75
+ */
76
+ severity(level: 'error' | 'warn'): void;
77
+ /**
78
+ * Subclasses implement this to collect and evaluate violations.
79
+ * Called lazily during `.check()` / `.warn()`.
80
+ */
81
+ protected abstract collectViolations(): ArchViolation[];
82
+ }
83
+ //# sourceMappingURL=terminal-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-builder.d.ts","sourceRoot":"","sources":["../src/terminal-builder.ts"],"names":[],"mappings":"AAAA,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;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAI5D;;;;;;;;;;;GAWG;AACH,8BAAsB,eAAe;IACnC,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IAC1B,SAAS,CAAC,SAAS,CAAC,EAAE,YAAY,CAAA;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA0B;IACtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;IAExD;;;OAGG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAK7B;;;;;OAKG;IACH,IAAI,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI;IAQlC;;;;;;;;;;OAUG;IACH,SAAS,CAAC,GAAG,QAAQ,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI;IAYnE;;;OAGG;IACH,YAAY,IAAI,eAAe;IAU/B;;;OAGG;IACH,UAAU,IAAI,aAAa,EAAE;IAU7B;;;;;OAKG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI;IAcnC;;;;;OAKG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI;IAclC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI;IAQvC;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,iBAAiB,IAAI,aAAa,EAAE;CACxD"}