@shrkcrft/boundaries 0.1.0-alpha.30 → 0.1.0-alpha.31

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 (226) hide show
  1. package/dist/baseline/compute-baseline.d.ts +8 -0
  2. package/dist/baseline/compute-baseline.d.ts.map +1 -1
  3. package/dist/baseline/compute-baseline.js +8 -7
  4. package/dist/baseline/diff-baseline.d.ts +28 -0
  5. package/dist/baseline/diff-baseline.d.ts.map +1 -1
  6. package/dist/baseline/diff-baseline.js +29 -0
  7. package/dist/evaluate/boundary-unit-finding.d.ts +10 -0
  8. package/dist/evaluate/boundary-unit-finding.d.ts.map +1 -0
  9. package/dist/evaluate/boundary-unit-finding.js +21 -0
  10. package/dist/evaluate/boundary-unit-kind.d.ts +7 -0
  11. package/dist/evaluate/boundary-unit-kind.d.ts.map +1 -0
  12. package/dist/evaluate/boundary-unit-kind.js +14 -0
  13. package/dist/evaluate/evaluate-boundaries.d.ts +230 -3
  14. package/dist/evaluate/evaluate-boundaries.d.ts.map +1 -1
  15. package/dist/evaluate/evaluate-boundaries.js +481 -53
  16. package/dist/evaluate/i-boundary-rule-settle-input.d.ts +27 -0
  17. package/dist/evaluate/i-boundary-rule-settle-input.d.ts.map +1 -0
  18. package/dist/evaluate/i-boundary-rule-settle-input.js +1 -0
  19. package/dist/evaluate/i-boundary-rule-settlement.d.ts +23 -0
  20. package/dist/evaluate/i-boundary-rule-settlement.d.ts.map +1 -0
  21. package/dist/evaluate/i-boundary-rule-settlement.js +1 -0
  22. package/dist/evaluate/i-boundary-unit-finding.d.ts +22 -0
  23. package/dist/evaluate/i-boundary-unit-finding.d.ts.map +1 -0
  24. package/dist/evaluate/i-boundary-unit-finding.js +1 -0
  25. package/dist/evaluate/settle-boundary-rule.d.ts +23 -0
  26. package/dist/evaluate/settle-boundary-rule.d.ts.map +1 -0
  27. package/dist/evaluate/settle-boundary-rule.js +73 -0
  28. package/dist/evaluate/with-boundary-rule-settlement.d.ts +11 -0
  29. package/dist/evaluate/with-boundary-rule-settlement.d.ts.map +1 -0
  30. package/dist/evaluate/with-boundary-rule-settlement.js +44 -0
  31. package/dist/extract/code-zones.d.ts +95 -3
  32. package/dist/extract/code-zones.d.ts.map +1 -1
  33. package/dist/extract/code-zones.js +313 -4
  34. package/dist/extract/extract-tokens.d.ts +30 -0
  35. package/dist/extract/extract-tokens.d.ts.map +1 -1
  36. package/dist/extract/extract-tokens.js +233 -27
  37. package/dist/extract/i-labeled-source.d.ts +12 -0
  38. package/dist/extract/i-labeled-source.d.ts.map +1 -0
  39. package/dist/extract/i-labeled-source.js +1 -0
  40. package/dist/extract/import-edges.d.ts +1 -0
  41. package/dist/extract/import-edges.d.ts.map +1 -1
  42. package/dist/extract/import-edges.js +20 -7
  43. package/dist/extract/inspect-source.d.ts +32 -3
  44. package/dist/extract/inspect-source.d.ts.map +1 -1
  45. package/dist/extract/inspect-source.js +50 -7
  46. package/dist/extract/parse-imports.d.ts +34 -12
  47. package/dist/extract/parse-imports.d.ts.map +1 -1
  48. package/dist/extract/parse-imports.js +119 -20
  49. package/dist/extract/scan-literals.d.ts +12 -1
  50. package/dist/extract/scan-literals.d.ts.map +1 -1
  51. package/dist/extract/scan-literals.js +15 -1
  52. package/dist/extract/source-liveness-request.d.ts +24 -0
  53. package/dist/extract/source-liveness-request.d.ts.map +1 -0
  54. package/dist/extract/source-liveness-request.js +45 -0
  55. package/dist/generated/check-provenance.d.ts.map +1 -1
  56. package/dist/generated/check-provenance.js +3 -1
  57. package/dist/generated/read-regen-tree.d.ts +16 -0
  58. package/dist/generated/read-regen-tree.d.ts.map +1 -0
  59. package/dist/generated/read-regen-tree.js +61 -0
  60. package/dist/generated/scan-generated.d.ts +8 -0
  61. package/dist/generated/scan-generated.d.ts.map +1 -1
  62. package/dist/generated/scan-generated.js +25 -10
  63. package/dist/index.d.ts +48 -0
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +51 -0
  66. package/dist/model/boundary-intended-empty-line.d.ts +10 -0
  67. package/dist/model/boundary-intended-empty-line.d.ts.map +1 -0
  68. package/dist/model/boundary-intended-empty-line.js +11 -0
  69. package/dist/model/boundary-markable-list.d.ts +15 -0
  70. package/dist/model/boundary-markable-list.d.ts.map +1 -0
  71. package/dist/model/boundary-markable-list.js +15 -0
  72. package/dist/model/boundary-rule-input-keys.d.ts +11 -0
  73. package/dist/model/boundary-rule-input-keys.d.ts.map +1 -0
  74. package/dist/model/boundary-rule-input-keys.js +29 -0
  75. package/dist/model/boundary-rule-input.d.ts +23 -0
  76. package/dist/model/boundary-rule-input.d.ts.map +1 -0
  77. package/dist/model/boundary-rule-input.js +1 -0
  78. package/dist/model/boundary-rule-key-problems.d.ts +11 -0
  79. package/dist/model/boundary-rule-key-problems.d.ts.map +1 -0
  80. package/dist/model/boundary-rule-key-problems.js +36 -0
  81. package/dist/model/boundary-rule-marker-problems.d.ts +26 -0
  82. package/dist/model/boundary-rule-marker-problems.d.ts.map +1 -0
  83. package/dist/model/boundary-rule-marker-problems.js +94 -0
  84. package/dist/model/boundary-rule-scope.d.ts +88 -0
  85. package/dist/model/boundary-rule-scope.d.ts.map +1 -0
  86. package/dist/model/boundary-rule-scope.js +175 -0
  87. package/dist/model/boundary-rule.d.ts +120 -5
  88. package/dist/model/boundary-rule.d.ts.map +1 -1
  89. package/dist/model/boundary-rule.js +148 -1
  90. package/dist/model/boundary-unit-problem-issue.d.ts +9 -0
  91. package/dist/model/boundary-unit-problem-issue.d.ts.map +1 -0
  92. package/dist/model/boundary-unit-problem-issue.js +16 -0
  93. package/dist/model/normalize-boundary-rule.d.ts +21 -0
  94. package/dist/model/normalize-boundary-rule.d.ts.map +1 -0
  95. package/dist/model/normalize-boundary-rule.js +62 -0
  96. package/dist/policy/evaluate-policy.d.ts +80 -3
  97. package/dist/policy/evaluate-policy.d.ts.map +1 -1
  98. package/dist/policy/evaluate-policy.js +152 -19
  99. package/dist/policy/i-policy-rule-liveness.d.ts +15 -0
  100. package/dist/policy/i-policy-rule-liveness.d.ts.map +1 -0
  101. package/dist/policy/i-policy-rule-liveness.js +1 -0
  102. package/dist/policy/run-policy.d.ts +1 -1
  103. package/dist/policy/run-policy.d.ts.map +1 -1
  104. package/dist/policy/run-policy.js +171 -21
  105. package/dist/registry/load-boundary-rules.d.ts +29 -1
  106. package/dist/registry/load-boundary-rules.d.ts.map +1 -1
  107. package/dist/registry/load-boundary-rules.js +38 -8
  108. package/dist/scan/glob.d.ts +90 -0
  109. package/dist/scan/glob.d.ts.map +1 -1
  110. package/dist/scan/glob.js +205 -0
  111. package/dist/scan/i-glob-unit-measure.d.ts +22 -0
  112. package/dist/scan/i-glob-unit-measure.d.ts.map +1 -0
  113. package/dist/scan/i-glob-unit-measure.js +1 -0
  114. package/dist/scan/import-pattern.d.ts +109 -0
  115. package/dist/scan/import-pattern.d.ts.map +1 -0
  116. package/dist/scan/import-pattern.js +191 -0
  117. package/dist/scan/node-builtin-package-names.d.ts +13 -0
  118. package/dist/scan/node-builtin-package-names.d.ts.map +1 -0
  119. package/dist/scan/node-builtin-package-names.js +25 -0
  120. package/dist/scan/scan-imports.d.ts +36 -3
  121. package/dist/scan/scan-imports.d.ts.map +1 -1
  122. package/dist/scan/scan-imports.js +114 -45
  123. package/dist/util/blank-run-hazard-finding.d.ts +34 -0
  124. package/dist/util/blank-run-hazard-finding.d.ts.map +1 -0
  125. package/dist/util/blank-run-hazard-finding.js +1 -0
  126. package/dist/util/blank-run-hazard.d.ts +29 -0
  127. package/dist/util/blank-run-hazard.d.ts.map +1 -0
  128. package/dist/util/blank-run-hazard.js +450 -0
  129. package/dist/util/dead-glob-units.d.ts +46 -0
  130. package/dist/util/dead-glob-units.d.ts.map +1 -0
  131. package/dist/util/dead-glob-units.js +116 -0
  132. package/dist/util/glob-list-liveness-input.d.ts +31 -0
  133. package/dist/util/glob-list-liveness-input.d.ts.map +1 -0
  134. package/dist/util/glob-list-liveness-input.js +65 -0
  135. package/dist/util/i-dead-glob-unit.d.ts +24 -0
  136. package/dist/util/i-dead-glob-unit.d.ts.map +1 -0
  137. package/dist/util/i-dead-glob-unit.js +1 -0
  138. package/dist/util/i-glob-list-units.d.ts +25 -0
  139. package/dist/util/i-glob-list-units.d.ts.map +1 -0
  140. package/dist/util/i-glob-list-units.js +1 -0
  141. package/dist/util/i-glob-liveness-list.d.ts +23 -0
  142. package/dist/util/i-glob-liveness-list.d.ts.map +1 -0
  143. package/dist/util/i-glob-liveness-list.js +1 -0
  144. package/dist/util/i-glob-liveness-request.d.ts +11 -0
  145. package/dist/util/i-glob-liveness-request.d.ts.map +1 -0
  146. package/dist/util/i-glob-liveness-request.js +1 -0
  147. package/dist/util/i-glob-negation.d.ts +13 -0
  148. package/dist/util/i-glob-negation.d.ts.map +1 -0
  149. package/dist/util/i-glob-negation.js +1 -0
  150. package/dist/util/matched-files.d.ts +22 -0
  151. package/dist/util/matched-files.d.ts.map +1 -0
  152. package/dist/util/matched-files.js +1 -0
  153. package/dist/util/negation-cause.d.ts +15 -0
  154. package/dist/util/negation-cause.d.ts.map +1 -0
  155. package/dist/util/negation-cause.js +21 -0
  156. package/dist/util/plane-scan-exclude-dirs.d.ts +17 -0
  157. package/dist/util/plane-scan-exclude-dirs.d.ts.map +1 -0
  158. package/dist/util/plane-scan-exclude-dirs.js +22 -0
  159. package/dist/util/read-glob-list-liveness.d.ts +20 -0
  160. package/dist/util/read-glob-list-liveness.d.ts.map +1 -0
  161. package/dist/util/read-glob-list-liveness.js +20 -0
  162. package/dist/util/read-scope-coverage.d.ts +90 -0
  163. package/dist/util/read-scope-coverage.d.ts.map +1 -0
  164. package/dist/util/read-scope-coverage.js +173 -0
  165. package/dist/util/read-scope.d.ts +14 -0
  166. package/dist/util/read-scope.d.ts.map +1 -0
  167. package/dist/util/read-scope.js +1 -0
  168. package/dist/util/read-selected-files.d.ts +16 -0
  169. package/dist/util/read-selected-files.d.ts.map +1 -0
  170. package/dist/util/read-selected-files.js +25 -0
  171. package/dist/util/settle-glob-lists.d.ts +12 -0
  172. package/dist/util/settle-glob-lists.d.ts.map +1 -0
  173. package/dist/util/settle-glob-lists.js +13 -0
  174. package/dist/util/unread-file-reason.d.ts +26 -0
  175. package/dist/util/unread-file-reason.d.ts.map +1 -0
  176. package/dist/util/unread-file-reason.js +26 -0
  177. package/dist/util/unread-file.d.ts +10 -0
  178. package/dist/util/unread-file.d.ts.map +1 -0
  179. package/dist/util/unread-file.js +1 -0
  180. package/dist/util/walk-files.d.ts +74 -10
  181. package/dist/util/walk-files.d.ts.map +1 -1
  182. package/dist/util/walk-files.js +137 -31
  183. package/dist/wiring/evaluate-wiring.d.ts +79 -5
  184. package/dist/wiring/evaluate-wiring.d.ts.map +1 -1
  185. package/dist/wiring/evaluate-wiring.js +267 -12
  186. package/dist/wiring/explain-wiring.d.ts +49 -5
  187. package/dist/wiring/explain-wiring.d.ts.map +1 -1
  188. package/dist/wiring/explain-wiring.js +118 -10
  189. package/dist/wiring/i-idiom-role-coverage.d.ts +24 -0
  190. package/dist/wiring/i-idiom-role-coverage.d.ts.map +1 -0
  191. package/dist/wiring/i-idiom-role-coverage.js +1 -0
  192. package/dist/wiring/i-registration-query-verdict.d.ts +38 -0
  193. package/dist/wiring/i-registration-query-verdict.d.ts.map +1 -0
  194. package/dist/wiring/i-registration-query-verdict.js +1 -0
  195. package/dist/wiring/i-registration-roles.d.ts +71 -0
  196. package/dist/wiring/i-registration-roles.d.ts.map +1 -0
  197. package/dist/wiring/i-registration-roles.js +1 -0
  198. package/dist/wiring/measure-idiom-role-coverage.d.ts +12 -0
  199. package/dist/wiring/measure-idiom-role-coverage.d.ts.map +1 -0
  200. package/dist/wiring/measure-idiom-role-coverage.js +20 -0
  201. package/dist/wiring/measure-registration-roles.d.ts +25 -0
  202. package/dist/wiring/measure-registration-roles.d.ts.map +1 -0
  203. package/dist/wiring/measure-registration-roles.js +124 -0
  204. package/dist/wiring/plan-wiring-fix.js +2 -2
  205. package/dist/wiring/registration-graph.d.ts +12 -0
  206. package/dist/wiring/registration-graph.d.ts.map +1 -1
  207. package/dist/wiring/registration-graph.js +16 -5
  208. package/dist/wiring/registration-query-verdict.d.ts +23 -0
  209. package/dist/wiring/registration-query-verdict.d.ts.map +1 -0
  210. package/dist/wiring/registration-query-verdict.js +118 -0
  211. package/dist/wiring/registry-query.d.ts +8 -0
  212. package/dist/wiring/registry-query.d.ts.map +1 -1
  213. package/dist/wiring/registry-query.js +12 -5
  214. package/dist/wiring/scan-wiring-files.d.ts +4 -2
  215. package/dist/wiring/scan-wiring-files.d.ts.map +1 -1
  216. package/dist/wiring/scan-wiring-files.js +52 -15
  217. package/dist/wiring/sink-imports.d.ts +11 -0
  218. package/dist/wiring/sink-imports.d.ts.map +1 -1
  219. package/dist/wiring/sink-imports.js +11 -2
  220. package/dist/wiring/trace-literal.d.ts +6 -0
  221. package/dist/wiring/trace-literal.d.ts.map +1 -1
  222. package/dist/wiring/trace-literal.js +5 -2
  223. package/dist/wiring/wiring-labeled-sources.d.ts +12 -0
  224. package/dist/wiring/wiring-labeled-sources.d.ts.map +1 -0
  225. package/dist/wiring/wiring-labeled-sources.js +20 -0
  226. package/package.json +2 -2
