@nielspeter/eess 0.2.2 → 0.4.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 (157) hide show
  1. package/README.md +3 -3
  2. package/dist/assertion-advice.d.ts +26 -0
  3. package/dist/assertion-advice.d.ts.map +1 -0
  4. package/dist/assertion-advice.js +29 -0
  5. package/dist/assertion-advice.js.map +1 -0
  6. package/dist/baseline.d.ts +69 -4
  7. package/dist/baseline.d.ts.map +1 -1
  8. package/dist/baseline.js +119 -13
  9. package/dist/baseline.js.map +1 -1
  10. package/dist/cache-registry.d.ts +14 -0
  11. package/dist/cache-registry.d.ts.map +1 -0
  12. package/dist/cache-registry.js +37 -0
  13. package/dist/cache-registry.js.map +1 -0
  14. package/dist/cardinality.d.ts +35 -0
  15. package/dist/cardinality.d.ts.map +1 -0
  16. package/dist/cardinality.js +65 -0
  17. package/dist/cardinality.js.map +1 -0
  18. package/dist/check-options.d.ts +14 -0
  19. package/dist/check-options.d.ts.map +1 -1
  20. package/dist/comment-suppression.d.ts +60 -0
  21. package/dist/comment-suppression.d.ts.map +1 -0
  22. package/dist/comment-suppression.js +89 -0
  23. package/dist/comment-suppression.js.map +1 -0
  24. package/dist/condition.d.ts +6 -0
  25. package/dist/condition.d.ts.map +1 -1
  26. package/dist/correspondence-core.d.ts +35 -0
  27. package/dist/correspondence-core.d.ts.map +1 -0
  28. package/dist/correspondence-core.js +38 -0
  29. package/dist/correspondence-core.js.map +1 -0
  30. package/dist/correspondence.d.ts +27 -5
  31. package/dist/correspondence.d.ts.map +1 -1
  32. package/dist/correspondence.js +39 -5
  33. package/dist/correspondence.js.map +1 -1
  34. package/dist/dedupe-config-findings.d.ts +56 -0
  35. package/dist/dedupe-config-findings.d.ts.map +1 -0
  36. package/dist/dedupe-config-findings.js +74 -0
  37. package/dist/dedupe-config-findings.js.map +1 -0
  38. package/dist/define.d.ts +44 -2
  39. package/dist/define.d.ts.map +1 -1
  40. package/dist/define.js +45 -4
  41. package/dist/define.js.map +1 -1
  42. package/dist/diff-aware.d.ts +8 -0
  43. package/dist/diff-aware.d.ts.map +1 -1
  44. package/dist/diff-aware.js +11 -2
  45. package/dist/diff-aware.js.map +1 -1
  46. package/dist/diff-disclosure.d.ts +72 -0
  47. package/dist/diff-disclosure.d.ts.map +1 -0
  48. package/dist/diff-disclosure.js +110 -0
  49. package/dist/diff-disclosure.js.map +1 -0
  50. package/dist/disk-set.d.ts +27 -0
  51. package/dist/disk-set.d.ts.map +1 -0
  52. package/dist/disk-set.js +12 -0
  53. package/dist/disk-set.js.map +1 -0
  54. package/dist/edge-coverage.d.ts +96 -0
  55. package/dist/edge-coverage.d.ts.map +1 -0
  56. package/dist/edge-coverage.js +108 -0
  57. package/dist/edge-coverage.js.map +1 -0
  58. package/dist/errors.d.ts +25 -0
  59. package/dist/errors.d.ts.map +1 -1
  60. package/dist/errors.js +32 -0
  61. package/dist/errors.js.map +1 -1
  62. package/dist/execute-rule.d.ts +2 -1
  63. package/dist/execute-rule.d.ts.map +1 -1
  64. package/dist/execute-rule.js +38 -6
  65. package/dist/execute-rule.js.map +1 -1
  66. package/dist/format-github.d.ts.map +1 -1
  67. package/dist/format-github.js +37 -5
  68. package/dist/format-github.js.map +1 -1
  69. package/dist/format-json.d.ts +83 -1
  70. package/dist/format-json.d.ts.map +1 -1
  71. package/dist/format-json.js +70 -9
  72. package/dist/format-json.js.map +1 -1
  73. package/dist/format.d.ts +4 -2
  74. package/dist/format.d.ts.map +1 -1
  75. package/dist/format.js +41 -19
  76. package/dist/format.js.map +1 -1
  77. package/dist/glob-site.d.ts +232 -0
  78. package/dist/glob-site.d.ts.map +1 -0
  79. package/dist/glob-site.js +156 -0
  80. package/dist/glob-site.js.map +1 -0
  81. package/dist/identity-root.d.ts +55 -0
  82. package/dist/identity-root.d.ts.map +1 -0
  83. package/dist/identity-root.js +170 -0
  84. package/dist/identity-root.js.map +1 -0
  85. package/dist/index.d.ts +41 -3
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +33 -3
  88. package/dist/index.js.map +1 -1
  89. package/dist/owns-empty-discovery.d.ts +5 -0
  90. package/dist/owns-empty-discovery.d.ts.map +1 -0
  91. package/dist/owns-empty-discovery.js +77 -0
  92. package/dist/owns-empty-discovery.js.map +1 -0
  93. package/dist/path-universe.d.ts +44 -0
  94. package/dist/path-universe.d.ts.map +1 -0
  95. package/dist/path-universe.js +21 -0
  96. package/dist/path-universe.js.map +1 -0
  97. package/dist/predicate.d.ts +33 -0
  98. package/dist/predicate.d.ts.map +1 -1
  99. package/dist/preset-dispatch.d.ts +19 -0
  100. package/dist/preset-dispatch.d.ts.map +1 -1
  101. package/dist/preset-dispatch.js +43 -3
  102. package/dist/preset-dispatch.js.map +1 -1
  103. package/dist/project-relative.d.ts +25 -0
  104. package/dist/project-relative.d.ts.map +1 -0
  105. package/dist/project-relative.js +54 -0
  106. package/dist/project-relative.js.map +1 -0
  107. package/dist/report.d.ts +11 -5
  108. package/dist/report.d.ts.map +1 -1
  109. package/dist/report.js +17 -0
  110. package/dist/report.js.map +1 -1
  111. package/dist/rule-builder-like.d.ts +11 -0
  112. package/dist/rule-builder-like.d.ts.map +1 -0
  113. package/dist/rule-builder-like.js +2 -0
  114. package/dist/rule-builder-like.js.map +1 -0
  115. package/dist/rule-builder.d.ts +109 -84
  116. package/dist/rule-builder.d.ts.map +1 -1
  117. package/dist/rule-builder.js +202 -158
  118. package/dist/rule-builder.js.map +1 -1
  119. package/dist/rule-description.d.ts +33 -1
  120. package/dist/rule-description.d.ts.map +1 -1
  121. package/dist/rule-description.js +17 -1
  122. package/dist/rule-description.js.map +1 -1
  123. package/dist/rule-metadata.d.ts +20 -3
  124. package/dist/rule-metadata.d.ts.map +1 -1
  125. package/dist/selection-memo.d.ts +47 -0
  126. package/dist/selection-memo.d.ts.map +1 -0
  127. package/dist/selection-memo.js +68 -0
  128. package/dist/selection-memo.js.map +1 -0
  129. package/dist/shallow-clone.d.ts +20 -0
  130. package/dist/shallow-clone.d.ts.map +1 -0
  131. package/dist/shallow-clone.js +27 -0
  132. package/dist/shallow-clone.js.map +1 -0
  133. package/dist/stderr.d.ts +16 -0
  134. package/dist/stderr.d.ts.map +1 -0
  135. package/dist/stderr.js +73 -0
  136. package/dist/stderr.js.map +1 -0
  137. package/dist/terminal-builder.d.ts +209 -9
  138. package/dist/terminal-builder.d.ts.map +1 -1
  139. package/dist/terminal-builder.js +264 -20
  140. package/dist/terminal-builder.js.map +1 -1
  141. package/dist/type-guards.d.ts +38 -0
  142. package/dist/type-guards.d.ts.map +1 -0
  143. package/dist/type-guards.js +42 -0
  144. package/dist/type-guards.js.map +1 -0
  145. package/dist/unsuppressable.d.ts +54 -0
  146. package/dist/unsuppressable.d.ts.map +1 -0
  147. package/dist/unsuppressable.js +76 -0
  148. package/dist/unsuppressable.js.map +1 -0
  149. package/dist/vacuity-findings.d.ts +55 -0
  150. package/dist/vacuity-findings.d.ts.map +1 -0
  151. package/dist/vacuity-findings.js +124 -0
  152. package/dist/vacuity-findings.js.map +1 -0
  153. package/dist/violation.d.ts +152 -4
  154. package/dist/violation.d.ts.map +1 -1
  155. package/dist/violation.js +262 -1
  156. package/dist/violation.js.map +1 -1
  157. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAMtD;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEpD,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAA;IAC9B,kDAAkD;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,sEAAsE;AACtE,MAAM,WAAW,mBAAoB,SAAQ,aAAa;IACxD,sCAAsC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAA;CAC7B;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,aAAa,EAAE,EAAE,OAAO,GAAE,aAAkB,GAAG,IAAI,CAY/F;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,aAAa,EAAE,EAC3B,OAAO,GAAE,mBAAwB,GAChC,aAAa,EAAE,CAQjB"}
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAMtD;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEpD,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAA;IAC9B,kDAAkD;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,sEAAsE;AACtE,MAAM,WAAW,mBAAoB,SAAQ,aAAa;IACxD,sCAAsC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAA;CAC7B;AAWD;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,aAAa,EAAE,EAAE,OAAO,GAAE,aAAkB,GAAG,IAAI,CAa/F;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,aAAa,EAAE,EAC3B,OAAO,GAAE,mBAAwB,GAChC,aAAa,EAAE,CAQjB"}
package/dist/report.js CHANGED
@@ -9,9 +9,26 @@ import { formatViolationsGitHub } from './format-github.js';
9
9
  * path) both delegate here, so the two reporting paths can no longer diverge
