@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
@@ -0,0 +1,450 @@
1
+ /**
2
+ * Static lint: does this regex backtrack quadratically on a BLANKED buffer?
3
+ *
4
+ * A non-`all` `scan` zone blanks comments and strings into runs of spaces
5
+ * (newlines kept, so line numbers stay true). A pattern that is fast on real
6
+ * source becomes O(run²) on such a buffer when:
7
+ *
8
+ * - (leading) an alternative's first mandatory construct — after only
9
+ * optional groups or non-restricting zero-width atoms — is a
10
+ * whitespace-consuming quantifier. Every offset of a run is a start, and
11
+ * each start re-scans the rest of the run. A start restricted to newlines
12
+ * (`(?:^|\n)`) is only a hazard when the quantifier also crosses newlines
13
+ * (`\s*`); `[ \t]*` then stops at the end of its line.
14
+ * - (adjacent) two whitespace-consuming quantifiers touch, separated only by
15
+ * optional items or whitespace-capable atoms — `\s*(?:<x>)?\s*=`, a lazy
16
+ * `[^;'"]*?` next to `\s*` — so the engine re-partitions each run between
17
+ * them.
18
+ *
19
+ * Measured: one such pattern took 3 ms on raw text and 5,402 ms on the
20
+ * blanked buffer of the same file. This cannot be caught by tests over
21
+ * ordinary source; it is caught here, from the pattern text, before it runs.
22
+ *
23
+ * Deterministic and advisory: a small tokenizer over the regex source, no
24
+ * regex engine involved. It over-approximates (lookarounds are treated as
25
+ * transparent; flags are ignored), which is the right direction for a hint.
26
+ */
27
+ export function findBlankRunHazards(source) {
28
+ const { alts } = parseAlternatives(source, 0);
29
+ const found = new Map();
30
+ const report = (h) => {
31
+ const key = `${h.shape}@${h.index}`;
32
+ const prev = found.get(key);
33
+ // A repeated group is analysed twice; keep the WIDEST reach seen, so a
34
+ // newline-crossing hazard is never under-reported as line-bounded.
35
+ if (!prev || (h.crossesNewline && !prev.crossesNewline))
36
+ found.set(key, h);
37
+ };
38
+ for (const alt of alts)
39
+ analyzeSeq(source, alt, { start: START_ANYWHERE, pending: null }, report);
40
+ return [...found.values()].sort((a, b) => a.index - b.index || a.shape.localeCompare(b.shape));
41
+ }
42
+ const NONE = { space: false, newline: false };
43
+ const BOTH = { space: true, newline: true };
44
+ /** A quantifier whose upper bound exceeds this is treated as unbounded. */
45
+ const UNBOUNDED_ABOVE = 256;
46
+ /** Where a match may still START inside a blank run, given what the pattern consumed so far. */
47
+ const START_RESTRICTED = 0;
48
+ const START_LINE = 1;
49
+ const START_ANYWHERE = 2;
50
+ // ── parser ──────────────────────────────────────────────────────────────────
51
+ function parseAlternatives(src, from) {
52
+ const alts = [[]];
53
+ let i = from;
54
+ while (i < src.length) {
55
+ const ch = src[i];
56
+ if (ch === ')')
57
+ break;
58
+ if (ch === '|') {
59
+ alts.push([]);
60
+ i += 1;
61
+ continue;
62
+ }
63
+ const item = parseItem(src, i);
64
+ i = item.i;
65
+ let node = item.node;
66
+ const q = parseQuantifier(src, i);
67
+ if (q) {
68
+ i = q.i;
69
+ if (node.t === 'atom' || node.t === 'group')
70
+ node = { ...node, min: q.min, max: q.max, end: i };
71
+ }
72
+ alts[alts.length - 1].push(node);
73
+ }
74
+ return { alts, i };
75
+ }
76
+ function parseQuantifier(src, i) {
77
+ const ch = src[i];
78
+ let min;
79
+ let max;
80
+ let j;
81
+ if (ch === '*')
82
+ [min, max, j] = [0, Infinity, i + 1];
83
+ else if (ch === '+')
84
+ [min, max, j] = [1, Infinity, i + 1];
85
+ else if (ch === '?')
86
+ [min, max, j] = [0, 1, i + 1];
87
+ else if (ch === '{') {
88
+ const m = /^\{(\d+)(?:(,)(\d*))?\}/.exec(src.slice(i));
89
+ if (!m)
90
+ return undefined; // a literal `{`
91
+ min = Number(m[1]);
92
+ max = m[2] === undefined ? min : m[3] === '' || m[3] === undefined ? Infinity : Number(m[3]);
93
+ j = i + m[0].length;
94
+ }
95
+ else
96
+ return undefined;
97
+ if (src[j] === '?')
98
+ j += 1; // lazy — same set of partitions, different order
99
+ return { min, max, i: j };
100
+ }
101
+ function parseItem(src, i) {
102
+ const ch = src[i];
103
+ if (ch === '(')
104
+ return parseGroup(src, i);
105
+ if (ch === '[') {
106
+ const cls = parseClass(src, i);
107
+ return { node: atom(cls.chars, i, cls.i), i: cls.i };
108
+ }
109
+ if (ch === '\\') {
110
+ const esc = parseEscape(src, i, false);
111
+ if (esc.anchor !== undefined) {
112
+ return { node: { t: 'anchor', restricting: esc.anchor, start: i, end: esc.i }, i: esc.i };
113
+ }
114
+ return { node: atom(esc.chars, i, esc.i), i: esc.i };
115
+ }
116
+ if (ch === '^' || ch === '$')
117
+ return { node: { t: 'anchor', restricting: true, start: i, end: i + 1 }, i: i + 1 };
118
+ if (ch === '.')
119
+ return { node: atom({ space: true, newline: false }, i, i + 1), i: i + 1 };
120
+ return { node: atom(charOf(ch.charCodeAt(0)), i, i + 1), i: i + 1 };
121
+ }
122
+ function atom(chars, start, end) {
123
+ return { t: 'atom', chars, start, end, min: 1, max: 1 };
124
+ }
125
+ function parseGroup(src, i) {
126
+ let j = i + 1;
127
+ let look = false;
128
+ if (src[j] === '?') {
129
+ const next = src[j + 1];
130
+ if (next === ':')
131
+ j += 2;
132
+ else if (next === '=' || next === '!') {
133
+ look = true;
134
+ j += 2;
135
+ }
136
+ else if (next === '<' && (src[j + 2] === '=' || src[j + 2] === '!')) {
137
+ look = true;
138
+ j += 3;
139
+ }
140
+ else if (next === '<') {
141
+ const close = src.indexOf('>', j + 2);
142
+ j = close === -1 ? src.length : close + 1;
143
+ }
144
+ else
145
+ j += 1;
146
+ }
147
+ const inner = parseAlternatives(src, j);
148
+ const end = inner.i < src.length ? inner.i + 1 : inner.i;
149
+ if (look)
150
+ return { node: { t: 'look', alts: inner.alts, start: i, end }, i: end };
151
+ return { node: { t: 'group', alts: inner.alts, start: i, end, min: 1, max: 1 }, i: end };
152
+ }
153
+ /** A character class: which blank-run characters it matches. */
154
+ function parseClass(src, i) {
155
+ let j = i + 1;
156
+ const negated = src[j] === '^';
157
+ if (negated)
158
+ j += 1;
159
+ let space = false;
160
+ let newline = false;
161
+ let first = true;
162
+ let prevCode;
163
+ while (j < src.length) {
164
+ const ch = src[j];
165
+ if (ch === ']' && !(first && false))
166
+ break;
167
+ first = false;
168
+ if (ch === '-' && prevCode !== undefined && src[j + 1] !== undefined && src[j + 1] !== ']') {
169
+ // A range `a-b`: covers a blank char when it spans it.
170
+ const hi = src[j + 1] === '\\' ? parseEscape(src, j + 1, true) : undefined;
171
+ const hiCode = hi?.code ?? src.charCodeAt(j + 1);
172
+ if (prevCode <= 0x20 && hiCode >= 0x20)
173
+ space = true;
174
+ if (prevCode <= 0x0a && hiCode >= 0x0a)
175
+ newline = true;
176
+ j = hi ? hi.i : j + 2;
177
+ prevCode = undefined;
178
+ continue;
179
+ }
180
+ if (ch === '\\') {
181
+ const esc = parseEscape(src, j, true);
182
+ if (esc.chars.space)
183
+ space = true;
184
+ if (esc.chars.newline)
185
+ newline = true;
186
+ prevCode = esc.code;
187
+ j = esc.i;
188
+ continue;
189
+ }
190
+ const code = ch.charCodeAt(0);
191
+ if (code === 0x20)
192
+ space = true;
193
+ if (code === 0x0a)
194
+ newline = true;
195
+ prevCode = code;
196
+ j += 1;
197
+ }
198
+ const end = j < src.length ? j + 1 : j;
199
+ // `[]` matches nothing; `[^]` matches everything — both fall out of the rule.
200
+ return { chars: negated ? { space: !space, newline: !newline } : { space, newline }, i: end };
201
+ }
202
+ /**
203
+ * One escape. `anchor` is set for `\b` (restricting) / `\B` (not) outside a
204
+ * class; `code` for a single-character escape (so a class range can use it).
205
+ */
206
+ function parseEscape(src, i, inClass) {
207
+ const c = src[i + 1];
208
+ if (c === undefined)
209
+ return { chars: NONE, i: i + 1 };
210
+ const j = i + 2;
211
+ switch (c) {
212
+ case 's':
213
+ case 'D':
214
+ case 'W':
215
+ return { chars: BOTH, i: j };
216
+ case 'S':
217
+ case 'd':
218
+ case 'w':
219
+ return { chars: NONE, i: j };
220
+ case 'b':
221
+ return inClass ? { chars: NONE, i: j, code: 8 } : { chars: NONE, i: j, anchor: true };
222
+ case 'B':
223
+ return { chars: NONE, i: j, anchor: false };
224
+ case 'n':
225
+ return { chars: charOf(0x0a), i: j, code: 0x0a };
226
+ case 'r':
227
+ return { chars: NONE, i: j, code: 0x0d };
228
+ case 't':
229
+ return { chars: NONE, i: j, code: 0x09 };
230
+ case 'f':
231
+ return { chars: NONE, i: j, code: 0x0c };
232
+ case 'v':
233
+ return { chars: NONE, i: j, code: 0x0b };
234
+ case '0':
235
+ return { chars: NONE, i: j, code: 0 };
236
+ case 'x': {
237
+ const m = /^[0-9a-fA-F]{2}/.exec(src.slice(j));
238
+ if (!m)
239
+ return { chars: NONE, i: j };
240
+ const code = parseInt(m[0], 16);
241
+ return { chars: charOf(code), i: j + 2, code };
242
+ }
243
+ case 'u': {
244
+ const braced = /^\{([0-9a-fA-F]+)\}/.exec(src.slice(j));
245
+ const plain = /^[0-9a-fA-F]{4}/.exec(src.slice(j));
246
+ const hex = braced?.[1] ?? plain?.[0];
247
+ if (hex === undefined)
248
+ return { chars: NONE, i: j };
249
+ const code = parseInt(hex, 16);
250
+ return { chars: charOf(code), i: j + (braced ? braced[0].length : 4), code };
251
+ }
252
+ case 'c':
253
+ return { chars: NONE, i: j + 1 };
254
+ case 'k': {
255
+ const close = src.indexOf('>', j);
256
+ return { chars: NONE, i: close === -1 ? src.length : close + 1 };
257
+ }
258
+ case 'p':
259
+ case 'P': {
260
+ const m = /^\{([^}]*)\}/.exec(src.slice(j));
261
+ const name = m?.[1] ?? '';
262
+ const end = m ? j + m[0].length : j;
263
+ if (c === 'P')
264
+ return { chars: BOTH, i: end };
265
+ if (/^(White_Space|space)$/i.test(name))
266
+ return { chars: BOTH, i: end };
267
+ if (/^(Z|Zs|Separator|Space_Separator)$/i.test(name))
268
+ return { chars: { space: true, newline: false }, i: end };
269
+ return { chars: NONE, i: end };
270
+ }
271
+ default:
272
+ if (c >= '1' && c <= '9' && !inClass) {
273
+ // A backreference — what it matches is not known statically.
274
+ let k = j;
275
+ while (k < src.length && src[k] >= '0' && src[k] <= '9')
276
+ k += 1;
277
+ return { chars: NONE, i: k };
278
+ }
279
+ return { chars: charOf(c.charCodeAt(0)), i: j, code: c.charCodeAt(0) };
280
+ }
281
+ }
282
+ function charOf(code) {
283
+ return { space: code === 0x20, newline: code === 0x0a };
284
+ }
285
+ // ── analysis ────────────────────────────────────────────────────────────────
286
+ function wsCapable(c) {
287
+ return c.space || c.newline;
288
+ }
289
+ function overlaps(a, b) {
290
+ return (a.space && b.space) || (a.newline && b.newline);
291
+ }
292
+ function unionChars(a, b) {
293
+ return { space: a.space || b.space, newline: a.newline || b.newline };
294
+ }
295
+ function joinPending(a, b) {
296
+ if (a === null)
297
+ return b;
298
+ if (b === null)
299
+ return a;
300
+ return { chars: unionChars(a.chars, b.chars), at: Math.min(a.at, b.at) };
301
+ }
302
+ function join(a, b) {
303
+ return { start: Math.max(a.start, b.start), pending: joinPending(a.pending, b.pending) };
304
+ }
305
+ /** State after consuming ONE mandatory character-matching item that is not an unbounded ws quantifier. */
306
+ function afterConsume(entry, chars) {
307
+ const start = entry.start === START_RESTRICTED
308
+ ? START_RESTRICTED
309
+ : chars.space
310
+ ? entry.start
311
+ : chars.newline
312
+ ? START_LINE
313
+ : START_RESTRICTED;
314
+ // A mandatory character that can itself be blank does NOT separate two
315
+ // whitespace quantifiers (`\s*\n\s*` still re-partitions the run).
316
+ return { start, pending: wsCapable(chars) ? entry.pending : null };
317
+ }
318
+ /**
319
+ * The blank chars a repeated group can consume when its body is made only of
320
+ * whitespace-capable atoms (and optional / non-restricting items) — i.e. when
321
+ * `(?:\s)+` behaves exactly like `\s+`. `undefined` otherwise.
322
+ */
323
+ function groupWsChars(alts) {
324
+ let acc;
325
+ for (const alt of alts) {
326
+ let sawWs = false;
327
+ let pure = true;
328
+ let chars = NONE;
329
+ for (const n of alt) {
330
+ if (n.t === 'anchor') {
331
+ if (n.restricting)
332
+ pure = false;
333
+ continue;
334
+ }
335
+ if (n.t === 'look')
336
+ continue;
337
+ if (n.t === 'atom') {
338
+ if (wsCapable(n.chars)) {
339
+ sawWs = true;
340
+ chars = unionChars(chars, n.chars);
341
+ }
342
+ else if (n.min > 0)
343
+ pure = false;
344
+ continue;
345
+ }
346
+ const inner = groupWsChars(n.alts);
347
+ if (inner) {
348
+ sawWs = true;
349
+ chars = unionChars(chars, inner);
350
+ }
351
+ else if (n.min > 0)
352
+ pure = false;
353
+ }
354
+ if (pure && sawWs)
355
+ acc = acc ? unionChars(acc, chars) : chars;
356
+ }
357
+ return acc;
358
+ }
359
+ function describe(src, start, end) {
360
+ return src.slice(start, end);
361
+ }
362
+ /** A whitespace-consuming unbounded quantifier — the construct every hazard is made of. */
363
+ function onWsQuantifier(src, chars, start, end, min, entry, report) {
364
+ const frag = describe(src, start, end);
365
+ let startMode = entry.start;
366
+ if (entry.start === START_ANYWHERE || (entry.start === START_LINE && chars.newline)) {
367
+ report({
368
+ shape: 'leading',
369
+ index: start,
370
+ fragment: frag,
371
+ message: entry.start !== START_ANYWHERE
372
+ ? `\`${frag}\` runs from every line start of a blank run across the lines below it — O(lines × run) per blanked comment`
373
+ : chars.newline
374
+ ? `\`${frag}\` can start a match at every offset of a blank run and re-scan the rest of it — O(run²) per blanked comment or string`
375
+ : `\`${frag}\` can start a match at every offset of a blanked line and re-scan the rest of that line — O(line²) per blanked line`,
376
+ // Reach: a quantifier that cannot consume `\n` stops at the end of its
377
+ // line, so its cost on a blanked buffer is per LINE, not per run.
378
+ crossesNewline: chars.newline,
379
+ ...(entry.start === START_LINE ? { fromLineStart: true } : {}),
380
+ });
381
+ // One leading finding per alternative is enough — later constructs are
382
+ // reached only through it.
383
+ startMode = START_RESTRICTED;
384
+ }
385
+ if (entry.pending && overlaps(entry.pending.chars, chars)) {
386
+ const pairFrag = describe(src, entry.pending.at, end);
387
+ report({
388
+ shape: 'adjacent',
389
+ index: entry.pending.at,
390
+ fragment: pairFrag,
391
+ message: `\`${pairFrag}\` puts two whitespace-consuming quantifiers side by side with nothing mandatory between them — the engine re-partitions every blank run between them, O(run²)`,
392
+ // The re-partitioning spans lines only when BOTH sides can take `\n`;
393
+ // overlapping on spaces alone, it is bounded by each line.
394
+ crossesNewline: entry.pending.chars.newline && chars.newline,
395
+ });
396
+ }
397
+ const mine = { chars, at: start };
398
+ // A skippable quantifier leaves the previous one still "open" as well.
399
+ const pending = min === 0 ? joinPending(entry.pending, mine) : mine;
400
+ const nextStart = startMode === START_RESTRICTED ? START_RESTRICTED : min === 0 ? startMode : startMode;
401
+ return { start: nextStart, pending };
402
+ }
403
+ function analyzeNode(src, node, entry, report) {
404
+ switch (node.t) {
405
+ case 'anchor':
406
+ return node.restricting ? { start: START_RESTRICTED, pending: null } : entry;
407
+ case 'look':
408
+ // Hazards INSIDE a lookaround still cost time; the lookaround itself
409
+ // consumes nothing, so it is transparent to its neighbours.
410
+ for (const alt of node.alts)
411
+ analyzeSeq(src, alt, { start: START_RESTRICTED, pending: null }, report);
412
+ return entry;
413
+ case 'atom': {
414
+ if (node.max > UNBOUNDED_ABOVE && wsCapable(node.chars)) {
415
+ return onWsQuantifier(src, node.chars, node.start, node.end, node.min, entry, report);
416
+ }
417
+ const after = afterConsume(entry, node.chars);
418
+ return node.min === 0 ? join(entry, after) : after;
419
+ }
420
+ case 'group': {
421
+ const repeatedWs = node.max > UNBOUNDED_ABOVE ? groupWsChars(node.alts) : undefined;
422
+ if (repeatedWs) {
423
+ // `(?:\s)+` is `\s+` — judge it as one quantifier.
424
+ for (const alt of node.alts)
425
+ analyzeSeq(src, alt, { start: START_RESTRICTED, pending: null }, report);
426
+ return onWsQuantifier(src, repeatedWs, node.start, node.end, node.min, entry, report);
427
+ }
428
+ let exit = analyzeAlternatives(src, node.alts, entry, report);
429
+ if (node.max > 1) {
430
+ // A repeated group feeds its own entry: catch a body adjacent to itself.
431
+ exit = analyzeAlternatives(src, node.alts, join(entry, exit), report);
432
+ }
433
+ return node.min === 0 ? join(entry, exit) : exit;
434
+ }
435
+ }
436
+ }
437
+ function analyzeAlternatives(src, alts, entry, report) {
438
+ let exit;
439
+ for (const alt of alts) {
440
+ const out = analyzeSeq(src, alt, entry, report);
441
+ exit = exit ? join(exit, out) : out;
442
+ }
443
+ return exit ?? entry;
444
+ }
445
+ function analyzeSeq(src, seq, entry, report) {
446
+ let state = entry;
447
+ for (const node of seq)
448
+ state = analyzeNode(src, node, state, report);
449
+ return state;
450
+ }
@@ -0,0 +1,46 @@
1
+ import type { IDeadGlobUnit } from './i-dead-glob-unit.js';
2
+ import type { IGlobListUnits } from './i-glob-list-units.js';
3
+ import type { IUnreadFile } from './unread-file.js';
4
+ /**
5
+ * THE dead-unit decision for a gate-plane glob list — every plane that reports
6
+ * a dead glob (an extraction source, a policy rule's `files`, a command
7
+ * baseline's `watchFiles` probe) reads it here, so "is this glob live?" is not
8
+ * answered three slightly different ways.
9
+ *
10
+ * `positivePaths` is what the walk returned (it may be a union wider than this
11
+ * list — only paths one of THIS list's inclusion globs matches are its positive
12
+ * set); `unread` is the reader's unread entries.
13
+ *
14
+ * - An INCLUSION glob is dead when it selects nothing that survives the list's
15
+ * negations and is in front of no unread entry the list keeps in scope:
16
+ * `matched 0 files`, or `matches only files the list's negations exclude (N)`.
17
+ * - A NEGATION is alive when it removes at least one file from its own list's
18
+ * positive set (a read file, or a matched-but-unread one), or could match
19
+ * beneath an unlistable directory the list's inclusion globs reach. Otherwise
20
+ * it is dead with `excludes nothing — none of the N file(s) the other globs
21
+ * select match it`. A negation is never judged by what it matches on its own:
22
+ * it matches nothing by itself; it subtracts. (Before round 12 every `!` read
23
+ * "matched 0 files", and `--fail-on-dead-units` failed load-bearing exclusions.)
24
+ *
25
+ * The boundary plane words its exemption `!` the same way ("exempts none of the
26
+ * N file(s) the rule's from globs match"): one liveness rule for one syntax.
27
+ */
28
+ export declare function globListUnits(positivePaths: readonly string[], unread: readonly IUnreadFile[], globs: readonly string[]): IGlobListUnits;
29
+ /**
30
+ * {@link globListUnits} over the one reader's POSITIVE walk of `globs`
31
+ * (`readMatchingFiles`, memoised under `withFileReadCache`, so a plane that
32
+ * already read the same list pays nothing twice), read and unread entries
33
+ * alike. For a plane that selects files by a bare list — a generated rule's
34
+ * `generatedGlob`, a doc-reference rule's `files` (pass its `dotDirsNamedBy`,
35
+ * so the walk is the one the rule reads). An extraction source comes through
36
+ * here too, via `sourceGlobUnits`.
37
+ *
38
+ * The engine entry normalises idempotently (round 13): a loaded list is plain
39
+ * strings and passes through; a hand-built `{ pattern, expectEmpty }` entry
40
+ * is judged as its pattern; a MALFORMED entry is never handed to a glob reader
41
+ * and never silently dropped — it is reported as a dead unit naming the problem.
42
+ */
43
+ export declare function readGlobListUnits(projectRoot: string, rawGlobs: readonly string[], excludeDirs?: ReadonlySet<string>, allowDotDirs?: ReadonlySet<string>): IGlobListUnits;
44
+ /** The dead units of one glob list — {@link globListUnits}`.dead`. */
45
+ export declare function deadGlobUnits(positivePaths: readonly string[], unread: readonly IUnreadFile[], globs: readonly string[]): readonly IDeadGlobUnit[];
46
+ //# sourceMappingURL=dead-glob-units.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dead-glob-units.d.ts","sourceRoot":"","sources":["../../src/util/dead-glob-units.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAG7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAC3B,aAAa,EAAE,SAAS,MAAM,EAAE,EAChC,MAAM,EAAE,SAAS,WAAW,EAAE,EAC9B,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,cAAc,CA0DhB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,WAAW,GAAE,WAAW,CAAC,MAAM,CAAa,EAC5C,YAAY,GAAE,WAAW,CAAC,MAAM,CAAa,GAC5C,cAAc,CAUhB;AAED,sEAAsE;AACtE,wBAAgB,aAAa,CAC3B,aAAa,EAAE,SAAS,MAAM,EAAE,EAChC,MAAM,EAAE,SAAS,WAAW,EAAE,EAC9B,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,SAAS,aAAa,EAAE,CAE1B"}
@@ -0,0 +1,116 @@
1
+ import { normalizeUnitList, unitProblemsOf } from '@shrkcrft/core';
2
+ import { globListParts, globMayMatchUnder, globToRegex, matchesAny, measureGlobList } from "../scan/glob.js";
3
+ import { isUnreadDirectory, unreadEntryMatches } from "./read-scope-coverage.js";
4
+ import { readMatchingFiles } from "./walk-files.js";
5
+ /**
6
+ * THE dead-unit decision for a gate-plane glob list — every plane that reports
7
+ * a dead glob (an extraction source, a policy rule's `files`, a command
8
+ * baseline's `watchFiles` probe) reads it here, so "is this glob live?" is not
9
+ * answered three slightly different ways.
10
+ *
11
+ * `positivePaths` is what the walk returned (it may be a union wider than this
12
+ * list — only paths one of THIS list's inclusion globs matches are its positive
13
+ * set); `unread` is the reader's unread entries.
14
+ *
15
+ * - An INCLUSION glob is dead when it selects nothing that survives the list's
16
+ * negations and is in front of no unread entry the list keeps in scope:
17
+ * `matched 0 files`, or `matches only files the list's negations exclude (N)`.
18
+ * - A NEGATION is alive when it removes at least one file from its own list's
19
+ * positive set (a read file, or a matched-but-unread one), or could match
20
+ * beneath an unlistable directory the list's inclusion globs reach. Otherwise
21
+ * it is dead with `excludes nothing — none of the N file(s) the other globs
22
+ * select match it`. A negation is never judged by what it matches on its own:
23
+ * it matches nothing by itself; it subtracts. (Before round 12 every `!` read
24
+ * "matched 0 files", and `--fail-on-dead-units` failed load-bearing exclusions.)
25
+ *
26
+ * The boundary plane words its exemption `!` the same way ("exempts none of the
27
+ * N file(s) the rule's from globs match"): one liveness rule for one syntax.
28
+ */
29
+ export function globListUnits(positivePaths, unread, globs) {
30
+ const { include, exclude } = globListParts(globs);
31
+ const measures = measureGlobList(positivePaths, globs);
32
+ const negationsAsWritten = exclude.map((n) => `!${n}`);
33
+ const unreadFilesInScope = unread.filter((u) => !isUnreadDirectory(u) && matchesAny(u.path, include));
34
+ const dead = [];
35
+ const negations = [];
36
+ let positiveSize;
37
+ for (const m of measures) {
38
+ if (!m.negation) {
39
+ if (m.effective > 0)
40
+ continue;
41
+ // Reaching an unread file, or beneath an unlistable directory, that the
42
+ // list keeps in scope is matching something it could not examine — never dead.
43
+ if (unread.some((u) => unreadEntryMatches(u, [m.glob, ...negationsAsWritten])))
44
+ continue;
45
+ // Every raw hit here was excluded — count the unread ones too, so a glob
46
+ // whose only match is an excluded over-cap file never reads "matched 0 files".
47
+ const includeRe = globToRegex(m.glob);
48
+ const hits = m.matched + unreadFilesInScope.filter((u) => includeRe.test(u.path)).length;
49
+ dead.push({
50
+ glob: m.glob,
51
+ negation: false,
52
+ matched: hits,
53
+ reason: hits === 0 ? 'matched 0 files' : `matches only files the list's negations exclude (${hits})`,
54
+ });
55
+ continue;
56
+ }
57
+ const pattern = m.glob.slice(1);
58
+ const re = globToRegex(pattern);
59
+ const excludes = m.effective + unreadFilesInScope.filter((u) => re.test(u.path)).length;
60
+ if (excludes > 0) {
61
+ negations.push({ glob: m.glob, excludes });
62
+ continue;
63
+ }
64
+ // A directory whose files were never enumerated may hold what it excludes.
65
+ const mayExcludeUnlisted = unread.some((u) => isUnreadDirectory(u) &&
66
+ include.some((g) => globMayMatchUnder(g, u.path)) &&
67
+ globMayMatchUnder(pattern, u.path));
68
+ if (mayExcludeUnlisted)
69
+ continue;
70
+ positiveSize ??=
71
+ positivePaths.filter((p) => matchesAny(p, include)).length + unreadFilesInScope.length;
72
+ dead.push({
73
+ glob: m.glob,
74
+ negation: true,
75
+ matched: positiveSize,
76
+ reason: `excludes nothing — none of the ${positiveSize} file(s) the other globs select match it`,
77
+ });
78
+ }
79
+ // Emptied by its own negations: nothing an inclusion glob matched survives,
80
+ // a live negation removed something (so the positive set was not empty), and
81
+ // no unread entry the list keeps in scope could hold a survivor.
82
+ const allExcluded = negations.length > 0 &&
83
+ !measures.some((m) => !m.negation && m.effective > 0) &&
84
+ !unread.some((u) => unreadEntryMatches(u, globs));
85
+ return { dead, negations, checked: measures.length, allExcluded, measures };
86
+ }
87
+ /**
88
+ * {@link globListUnits} over the one reader's POSITIVE walk of `globs`
89
+ * (`readMatchingFiles`, memoised under `withFileReadCache`, so a plane that
90
+ * already read the same list pays nothing twice), read and unread entries
91
+ * alike. For a plane that selects files by a bare list — a generated rule's
92
+ * `generatedGlob`, a doc-reference rule's `files` (pass its `dotDirsNamedBy`,
93
+ * so the walk is the one the rule reads). An extraction source comes through
94
+ * here too, via `sourceGlobUnits`.
95
+ *
96
+ * The engine entry normalises idempotently (round 13): a loaded list is plain
97
+ * strings and passes through; a hand-built `{ pattern, expectEmpty }` entry
98
+ * is judged as its pattern; a MALFORMED entry is never handed to a glob reader
99
+ * and never silently dropped — it is reported as a dead unit naming the problem.
100
+ */
101
+ export function readGlobListUnits(projectRoot, rawGlobs, excludeDirs = new Set(), allowDotDirs = new Set()) {
102
+ const normalized = normalizeUnitList(rawGlobs, 'files');
103
+ const globs = normalized.ok ? normalized.value.units : rawGlobs.filter((g) => typeof g === 'string');
104
+ const malformed = normalized.ok
105
+ ? []
106
+ : unitProblemsOf(normalized.error).map((reason) => ({ glob: '(malformed entry)', negation: false, matched: 0, reason }));
107
+ if (globs.length === 0)
108
+ return { dead: malformed, negations: [], checked: 0, allExcluded: false };
109
+ const matched = readMatchingFiles(projectRoot, globs, excludeDirs, allowDotDirs);
110
+ const units = globListUnits([...matched.files.keys()], matched.unread, globs);
111
+ return malformed.length > 0 ? { ...units, dead: [...units.dead, ...malformed] } : units;
112
+ }
113
+ /** The dead units of one glob list — {@link globListUnits}`.dead`. */
114
+ export function deadGlobUnits(positivePaths, unread, globs) {
115
+ return globListUnits(positivePaths, unread, globs).dead;
116
+ }
@@ -0,0 +1,31 @@
1
+ import { type IUnitLivenessInput } from '@shrkcrft/core';
2
+ import type { IGlobLivenessRequest } from './i-glob-liveness-request.js';
3
+ /**
4
+ * THE observation predicate for a gate-plane glob unit (round 13). Every
5
+ * gate-plane reporter — `gates coverage` / `gates try` / `quality`
6
+ * (`buildGateCoverage`), `policy-lint` (`runPolicyLint`), and each plane
7
+ * engine's rule-emptiness check (`settleGlobLists`) — builds its core
8
+ * `settleUnitLiveness` input here, so a glob and its marker are read one way
9
+ * everywhere:
10
+ *
11
+ * unit | exists | live
12
+ * --------------------------------------+--------+------
13
+ * inclusion glob, not dead | yes | yes (`it now matches N file(s)`)
14
+ * inclusion glob, dead, matched > 0 | yes | no (every match excluded by the list's own negations)
15
+ * inclusion glob, dead, matched 0 | no | no (its target does not exist)
16
+ * negation that excludes a file | yes | yes
17
+ * negation that excludes nothing | no | no
18
+ *
19
+ * `exists` is RAW target existence — it decides a MARKED unit (intended-empty,
20
+ * or went-live), so an acceptance never says "does not exist yet" about a glob
21
+ * whose files exist but are all excluded. `live` is the one dead-unit decision
22
+ * (`globListUnits`) negated — it decides an unmarked unit, and whether a
23
+ * went-live unit is effective. A glob in front of an unread file is not dead
24
+ * (`globListUnits`), so it reads live, never intended-empty.
25
+ *
26
+ * Advisory weight: a dead gate glob is listed and withholds the ✓, it moves
27
+ * the exit only under `--fail-on-dead-units`; record B (the acceptance) is
28
+ * emitted for any intended-empty unit.
29
+ */
30
+ export declare function globListLivenessInput(request: IGlobLivenessRequest): IUnitLivenessInput;
31
+ //# sourceMappingURL=glob-list-liveness-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob-list-liveness-input.d.ts","sourceRoot":"","sources":["../../src/util/glob-list-liveness-input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,kBAAkB,EAAyB,MAAM,gBAAgB,CAAC;AAC/G,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,oBAAoB,GAAG,kBAAkB,CAoCvF"}