@@ -1,12 +1,40 @@
1
1
  import { type IImportContext } from '@shrkcrft/core';
2
- import { type IBoundaryRule } from '../model/boundary-rule.js';
2
+ import { type IBoundaryRule, type IBoundaryRuleValidationIssue } from '../model/boundary-rule.js';
3
+ /** One rule a file declared that failed validation — dropped from the registry, never silently. */
4
+ export interface IBoundaryRuleInvalid {
5
+ /** Position in the file's rule array. */
6
+ readonly index: number;
7
+ /** The rule's id, when it had a string one. */
8
+ readonly ruleId?: string;
9
+ /** The export the rule array came from (`default`, `rules` or `boundaries`). */
10
+ readonly exportName?: string;
11
+ readonly issues: readonly IBoundaryRuleValidationIssue[];
12
+ }
3
13
  export interface ILoadedBoundaryRulesFile {
4
14
  source: string;
5
15
  rules: IBoundaryRule[];
6
16
  warnings: string[];
17
+ /**
18
+ * Rules that failed validation (round 11, 1.3#boundary-invalid-rule). They
19
+ * used to be a warning string nobody rendered, and `check boundaries`
20
+ * reported green over the fence that had just vanished. The structured list
21
+ * lets every surface report them as ERRORED rules — never evaluated.
22
+ */
23
+ invalid: IBoundaryRuleInvalid[];
24
+ /** The file could not be imported, or exported no rule array. Nothing in it was evaluated. */
25
+ loadError?: string;
26
+ /** The file does not exist. */
27
+ missing?: boolean;
7
28
  }