10
10
  * (plan 0070). Emits nothing for an empty set.
11
11
  */
12
+ let violationsEmitted = 0;
13
+ /**
14
+ * How many violations this module has actually WRITTEN, ever.
15
+ *
16
+ * Read as a delta by a caller that aggregates reporting, to answer one question it
17
+ * cannot otherwise answer: *did something emit while I was loading that module?*
18
+ *
19
+ * The alternative a caller might reach for — counting the writes it SUPPRESSED and
20
+ * inferring "then nothing was written" — is a double negative and is unsound: a
21
+ * module that suppresses one terminal and leaks through another satisfies it while
22
+ * leaking. Measured on `eess-ts`, which shipped that inference and fell into
23
+ * exactly that case. Count emissions, not silences.
24
+ */
25
+ export function violationsEmittedCount() {
26
+ return violationsEmitted;
27
+ }
12
28
  export function reportViolations(violations, options = {}) {
13
29
  if (violations.length === 0)
14
30
  return;
31
+ violationsEmitted += violations.length;
15
32
  const format = options.format ?? 'terminal';
16
33
  if (format === 'json') {
17
34
  process.stdout.write(formatViolationsJson(violations, options.reason) + '\n');
@@ -1 +1 @@
1
- {"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAwB3D;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAA2B,EAAE,UAAyB,EAAE;IACvF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAM;IACnC,MAAM,MAAM,GAAiB,OAAO,CAAC,MAAM,IAAI,UAAU,CAAA;IACzD,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAA;IAC/E,CAAC;SAAM,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;IAC1E,CAAC;SAAM,CAAC;QACN,wEAAwE;QACxE,0DAA0D;QAC1D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAA;IAC3E,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,UAA2B,EAC3B,UAA+B,EAAE;IAEjC,MAAM,IAAI,GAAe,OAAO,CAAC,MAAM,IAAI,OAAO,CAAA;IAClD,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,UAAU,CAAA;IACxC,gBAAgB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IACrC,IAAI,IAAI,KAAK,OAAO,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IACrD,CAAC;IACD,OAAO,UAAU,CAAA;AACnB,CAAC"}
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAwB3D;;;;;;GAMG;AACH,IAAI,iBAAiB,GAAG,CAAC,CAAA;AAEzB;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,iBAAiB,CAAA;AAC1B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,UAA2B,EAAE,UAAyB,EAAE;IACvF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAM;IACnC,iBAAiB,IAAI,UAAU,CAAC,MAAM,CAAA;IACtC,MAAM,MAAM,GAAiB,OAAO,CAAC,MAAM,IAAI,UAAU,CAAA;IACzD,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAA;IAC/E,CAAC;SAAM,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;IAC1E,CAAC;SAAM,CAAC;QACN,wEAAwE;QACxE,0DAA0D;QAC1D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAA;IAC3E,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,UAA2B,EAC3B,UAA+B,EAAE;IAEjC,MAAM,IAAI,GAAe,OAAO,CAAC,MAAM,IAAI,OAAO,CAAA;IAClD,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,UAAU,CAAA;IACxC,gBAAgB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IACrC,IAAI,IAAI,KAAK,OAAO,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IACrD,CAAC;IACD,OAAO,UAAU,CAAA;AACnB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { ArchViolation } from './violation.js';
2
+ /**
3
+ * Minimal shape the CLI runner and presets collect violations from: anything
4
+ * exposing a non-throwing, severity-stamped `.violations()`. Lives in core (not
5
+ * `cli/`) so presets can return `RuleBuilderLike[]` without depending on CLI
6
+ * infrastructure.
7
+ */
8
+ export interface RuleBuilderLike {
9
+ violations: () => ArchViolation[];
10
+ }
11
+ //# sourceMappingURL=rule-builder-like.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-builder-like.d.ts","sourceRoot":"","sources":["../src/rule-builder-like.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,aAAa,EAAE,CAAA;CAClC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rule-builder-like.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-builder-like.js","sourceRoot":"","sources":["../src/rule-builder-like.ts"],"names":[],"mappings":""}
@@ -1,11 +1,9 @@
1
1
  import type { Predicate } from './predicate.js';
2
2
  import type { Condition, ConditionContext } from './condition.js';
3
- import type { ArchViolation } from './violation.js';
4
- import type { CheckOptions } from './check-options.js';
5
- import type { RuleMetadata } from './rule-metadata.js';
6
3
  import type { RuleDescription } from './rule-description.js';
7
- import type { SilentExclusion } from './silent-exclusion.js';
8
4
  import type { Selection, ElementInfo } from './correspondence.js';
5
+ import type { GlobNode } from './glob-site.js';
6
+ import { TerminalBuilder, type CollectResult } from './terminal-builder.js';
9
7
  /**
10
8
  * Abstract base class for all rule builders.
11
9
  *
@@ -15,20 +13,35 @@ import type { Selection, ElementInfo } from './correspondence.js';
15
13
  * 3. Add condition methods that call `addCondition()`
16
14
  *
17
15
  * The builder accumulates predicates and conditions. Nothing executes
18
- * until a terminal method (`.check()`, `.warn()`, `.severity()`) is called.
16
+ * until a terminal method (`.check()`, `.warn()`, `.severity()`) is called —
17
+ * inherited from {@link TerminalBuilder} (plan 0088 Phase 4), which also
18
+ * supplies `because`/`rule`/`excluding`/`violations`/`expectEmpty`/
19
+ * `expectNonEmpty` and the ADR-010 evidence gate. `RuleBuilder` keeps its own
20
+ * `<T, P>` two-param generic — `P` is the dialect's project type, kept
21
+ * separate from `TerminalBuilder` (which has no project concept, since
22
+ * `correspondence()`, `SmellBuilder`, and the graphql builders don't share a
23
+ * uniform notion of "one project") — every builder across all five dialects
24
+ * depends on this exact two-param signature, so it is not replaced by the
25
+ * fold, only extended.
19
26
  */
20
- export declare abstract class RuleBuilder<T, P = unknown> {
27
+ export declare abstract class RuleBuilder<T, P = unknown> extends TerminalBuilder {
21
28
  protected readonly project: P;
22
29
  protected _predicates: Predicate<T>[];
23
30
  protected _conditions: Condition<T>[];
24
- protected _reason?: string;
25
- protected _metadata?: RuleMetadata;
26
- protected _exclusions: (string | RegExp)[];
27
- protected _silentIndices: Set<number>;
28
31
  protected _phase: 'predicate' | 'condition';
32
+ /** Whether `.should()` was ever reached — bug 0155 state 3/4 vs state 1. */
33
+ protected _reachedShould: boolean;
34
+ /**
35
+ * Descriptions of predicate-only methods used AFTER `.should()` — bug 0155
36
+ * state 2. A dual-use method dispatches to a condition in that phase and
37
+ * never lands here, so anything recorded is genuinely a filter written where
38
+ * an assertion was meant.
39
+ */
40
+ protected _misplaced: string[];
29
41
  constructor(project: P);
30
42
  /**
31
- * Begin the predicate phase. Returns `this` for chaining.
43
+ * Begin the predicate phase. Returns a COPY — a held selection is never
44
+ * edited by narrowing it (see `TerminalBuilder.copy()`).
32
45
  * Purely a readability marker — `.that().haveNameMatching(...)` reads like English.
33
46
  * Explicitly resets phase to 'predicate' — defensive against `.should().that()` misuse.
34
47
  */
@@ -59,50 +72,6 @@ export declare abstract class RuleBuilder<T, P = unknown> {
59
72
  * as a predicate; if it has `evaluate` it is treated as a condition.
60
73
  */
61
74
  satisfy(custom: Predicate<T> | Condition<T>): this;
62
- /**
63
- * Attach a human-readable rationale to the rule.
64
- * Included in violation messages when `.check()` throws.
65
- */
66
- because(reason: string): this;
67
- /**
68
- * Attach rich metadata to the rule.
69
- * Provides educational context in violation output: why, how to fix, docs link.
70
- *
71
- * If `metadata.because` is set, it also sets the reason (same as `.because()`).
72
- */
73
- rule(metadata: RuleMetadata): this;
74
- /**
75
- * Exclude specific violations from reporting by matching against
76
- * the violation's `element`, `file`, or `message` fields.
77
- *
78
- * Matched violations are silently suppressed. Use for permanent,
79
- * intentional exceptions — not for temporary violations (use baseline for those).
80
- *
81
- * Patterns are matched against all three fields. Prefer anchored regexes
82
- * or full string matches over short substrings, especially for `message`
83
- * matching, to avoid accidentally suppressing unrelated violations whose
84
- * messages happen to contain the same text.
85
- *
86
- * Emits a warning if an exclusion matches zero violations — so renamed
87
- * or deleted exceptions don't silently stay in the rule.
88
- *
89
- * For narrowing a rule's scope at the predicate phase (so the rule never
90
- * evaluates the excluded element), use `satisfy(not(<predicate>))` instead.
91
- * See the "Excluding a file from a rule's scope" recipe in docs/recipes.md.
92
- *
93
- * @example
94
- * // Exclude by element name (fully qualified)
95
- * .excluding('Asset.getImageUrl')
96
- *
97
- * @example
98
- * // Exclude by file path (regex anchored to suffix)
99
- * .excluding(/repositories\/index\.ts$/)
100
- *
101
- * @example
102
- * // Multiple exclusions, mixed forms
103
- * .excluding('Asset.getImageUrl', /\/legacy\//, /generated/)
104
- */
105
- excluding(...patterns: (string | RegExp | SilentExclusion)[]): this;
106
75
  /**
107
76
  * Return the predicate-filtered elements as a labelled `Selection<T>` for
108
77
  * cross-validation (`correspondence()`). The set is exactly the elements this
@@ -121,38 +90,61 @@ export declare abstract class RuleBuilder<T, P = unknown> {
121
90
  */
122
91
  describeRule(): RuleDescription;
123
92
  /**
124
- * Execute the rule and return violations after exclusion filtering.
125
- * Does not throw — use for programmatic access (presets, aggregation).
126
- */
127
- violations(): ArchViolation[];
128
- /**
129
- * Execute the rule and throw `ArchRuleError` if any violations are found.
130
- * This is the primary terminal method — use in test assertions.
93
+ * ADR-010: is this rule's assertion satisfied by an empty selection, as a
94
+ * property of what its conditions assert (`.notExist()`) rather than a
95
+ * caller's `.expectEmpty()` declaration?
131
96
  *
132
- * @param options - Optional baseline and diff filtering
133
- */
134
- check(options?: CheckOptions): void;
135
- /**
136
- * Execute the rule and log violations to stderr. Does not throw.
137
- * Use for rules that should warn but not fail CI.
97
+ * `.every()`, not `.some()` — `.andShould()` ANDs, so a rule reading
98
+ * `.should().notExist().andShould().beExported()` still asserts something
99
+ * about subjects that exist, and exempting it on the strength of the one
100
+ * cardinality condition would silence the other. An empty condition list is
101
+ * NOT exempt: `[].every()` is vacuously `true`, and a rule with zero
102
+ * conditions is the assertion-less case `collectViolations()` now reports as
103
+ * a configuration finding (bug 0155; it used to be an unreachable stderr
104
+ * warning, which this comment named until that fix landed) — this override
105
+ * must not paper over it by also declaring it cardinality-satisfied.
138
106
  *
139
- * @param options - Optional baseline and diff filtering
107
+ * **Load-bearing:** for a DEAD selector this early return is the only thing
108
+ * between an assertion-less rule and a silent pass (the gate runs after the
109
+ * zero-examined branch). Pinned in `assertion-less-rules.test.ts`.
140
110
  */
141
- warn(options?: CheckOptions): void;
142
111
  /**
143
- * Execute the rule with the given severity.
144
- * `.severity('error')` is equivalent to `.check()`.
145
- * `.severity('warn')` is equivalent to `.warn()`.
146
- */
147
- severity(level: 'error' | 'warn'): void;
112
+ * Does this rule assert anything at all? — bug 0155.
113
+ *
114
+ * Conditions alone are not enough: a predicate-only method written *after*
115
+ * `.should()` silently shrank the set the conditions run over, possibly to
116
+ * empty, in which case they hold vacuously. That rule asserts nothing
117
+ * meaningful even though `_conditions` is non-empty.
118
+ */
119
+ protected assertsSomething(): boolean;
120
+ protected assertionAdvice(): string;
121
+ protected assertsCardinality(): boolean;
122
+ /**
123
+ * ADR-010 part 3: did this rule's own underlying source (the project, a
124
+ * diagram, an SDL string — whatever `P` loads) produce nothing at all,
125
+ * before `getElements()`'s own domain-specific extraction or any
126
+ * predicate ran? Default `false` — the kernel has no way to know, since
127
+ * `P` is opaque here. A dialect's concrete builder, which knows `P`'s real
128
+ * shape (e.g. `ArchProject`), overrides this to check its own project's
129
+ * file count directly — never `getElements().length`, which conflates "the
130
+ * source is empty" with "this domain legitimately has nothing in a
131
+ * healthy project" (e.g. zero JSX elements in a backend-only codebase).
132
+ */
133
+ protected sourceEmpty(): boolean;
134
+ /** Declared globs, position derived from `_phase`. Pure — no `P` needed. */
135
+ globs(): readonly GlobNode[];
136
+ /** This rule's own project, for `diagnose()`/`doctor` outside `.check()`. */
137
+ getProject(): P;
138
+ /** A `globs()` tree diagnosably dead, when zero examined — same shape as `sourceEmpty()`. */
139
+ protected deadGlobDiagnosis(): string | undefined;
148
140
  /**
149
141
  * Register a predicate. Called by concrete builder methods like
150
- * `.haveNameMatching()`, `.extend()`, etc.
142
+ * `.haveNameMatching()`, `.extend()`, etc. Returns a COPY — see `copy()`.
151
143
  */
152
144
  protected addPredicate(predicate: Predicate<T>): this;
153
145
  /**
154
146
  * Register a condition. Called by concrete builder methods like
155
- * `.notContain()`, `.notExist()`, etc.
147
+ * `.notContain()`, `.notExist()`, etc. Returns a COPY — see `copy()`.
156
148
  */
157
149
  protected addCondition(condition: Condition<T>): this;
158
150
  /**
@@ -161,10 +153,31 @@ export declare abstract class RuleBuilder<T, P = unknown> {
161
153
  */
162
154
  protected abstract getElements(): T[];
163
155
  /**
164
- * Create a fork of this builder with the same predicates but empty conditions.
165
- * Used by `.should()` to support named selections without mutation.
156
+ * An independent copy, carrying **both** predicates and conditions.
157
+ * Extends `TerminalBuilder.copy()` with the fields this class adds.
166
158
  *
167
- * Subclasses with additional constructor args MUST override this method.
159
+ * Subclasses with additional mutable-reference fields of their own (arrays,
160
+ * sets, maps a chain method pushes onto) MUST override this and call
161
+ * `super.copy()` first, the same reason `TerminalBuilder.copy()`'s own
162
+ * docstring gives — `Object.assign` shallow-copies a field, it does not
163
+ * give it independent identity.
164
+ */
165
+ protected copy(): this;
166
+ /**
167
+ * A fork of this builder, carrying BOTH lists. Used by `.should()` to support
168
+ * named selections without mutation.
169
+ *
170
+ * **Nothing here clears the conditions** (bug 0156). It used to, and a second
171
+ * `.should()` therefore silently discarded the first assertion — a rule that
172
+ * asserted something turned into one that asserted less, by a chain method.
173
+ * `.should().X().should().Y()` now accumulates exactly as `.andShould()` does.
174
+ *
175
+ * Ported from `packages/ts/src/core/rule-builder.ts`, where the engine copy
176
+ * landed the fix and left the kernel behind. Found by the architect review of
177
+ * PR #72: `eess-md`, `eess-mermaid` and `eess-gherkin` all extend THIS class,
178
+ * so they carried the defect — and `check:corpus`, `check:ledger` and
179
+ * `check:diagram` are md/mermaid gates, meaning this repo's own corpus
180
+ * enforcement ran on the broken copy.
168
181
  */
169
182
  protected fork(): this;
170
183
  /**
@@ -172,10 +185,22 @@ export declare abstract class RuleBuilder<T, P = unknown> {
172
185
  */
173
186
  private buildRuleDescription;
174
187
  /**
175
- * Execute the full pipeline: filter elements with predicates,
176
- * evaluate conditions, return violations.
177
- */
178
- private evaluate;
188
+ * Execute the full pipeline: filter elements with predicates, evaluate
189
+ * conditions, return violations plus the ADR-010 evidence — the
190
+ * post-predicate subject count, whatever it is. `TerminalBuilder.check()` /
191
+ * `.warn()` / `.violations()` decide what a zero count means (a
192
+ * configuration finding, unless `.expectEmpty()` was declared); this method
193
+ * only reports the count, never interprets it.
194
+ *
195
+ * `sourceEmpty` (ADR-010 part 3) is deliberately not `allElements.length
196
+ * === 0` — a domain's own `getElements()` legitimately returning nothing in
197
+ * a healthy, fully-loaded project (e.g. zero JSX elements in a
198
+ * backend-only codebase) is not brokenness, and conflating the two broke
199
+ * `.notExist()`'s cardinality exemption for exactly that case. Subclasses
200
+ * that can answer "did my underlying source load anything at all" override
201
+ * `sourceEmpty()`; the default assumes it did.
202
+ */
203
+ protected collectViolations(): CollectResult;
179
204
  /**
180
205
  * Build the `ConditionContext` passed to each condition.
181
206
  *
@@ -1 +1 @@
1
- {"version":3,"file":"rule-builder.d.ts","sourceRoot":"","sources":["../src/rule-builder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rule-builder.d.ts","sourceRoot":"","sources":["../src/rule-builder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAEjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjE,OAAO,KAAK,EAAgB,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAE5D,OAAO,EAAE,eAAe,EAAE,KAAK,aAAa,EAA0B,MAAM,uBAAuB,CAAA;AA6CnG;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,8BAAsB,WAAW,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,CAAE,SAAQ,eAAe;IAc3D,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;IAbzC,SAAS,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAK;IAC1C,SAAS,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAK;IAC1C,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAAc;IACzD,4EAA4E;IAC5E,SAAS,CAAC,cAAc,UAAQ;IAChC;;;;;OAKG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,CAAK;gBAEJ,OAAO,EAAE,CAAC;IAMzC;;;;;OAKG;IACH,IAAI,IAAI,IAAI;IAMZ;;;OAGG;IACH,GAAG,IAAI,IAAI;IAIX;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAOd;;;OAGG;IACH,SAAS,IAAI,IAAI;IAIjB;;;;;;;;OAQG;IACH,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI;IAelD;;;;;;;OAOG;IACH,MAAM,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,WAAW,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC,CAAC;IAOpF;;;OAGG;IACH,YAAY,IAAI,eAAe;IAW/B;;;;;;;;;;;;;;;;;;OAkBG;IACH;;;;;;;OAOG;IACH,SAAS,CAAC,gBAAgB,IAAI,OAAO;IAIrC,SAAS,CAAC,eAAe,IAAI,MAAM;cAQhB,kBAAkB,IAAI,OAAO;IAKhD;;;;;;;;;;OAUG;IACH,SAAS,CAAC,WAAW,IAAI,OAAO;IAIhC,4EAA4E;IAC5E,KAAK,IAAI,SAAS,QAAQ,EAAE;IAI5B,6EAA6E;IAC7E,UAAU,IAAI,CAAC;IAIf,6FAA6F;IAC7F,SAAS,CAAC,iBAAiB,IAAI,MAAM,GAAG,SAAS;IAMjD;;;OAGG;IACH,SAAS,CAAC,YAAY,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI;IAWrD;;;OAGG;IACH,SAAS,CAAC,YAAY,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI;IAMrD;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,WAAW,IAAI,CAAC,EAAE;IAErC;;;;;;;;;OASG;cACgB,IAAI,IAAI,IAAI;IAQ/B;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,IAAI,IAAI,IAAI;IAQtB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAS5B;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,iBAAiB,IAAI,aAAa;IAuC5C;;;;;;OAMG;IACH,SAAS,CAAC,qBAAqB,IAAI,gBAAgB;CASpD"}