8
29
  export interface ILoadBoundaryRulesOptions {
9
30
  importContext?: IImportContext;
31
+ /**
32
+ * The pack that contributed the file (round 13): stamped onto every
33
+ * `expectEmpty` marker of its rules (`stampUnitMarks`), so a pack marker that
34
+ * went live is reported as INFO and never fails the consumer, who cannot edit
35
+ * it. Unset for local rules and `--rule-file` / `--diff-against` candidates.
36
+ */
37
+ packageName?: string;
10
38
  }
11
39
  export declare function loadBoundaryRulesFromFile(absPath: string, options?: ILoadBoundaryRulesOptions): Promise<ILoadedBoundaryRulesFile>;
12
40
  //# sourceMappingURL=load-boundary-rules.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"load-boundary-rules.d.ts","sourceRoot":"","sources":["../../src/registry/load-boundary-rules.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAErF,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,yBAAyB;IACxC,aAAa,CAAC,EAAE,cAAc,CAAC;CAChC;AAED,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC,CA0CnC"}
1
+ {"version":3,"file":"load-boundary-rules.d.ts","sourceRoot":"","sources":["../../src/registry/load-boundary-rules.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAA8B,MAAM,gBAAgB,CAAC;AACjF,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,4BAA4B,EAClC,MAAM,2BAA2B,CAAC;AAInC,mGAAmG;AACnG,MAAM,WAAW,oBAAoB;IACnC,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,gFAAgF;IAChF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,SAAS,4BAA4B,EAAE,CAAC;CAC1D;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;OAKG;IACH,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,8FAA8F;IAC9F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC,CAwEnC"}
@@ -1,14 +1,17 @@
1
1
  import { existsSync } from 'node:fs';
2
- import { safeImport } from '@shrkcrft/core';
3
- import { validateBoundaryRule } from "../model/boundary-rule.js";
2
+ import { safeImport, unitProblemsOf } from '@shrkcrft/core';
3
+ import { validateBoundaryRule, } from "../model/boundary-rule.js";
4
+ import { normalizeBoundaryRule } from "../model/normalize-boundary-rule.js";
4
5
  export async function loadBoundaryRulesFromFile(absPath, options = {}) {
5
6
  const out = {
6
7
  source: absPath,
7
8
  rules: [],
8
9
  warnings: [],
10
+ invalid: [],
9
11
  };
10
12
  if (!existsSync(absPath)) {
11
13
  out.warnings.push(`boundary rules file not found: ${absPath}`);
14
+ out.missing = true;
12
15
  return out;
13
16
  }
14
17
  const result = options.importContext
@@ -21,20 +24,47 @@ export async function loadBoundaryRulesFromFile(absPath, options = {}) {
21
24
  ? 'timed out loading boundary rules from'
22
25
  : 'failed to load boundary rules from';
23
26
  out.warnings.push(`${label} ${absPath}: ${result.error.message}`);
27
+ out.loadError = result.error.message;
24
28
  return out;
25
29
  }
26
30
  const candidates = pickArray(result.module.default) ??
27
31
  pickArray(result.module.rules) ??
28
- pickArray(result.module.boundaries) ??
29
- [];
30
- for (const c of candidates) {
32
+ pickArray(result.module.boundaries);
33
+ if (candidates === null) {
34
+ // A listed rule file that exports no array loads NOTHING — the same silent
35
+ // vanishing act as an invalid rule, one level up.
36
+ const message = 'exports no rule array (expected a default export, `rules` or `boundaries` array)';
37
+ out.warnings.push(`${absPath}: ${message}`);
38
+ out.loadError = message;
39
+ return out;
40
+ }
41
+ // The export the rule array came from, so every rejection surface prints
42
+ // `(default[1])` — one wording with every other contribution kind.
43
+ const exportName = pickArray(result.module.default) !== null
44
+ ? 'default'
45
+ : pickArray(result.module.rules) !== null
46
+ ? 'rules'
47
+ : 'boundaries';
48
+ candidates.forEach((c, index) => {
31
49
  const v = validateBoundaryRule(c);
50
+ const id = c?.id;
32
51
  if (!v.valid) {
33
52
  out.warnings.push(`${absPath}: skipping invalid boundary rule (${v.issues.map((i) => i.field).join(', ')})`);
34
- continue;
53
+ out.invalid.push({ index, exportName, ...(typeof id === 'string' ? { ruleId: id } : {}), issues: v.issues });
54
+ return;
35
55
  }
36
- out.rules.push(c);
37
- }
56
+ // Round 13: the LOADED rule — plain string lists plus the expectEmptyUnits
57
+ // ledger, each marker stamped with the contributing pack. Validation above
58
+ // ran the same parser, so this cannot fail for a valid rule; if it ever
59
+ // did, the rule is an errored rule, never a crash.
60
+ const n = normalizeBoundaryRule(c, options.packageName);
61
+ if (!n.ok) {
62
+ const issues = unitProblemsOf(n.error).map((message) => ({ field: '<markers>', message }));
63
+ out.invalid.push({ index, exportName, ...(typeof id === 'string' ? { ruleId: id } : {}), issues });
64
+ return;
65
+ }
66
+ out.rules.push(n.value);
67
+ });
38
68
  return out;
39
69
  }
40
70
  function pickArray(v) {
@@ -1,3 +1,5 @@
1
+ import { type IGlobList } from '@shrkcrft/core';
2
+ import type { IGlobUnitMeasure } from './i-glob-unit-measure.js';
1
3
  /**
2
4
  * Minimal glob matcher tuned for the patterns boundary rules use:
3
5
  * - `**` matches any number of path segments (including zero)
@@ -7,7 +9,95 @@
7
9
  *
8
10
  * Patterns are matched against the literal string (file path or import
9
11
  * specifier) — no I/O, no resolution. The function is pure and deterministic.
12
+ *
13
+ * Compiled patterns are MEMOISED (round 11, 6.3): `matchesAny` used to
14
+ * recompile a pattern on every test, once per edge × pattern, and that was the
15
+ * dominant cost of a boundary run at scale (1.5 s → 0.09 s at 300 rules). The
16
+ * returned RegExp has no `g`/`y` flag, so sharing one instance is stateless.
17
+ * The cache is bounded ({@link GLOB_REGEX_CACHE_LIMIT}) and simply cleared when
18
+ * full — a pattern set larger than that is recompiled, never wrong.
10
19
  */
11
20
  export declare function globToRegex(pattern: string): RegExp;
21
+ /** The most compiled globs {@link globToRegex} keeps before it starts over. */
22
+ export declare const GLOB_REGEX_CACHE_LIMIT = 10000;
23
+ /** How many compiled globs are memoised right now (bounded by {@link GLOB_REGEX_CACHE_LIMIT}). */
24
+ export declare function globRegexCacheSize(): number;
25
+ /**
26
+ * The raw OR primitive: does ANY pattern match `value`? A `!` is a literal
27
+ * character here, so it is right for import specifiers and plain pattern sets,
28
+ * and WRONG for a user-written glob list — use {@link globListSelects} there.
29
+ */
12
30
  export declare function matchesAny(value: string, patterns: readonly string[]): boolean;
31
+ /**
32
+ * A list's inclusion globs and negations (`!` stripped), through core's one
33
+ * parser. Memoised by the list's TEXT (its JSON, so no two lists share a key),
34
+ * never by array identity — a caller that builds a list and then pushes to it
35
+ * must not read a stale parse.
36
+ */
37
+ export declare function globListParts(globs: readonly string[]): IGlobList;
38
+ /**
39
+ * THE scope test for a user-written glob list, on every gate plane: `path` is
40
+ * selected iff one of the list's inclusion globs matches it and none of its
41
+ * negations does.
42
+ *
43
+ * Order-independent (a later glob never re-includes) and LIST-LOCAL: a
44
+ * negation subtracts from its own list's positive set only. That is why a walk
45
+ * is never negation-aware ({@link globListWalkGlobs}): planes walk the UNION of
46
+ * many lists once, and a negation applied there would delete rule A's `!x`
47
+ * from rule B's scope. Selection happens per list, after the walk.
48
+ */
49
+ export declare function globListSelects(path: string, globs: readonly string[]): boolean;
50
+ /**
51
+ * The globs a WALK over `globs` must match: the inclusion globs only. A walk
52
+ * is a positive union primitive (see {@link globListSelects}); what a list's
53
+ * negations remove is decided per list, over what the walk returned.
54
+ */
55
+ export declare function globListWalkGlobs(globs: readonly string[]): readonly string[];
56
+ /**
57
+ * What each glob of ONE list did to `paths`, in first-seen order (a glob listed
58
+ * twice is measured once). See {@link IGlobUnitMeasure}: an inclusion glob's
59
+ * `effective` count is what it selected that survives the list's negations; a
60
+ * negation's is what it removed from the list's positive set.
61
+ *
62
+ * `paths` may be a wider walk than this list's own (a union over many rules):
63
+ * only the paths one of THIS list's inclusion globs matches are its positive
64
+ * set, so another list's files never count as excluded here.
65
+ */
66
+ export declare function measureGlobList(paths: readonly string[], globs: readonly string[]): readonly IGlobUnitMeasure[];
67
+ /**
68
+ * Could `glob` match some path strictly BENEATH directory `dir` (project-
69
+ * relative, `/`-separated; a trailing `/` is optional and `./` is the root)?
70
+ *
71
+ * The question an unlistable directory asks: its files were never enumerated,
72
+ * so a rule is in front of it whenever a path under it COULD be in the rule's
73
+ * scope. Segment-wise and deliberately one-directional: a `**` absorbs the
74
+ * rest, so a false "yes" is possible (a gap reported that a full listing might
75
+ * have ruled out) and a false "no" is not — `src/app/*.ts` cannot reach
76
+ * `src/app/sub/`, `src/app/**` and `**\/*.ts` can.
77
+ */
78
+ export declare function globMayMatchUnder(glob: string, dir: string): boolean;
79
+ /**
80
+ * Does `glob` match EVERY path beneath directory `dir`? True for `**`, and for
81
+ * a glob ending in `/**` whose base matches the directory or one of its
82
+ * ancestors (`src/generated/**` covers `src/generated/` and
83
+ * `src/generated/deep/`). The test an exemption must pass to take an
84
+ * unlistable directory out of a rule's scope: exempting PART of it leaves the
85
+ * rest a gap. Conservative — any other shape answers `false`.
86
+ */
87
+ export declare function globCoversAllUnder(glob: string, dir: string): boolean;
88
+ /**
89
+ * How many of `paths` each glob of ONE list effectively counts for, keyed by
90
+ * glob in first-seen order (a glob listed twice is counted once): an inclusion
91
+ * glob's selected files (those surviving the list's negations), a negation's
92
+ * EXCLUDED files. The `glob → effective` projection of {@link measureGlobList}.
93
+ *
94
+ * `globListSelects` collapses a glob LIST into one yes/no, which is right for
95
+ * "is this file in scope" and exactly wrong for "is every glob still live": one
96
+ * glob of a rule's `files[]` can match nothing after a directory rename while
97
+ * its siblings keep the rule connected, and the rule reads green forever. A
98
+ * zero here is that dead unit — and, since round 12, a negation is judged by
99
+ * what it EXCLUDES, so a load-bearing `!**\/*.spec.ts` is never a zero. Pure —
100
+ * callers pass the file list their own walk produced.
101
+ */
102
+ export declare function countMatchesPerGlob(paths: readonly string[], globs: readonly string[]): ReadonlyMap<string, number>;
13
103
  //# sourceMappingURL=glob.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../../src/scan/glob.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAmCnD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAK9E"}
1
+ {"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../../src/scan/glob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC9E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAI7C,kGAAkG;AAClG,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAuCD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAK9E;AAUD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CASjE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAI/E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAE7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,gBAAgB,EAAE,CA2B/G;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAapE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAWrE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAE7B"}
package/dist/scan/glob.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { isNegatedGlob, parseGlobList } from '@shrkcrft/core';
1
2
  /**
2
3
  * Minimal glob matcher tuned for the patterns boundary rules use:
3
4
  * - `**` matches any number of path segments (including zero)
@@ -7,8 +8,32 @@
7
8
  *
8
9
  * Patterns are matched against the literal string (file path or import
9
10
  * specifier) — no I/O, no resolution. The function is pure and deterministic.
11
+ *
12
+ * Compiled patterns are MEMOISED (round 11, 6.3): `matchesAny` used to
13
+ * recompile a pattern on every test, once per edge × pattern, and that was the
14
+ * dominant cost of a boundary run at scale (1.5 s → 0.09 s at 300 rules). The
15
+ * returned RegExp has no `g`/`y` flag, so sharing one instance is stateless.
16
+ * The cache is bounded ({@link GLOB_REGEX_CACHE_LIMIT}) and simply cleared when
17
+ * full — a pattern set larger than that is recompiled, never wrong.
10
18
  */
11
19
  export function globToRegex(pattern) {
20
+ const cached = GLOB_REGEX_CACHE.get(pattern);
21
+ if (cached)
22
+ return cached;
23
+ if (GLOB_REGEX_CACHE.size >= GLOB_REGEX_CACHE_LIMIT)
24
+ GLOB_REGEX_CACHE.clear();
25
+ const compiled = compileGlob(pattern);
26
+ GLOB_REGEX_CACHE.set(pattern, compiled);
27
+ return compiled;
28
+ }
29
+ /** The most compiled globs {@link globToRegex} keeps before it starts over. */
30
+ export const GLOB_REGEX_CACHE_LIMIT = 10_000;
31
+ const GLOB_REGEX_CACHE = new Map();
32
+ /** How many compiled globs are memoised right now (bounded by {@link GLOB_REGEX_CACHE_LIMIT}). */
33
+ export function globRegexCacheSize() {
34
+ return GLOB_REGEX_CACHE.size;
35
+ }
36
+ function compileGlob(pattern) {
12
37
  let r = '';
13
38
  for (let i = 0; i < pattern.length; i += 1) {
14
39
  const ch = pattern[i];
@@ -46,6 +71,11 @@ export function globToRegex(pattern) {
46
71
  }
47
72
  return new RegExp('^' + r + '$');
48
73
  }
74
+ /**
75
+ * The raw OR primitive: does ANY pattern match `value`? A `!` is a literal
76
+ * character here, so it is right for import specifiers and plain pattern sets,
77
+ * and WRONG for a user-written glob list — use {@link globListSelects} there.
78
+ */
49
79
  export function matchesAny(value, patterns) {
50
80
  for (const p of patterns) {
51
81
  if (globToRegex(p).test(value))
@@ -53,3 +83,178 @@ export function matchesAny(value, patterns) {
53
83
  }
54
84
  return false;
55
85
  }
86
+ /** Parsed lists that carry a negation, keyed by the list's text (bounded like the regex cache). */
87
+ const GLOB_LIST_CACHE = new Map();
88
+ function hasNegation(globs) {
89
+ for (const g of globs)
90
+ if (isNegatedGlob(g))
91
+ return true;
92
+ return false;
93
+ }
94
+ /**
95
+ * A list's inclusion globs and negations (`!` stripped), through core's one
96
+ * parser. Memoised by the list's TEXT (its JSON, so no two lists share a key),
97
+ * never by array identity — a caller that builds a list and then pushes to it
98
+ * must not read a stale parse.
99
+ */
100
+ export function globListParts(globs) {
101
+ if (!hasNegation(globs))
102
+ return { include: globs, exclude: [] };
103
+ const key = JSON.stringify(globs);
104
+ const cached = GLOB_LIST_CACHE.get(key);
105
+ if (cached)
106
+ return cached;
107
+ if (GLOB_LIST_CACHE.size >= GLOB_REGEX_CACHE_LIMIT)
108
+ GLOB_LIST_CACHE.clear();
109
+ const parsed = parseGlobList(globs);
110
+ GLOB_LIST_CACHE.set(key, parsed);
111
+ return parsed;
112
+ }
113
+ /**
114
+ * THE scope test for a user-written glob list, on every gate plane: `path` is
115
+ * selected iff one of the list's inclusion globs matches it and none of its
116
+ * negations does.
117
+ *
118
+ * Order-independent (a later glob never re-includes) and LIST-LOCAL: a
119
+ * negation subtracts from its own list's positive set only. That is why a walk
120
+ * is never negation-aware ({@link globListWalkGlobs}): planes walk the UNION of
121
+ * many lists once, and a negation applied there would delete rule A's `!x`
122
+ * from rule B's scope. Selection happens per list, after the walk.
123
+ */
124
+ export function globListSelects(path, globs) {
125
+ if (!hasNegation(globs))
126
+ return matchesAny(path, globs);
127
+ const list = globListParts(globs);
128
+ return matchesAny(path, list.include) && !matchesAny(path, list.exclude);
129
+ }
130
+ /**
131
+ * The globs a WALK over `globs` must match: the inclusion globs only. A walk
132
+ * is a positive union primitive (see {@link globListSelects}); what a list's
133
+ * negations remove is decided per list, over what the walk returned.
134
+ */
135
+ export function globListWalkGlobs(globs) {
136
+ return globListParts(globs).include;
137
+ }
138
+ /**
139
+ * What each glob of ONE list did to `paths`, in first-seen order (a glob listed
140
+ * twice is measured once). See {@link IGlobUnitMeasure}: an inclusion glob's
141
+ * `effective` count is what it selected that survives the list's negations; a
142
+ * negation's is what it removed from the list's positive set.
143
+ *
144
+ * `paths` may be a wider walk than this list's own (a union over many rules):
145
+ * only the paths one of THIS list's inclusion globs matches are its positive
146
+ * set, so another list's files never count as excluded here.
147
+ */
148
+ export function measureGlobList(paths, globs) {
149
+ const list = globListParts(globs);
150
+ const positive = list.exclude.length === 0 ? undefined : paths.filter((p) => matchesAny(p, list.include));
151
+ const selected = positive?.filter((p) => !matchesAny(p, list.exclude));
152
+ const out = [];
153
+ const seen = new Set();
154
+ for (const glob of globs) {
155
+ if (seen.has(glob))
156
+ continue;
157
+ seen.add(glob);
158
+ if (isNegatedGlob(glob)) {
159
+ const re = globToRegex(glob.slice(1));
160
+ let excluded = 0;
161
+ for (const p of positive ?? [])
162
+ if (re.test(p))
163
+ excluded += 1;
164
+ out.push({ glob, negation: true, matched: excluded, effective: excluded });
165
+ continue;
166
+ }
167
+ const re = globToRegex(glob);
168
+ let matched = 0;
169
+ for (const p of paths)
170
+ if (re.test(p))
171
+ matched += 1;
172
+ let effective = matched;
173
+ if (selected !== undefined) {
174
+ effective = 0;
175
+ for (const p of selected)
176
+ if (re.test(p))
177
+ effective += 1;
178
+ }
179
+ out.push({ glob, negation: false, matched, effective });
180
+ }
181
+ return out;
182
+ }
183
+ /** A project-relative directory path without `./` or trailing slashes (`''` is the root). */
184
+ function normaliseDirPath(dir) {
185
+ let d = dir;
186
+ if (d === '.' || d === './')
187
+ return '';
188
+ if (d.startsWith('./'))
189
+ d = d.slice(2);
190
+ return d.replace(/\/+$/, '');
191
+ }
192
+ /**
193
+ * Could `glob` match some path strictly BENEATH directory `dir` (project-
194
+ * relative, `/`-separated; a trailing `/` is optional and `./` is the root)?
195
+ *
196
+ * The question an unlistable directory asks: its files were never enumerated,
197
+ * so a rule is in front of it whenever a path under it COULD be in the rule's
198
+ * scope. Segment-wise and deliberately one-directional: a `**` absorbs the
199
+ * rest, so a false "yes" is possible (a gap reported that a full listing might
200
+ * have ruled out) and a false "no" is not — `src/app/*.ts` cannot reach
201
+ * `src/app/sub/`, `src/app/**` and `**\/*.ts` can.
202
+ */
203
+ export function globMayMatchUnder(glob, dir) {
204
+ const d = normaliseDirPath(dir);
205
+ const dirSegs = d === '' ? [] : d.split('/');
206
+ const globSegs = glob.split('/');
207
+ for (let i = 0; i < globSegs.length; i += 1) {
208
+ const segment = globSegs[i];
209
+ if (segment.includes('**'))
210
+ return true;
211
+ // The directory is consumed and the glob has segments left for a file beneath it.
212
+ if (i >= dirSegs.length)
213
+ return true;
214
+ if (!globToRegex(segment).test(dirSegs[i]))
215
+ return false;
216
+ }
217
+ // The glob ended at (or above) the directory: nothing strictly beneath it matches.
218
+ return false;
219
+ }
220
+ /**
221
+ * Does `glob` match EVERY path beneath directory `dir`? True for `**`, and for
222
+ * a glob ending in `/**` whose base matches the directory or one of its
223
+ * ancestors (`src/generated/**` covers `src/generated/` and
224
+ * `src/generated/deep/`). The test an exemption must pass to take an
225
+ * unlistable directory out of a rule's scope: exempting PART of it leaves the
226
+ * rest a gap. Conservative — any other shape answers `false`.
227
+ */
228
+ export function globCoversAllUnder(glob, dir) {
229
+ if (glob === '**')
230
+ return true;
231
+ if (!glob.endsWith('/**'))
232
+ return false;
233
+ const base = glob.slice(0, -3);
234
+ const d = normaliseDirPath(dir);
235
+ if (d === '')
236
+ return false;
237
+ const segs = d.split('/');
238
+ for (let n = segs.length; n >= 1; n -= 1) {
239
+ if (globToRegex(base).test(segs.slice(0, n).join('/')))
240
+ return true;
241
+ }
242
+ return false;
243
+ }
244
+ /**
245
+ * How many of `paths` each glob of ONE list effectively counts for, keyed by
246
+ * glob in first-seen order (a glob listed twice is counted once): an inclusion
247
+ * glob's selected files (those surviving the list's negations), a negation's
248
+ * EXCLUDED files. The `glob → effective` projection of {@link measureGlobList}.
249
+ *
250
+ * `globListSelects` collapses a glob LIST into one yes/no, which is right for
251
+ * "is this file in scope" and exactly wrong for "is every glob still live": one
252
+ * glob of a rule's `files[]` can match nothing after a directory rename while
253
+ * its siblings keep the rule connected, and the rule reads green forever. A
254
+ * zero here is that dead unit — and, since round 12, a negation is judged by
255
+ * what it EXCLUDES, so a load-bearing `!**\/*.spec.ts` is never a zero. Pure —
256
+ * callers pass the file list their own walk produced.
257
+ */
258
+ export function countMatchesPerGlob(paths, globs) {
259
+ return new Map(measureGlobList(paths, globs).map((m) => [m.glob, m.effective]));
260
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * What ONE glob of a list did to a set of paths (`measureGlobList`).
3
+ *
4
+ * An inclusion glob's `effective` count is the paths it matched that SURVIVE
5
+ * the list's negations; a negation's is the paths it REMOVED from what the
6
+ * list's inclusion globs selected. So "is this unit live?" is `effective > 0`
7
+ * for both kinds, and a negation is never "dead" merely for matching no path
8
+ * on its own — a negation matches nothing by itself; it subtracts.
9
+ */
10
+ export interface IGlobUnitMeasure {
11
+ /** The glob as written (a negation keeps its `!`). */
12
+ readonly glob: string;
13
+ readonly negation: boolean;
14
+ /**
15
+ * Inclusion glob: every path it matches. Negation: every path it matches
16
+ * among those the list's inclusion globs select (its raw reach).
17
+ */
18
+ readonly matched: number;
19
+ /** Inclusion glob: matched paths that are selected. Negation: paths it excludes. */
20
+ readonly effective: number;
21
+ }
22
+ //# sourceMappingURL=i-glob-unit-measure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i-glob-unit-measure.d.ts","sourceRoot":"","sources":["../../src/scan/i-glob-unit-measure.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oFAAoF;IACpF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,109 @@
1
+ import type { BoundaryMatchKind, ForbiddenMatchMode } from '../model/boundary-rule.js';
2
+ /**
3
+ * How a forbidden-import (or exception-target) pattern matches an import
4
+ * specifier — the pattern language of the boundary plane (round 11, 1.5).
5
+ *
6
+ * The generic glob matcher keeps `*` = "any chars except `/`" (glob.test.ts
7
+ * pins that), so a bare package pattern like `@scope/package-a` or
8
+ * `@scope/package-*` used to match ONLY the package entrypoint: every subpath
9
+ * import of the very same forbidden package (`@scope/package-a/sub`) escaped
10
+ * the fence, and the gate reported green over it. Package semantics live HERE,
11
+ * in the boundary evaluator's matcher, not in glob.ts.
12
+ *
13
+ * The rule, in one sentence: a pattern with no `**` and no trailing `/` is a
14
+ * PACKAGE pattern — it matches the specifier itself and everything under it
15
+ * (`<pattern>/**`), at a segment boundary, so `@scope/pkg` covers
16
+ * `@scope/pkg/deep` but never `@scope/pkg-legacy`. A pattern containing `**`
17
+ * already says how deep it reaches and is matched exactly as written. A rule
18
+ * that means "the entrypoint only" (barrel avoidance: forbid `lodash`, allow
19
+ * `lodash/get`) opts out with `forbiddenMatch: 'exact'`.
20
+ */
21
+ /** Does `pattern` take package semantics (entrypoint + every subpath)? */
22
+ export declare function isPackagePattern(pattern: string): boolean;
23
+ /**
24
+ * Match `specifier` against `pattern`.
25
+ *
26
+ * Returns `'exact'` when the pattern matches the specifier as written,
27
+ * `'subpath'` when only the package semantics reached it (a deeper import of a
28
+ * package the pattern names), and `null` otherwise. `mode: 'exact'` disables
29
+ * the subpath half. The verdict label travels on the violation (`matchKind`)
30
+ * so a newly-reported subpath edge explains itself.
31
+ */
32
+ export declare function matchImportPattern(specifier: string, pattern: string, mode?: ForbiddenMatchMode): BoundaryMatchKind | null;
33
+ /**
34
+ * Why `pattern` cannot mean what its author wrote in a SPECIFIER list
35
+ * (`forbiddenImports`, `allowedImports`, `exceptions[].target`) matched under
36
+ * `mode` — or `undefined` for a well-formed pattern (round 12, R12-5.2). The
37
+ * one predicate the rule validator (every local AND pack rule file) and the
38
+ * evaluator's dead-unit reach both read.
39
+ *
40
+ * - `''` names no import.
41
+ * - A leading `!` is the NEGATION syntax of every glob list in this repo, but
42
+ * a specifier list takes none: compiled as written it matches only an
43
+ * import that itself starts with `!` (a webpack inline loader), so the
44
+ * carve-out an author meant silently matched nothing. (A literal
45
+ * inline-loader specifier is still reachable: start the pattern with `?`.)
46
+ * - Under package semantics a trailing `/` is the one spelling left literal
47
+ * ({@link isPackagePattern}): it matches only an import written WITH that
48
+ * slash (`'buffer/'`, the userland-polyfill idiom) — never the package, never
49
+ * its subpaths — while the gate reported ✓ over both. `mode: 'exact'` (and
50
+ * `allowedImports`, always exact) keeps it as the literal it then plainly is.
51
+ *
52
+ * Deliberately NOT "can never match": `'buffer/'` is a real import specifier.
53
+ * The trailing-slash fix it names is lossless — the package spelling covers
54
+ * the slash spelling too (`matchImportPattern('buffer/', 'buffer')` is
55
+ * `subpath`), so following the advice never narrows a fence.
56
+ */
57
+ export declare function importPatternDefect(pattern: string, mode?: ForbiddenMatchMode): string | undefined;
58
+ /**
59
+ * Does every specifier `inner` matches also match `outer`? (round 12, R12-5.3 /
60
+ * R12-5.6). `outerMode` is how `outer` matches (the rule's `forbiddenMatch`);
61
+ * `innerMode` is how `inner` does — the same mode for a sibling forbidden
62
+ * pattern, `exact` for an `allowedImports` entry (never widened).
63
+ *
64
+ * Deliberately CONSERVATIVE — `true` only when the literal shape proves it,
65
+ * because a false "covered" would tell an author to delete a live pattern (or
66
+ * call a working allowance dead). Two proofs:
67
+ *
68
+ * 1. `inner` is a literal (no `*` / `?`) that `outer` matches — and, when
69
+ * `inner` itself widens to its subpaths (a package pattern under package
70
+ * semantics), `outer` widens too.
71
+ * 2. `outer` is a package pattern under package semantics, and it matches
72
+ * `inner`'s literal prefix up to one of its `/`: every specifier `inner`
73
+ * can match starts with that prefix plus `/`, a subpath `outer` covers.
74
+ *
75
+ * A pattern with an {@link importPatternDefect} proves nothing either way.
76
+ */
77
+ export declare function importPatternSubsumes(outer: string, inner: string, outerMode?: ForbiddenMatchMode, innerMode?: ForbiddenMatchMode): boolean;
78
+ /** Why the dead-unit judge never calls a relative specifier pattern dead — its went-live evidence and the marker refusal both quote it. */
79
+ export declare const RELATIVE_PATTERN_NEVER_DEAD = "a relative pattern is never judged dead (it cannot be judged without an importing file)";
80
+ /** Why the dead-unit judge never calls a leading-`*` specifier pattern dead — its went-live evidence and the marker refusal both quote it. */
81
+ export declare const LEADING_WILDCARD_NEVER_DEAD = "a leading wildcard could match any known package name, so it is never judged dead";
82
+ /**
83
+ * Could `pattern` match an import of package `name` (or one of its subpaths)?
84
+ * Deliberately permissive — a false "resolvable" costs a missed warning, a false
85
+ * "dead" would cry wolf on a legitimate guard. The evaluator's reach
86
+ * (`resolvedBy`) and {@link importPatternNeverJudgedDead} both read it.
87
+ */
88
+ export declare function couldMatchPackageName(pattern: string, name: string, mode: ForbiddenMatchMode): boolean;
89
+ /**
90
+ * Why the boundary dead-unit judge can NEVER call `pattern` dead, matched
91
+ * under `mode` — or `undefined` when it can (round 13, K5). Proved from the
92
+ * pattern alone, through the judge's own reach rules (`resolvedBy` in the
93
+ * evaluator): the judge calls a specifier pattern dead only when it resolves to
94
+ * nothing anywhere, and the orchestrator ALWAYS supplies the runtime builtins
95
+ * as known packages (`nodeBuiltinPackageNames`), so these always resolve:
96
+ *
97
+ * - a relative pattern (`./x`, `../legacy/**`) — never judged without an
98
+ * importing file;
99
+ * - a leading `*` — it could match any known package name, and a builtin is
100
+ * always known;
101
+ * - a pattern a runtime builtin module matches (`fs`, `node:*`,
102
+ * `fs/promises`) — that package always exists.
103
+ *
104
+ * A `{ pattern, expectEmpty: true }` marker on such a pattern could only ever
105
+ * read went-live, so the rule validator refuses it at load. A pattern with an
106
+ * {@link importPatternDefect} is answered by the defect instead (`undefined`).
107
+ */
108
+ export declare function importPatternNeverJudgedDead(pattern: string, mode?: ForbiddenMatchMode): string | undefined;
109
+ //# sourceMappingURL=import-pattern.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-pattern.d.ts","sourceRoot":"","sources":["../../src/scan/import-pattern.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAIvF;;;;;;;;;;;;;;;;;;GAkBG;AAEH,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,kBAA8B,GACnC,iBAAiB,GAAG,IAAI,CAM1B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,kBAA8B,GAAG,MAAM,GAAG,SAAS,CAoB7G;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,kBAA8B,EACzC,SAAS,GAAE,kBAA8B,GACxC,OAAO,CAgBT;AAED,2IAA2I;AAC3I,eAAO,MAAM,2BAA2B,4FACmD,CAAC;AAE5F,8IAA8I;AAC9I,eAAO,MAAM,2BAA2B,sFAC6C,CAAC;AAEtF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAkBtG;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,kBAA8B,GAAG,MAAM,GAAG,SAAS,CAWtH"}