arkgate 4.8.23 → 4.8.25

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 (201) hide show
  1. package/CHANGELOG.md +153 -0
  2. package/README.md +7 -5
  3. package/bin/ark-check-runtime.mjs +41 -91
  4. package/bin/ark-dashboard.mjs +56 -53
  5. package/bin/ark-layer-match.mjs +707 -121
  6. package/bin/ark-mcp-runtime.mjs +448 -169
  7. package/bin/ark-mcp.mjs +68 -4
  8. package/bin/ark-shared.mjs +42 -22
  9. package/bin/ark.mjs +65 -97
  10. package/bin/lib/adapter-contract-types.mjs +5 -0
  11. package/bin/lib/adapter-contract.mjs +15 -0
  12. package/bin/lib/adoption-stance.mjs +2 -2
  13. package/bin/lib/adr-path.mjs +3 -3
  14. package/bin/lib/agent-gates.mjs +0 -61
  15. package/bin/lib/agent-homes.mjs +12 -21
  16. package/bin/lib/agent-projection-command.mjs +9 -5
  17. package/bin/lib/ambient-state.mjs +9 -30
  18. package/bin/lib/analysis-engine.mjs +15 -11
  19. package/bin/lib/architecture-scan.mjs +61 -15
  20. package/bin/lib/ark-order-doctor.mjs +4 -29
  21. package/bin/lib/ark-order-facts.mjs +319 -17
  22. package/bin/lib/ark-order-sensors.mjs +28 -101
  23. package/bin/lib/ark-order-types.mjs +3 -2
  24. package/bin/lib/ark-run-doctor.mjs +17 -28
  25. package/bin/lib/ark-run-facts.mjs +461 -48
  26. package/bin/lib/ark-run-sensors.mjs +6 -4
  27. package/bin/lib/arkgate-pins.mjs +91 -0
  28. package/bin/lib/arkrule-file-hints.mjs +2 -47
  29. package/bin/lib/arkrules-contract.mjs +124 -133
  30. package/bin/lib/arkrules-sensors.mjs +55 -252
  31. package/bin/lib/check-args.mjs +85 -1
  32. package/bin/lib/ci-and-commands.mjs +24 -78
  33. package/bin/lib/ci-merge-boundary.mjs +1 -1
  34. package/bin/lib/class-source-scan.mjs +479 -0
  35. package/bin/lib/clone-detection.mjs +474 -0
  36. package/bin/lib/codex-home.mjs +5 -3
  37. package/bin/lib/config-contract-slices.mjs +739 -0
  38. package/bin/lib/config-contract.mjs +42 -607
  39. package/bin/lib/config-extras.mjs +16 -22
  40. package/bin/lib/config-warnings.mjs +32 -0
  41. package/bin/lib/contract-smells.mjs +33 -15
  42. package/bin/lib/core-ratchet.mjs +0 -2
  43. package/bin/lib/dashboard-cli.mjs +62 -0
  44. package/bin/lib/deep-module-coach.mjs +2 -4
  45. package/bin/lib/deploy-path.mjs +13 -38
  46. package/bin/lib/design-delta.mjs +1 -1
  47. package/bin/lib/design-smells.mjs +2 -2
  48. package/bin/lib/diagnostic-catalog.mjs +23 -9
  49. package/bin/lib/doctor-advisories.mjs +78 -8
  50. package/bin/lib/doctor-green-cite.mjs +1 -1
  51. package/bin/lib/doctor-human.mjs +1 -1
  52. package/bin/lib/doctor-next-actions.mjs +19 -4
  53. package/bin/lib/doctor-plan.mjs +9 -13
  54. package/bin/lib/duplication-io.mjs +572 -0
  55. package/bin/lib/effective-contract-load.mjs +282 -83
  56. package/bin/lib/enforcement-profiles.mjs +40 -4
  57. package/bin/lib/entry-points-io.mjs +424 -0
  58. package/bin/lib/extra-merge-teeth.mjs +36 -0
  59. package/bin/lib/field-install.mjs +38 -14
  60. package/bin/lib/first-run-help.mjs +10 -5
  61. package/bin/lib/flat-parent-importers.mjs +139 -0
  62. package/bin/lib/flat-parent-pilot.mjs +40 -100
  63. package/bin/lib/gate-files.mjs +58 -90
  64. package/bin/lib/git-change-scope.mjs +109 -0
  65. package/bin/lib/github-enforcement.mjs +166 -29
  66. package/bin/lib/golden-pattern.mjs +1 -1
  67. package/bin/lib/hook-templates.mjs +17 -15
  68. package/bin/lib/host-support-matrix.mjs +3 -1
  69. package/bin/lib/html-report-advisories.mjs +4 -0
  70. package/bin/lib/html-report-depth.mjs +2 -2
  71. package/bin/lib/html-report.mjs +2 -2
  72. package/bin/lib/import-graph-projection.mjs +112 -0
  73. package/bin/lib/import-resolve.mjs +13 -1
  74. package/bin/lib/improvement-compass-doctor.mjs +1 -24
  75. package/bin/lib/install-migrate.mjs +50 -73
  76. package/bin/lib/invariant-coverage-io.mjs +4 -3
  77. package/bin/lib/invariant-coverage.mjs +85 -11
  78. package/bin/lib/invariant-probe-cli.mjs +347 -0
  79. package/bin/lib/invariant-probe-io.mjs +165 -0
  80. package/bin/lib/invariant-probe-runner.mjs +268 -0
  81. package/bin/lib/invariant-probe-sites.mjs +174 -0
  82. package/bin/lib/invariant-probe-workspace.mjs +183 -0
  83. package/bin/lib/invariant-probe.mjs +461 -0
  84. package/bin/lib/invariant-tests-path.mjs +16 -22
  85. package/bin/lib/literal-path-drift.mjs +4 -2
  86. package/bin/lib/managed-upgrade.mjs +2 -8
  87. package/bin/lib/mcp-adoption.mjs +13 -6
  88. package/bin/lib/mcp-hook-payload.mjs +403 -63
  89. package/bin/lib/orphan-modules-io.mjs +458 -0
  90. package/bin/lib/orphan-modules.mjs +251 -0
  91. package/bin/lib/outside-importers.mjs +197 -0
  92. package/bin/lib/package-manager.mjs +60 -2
  93. package/bin/lib/parse-health.mjs +1 -1
  94. package/bin/lib/passthrough-commands.mjs +83 -0
  95. package/bin/lib/persistence-write-hint.mjs +106 -0
  96. package/bin/lib/pin-dependent-gates.mjs +117 -0
  97. package/bin/lib/policy-delta-io.mjs +219 -14
  98. package/bin/lib/post-green-path.mjs +1 -1
  99. package/bin/lib/prepare-change.mjs +115 -7
  100. package/bin/lib/prepare-write.mjs +2 -2
  101. package/bin/lib/presets.mjs +59 -73
  102. package/bin/lib/product-copy.mjs +1 -6
  103. package/bin/lib/project-root.mjs +31 -62
  104. package/bin/lib/remediation.mjs +23 -4
  105. package/bin/lib/resolved-candidate-facts.mjs +405 -117
  106. package/bin/lib/rules-inventory.mjs +22 -3
  107. package/bin/lib/rules-under-contract.mjs +247 -9
  108. package/bin/lib/scan-files.mjs +36 -4
  109. package/bin/lib/schema-validation.mjs +130 -0
  110. package/bin/lib/sensor-promote-cli.mjs +2 -11
  111. package/bin/lib/sensor-promote-io.mjs +33 -22
  112. package/bin/lib/sensor-promotion.mjs +1 -1
  113. package/bin/lib/skill-catalog-apply.mjs +1 -1
  114. package/bin/lib/skill-install.mjs +42 -38
  115. package/bin/lib/snippet-analysis.mjs +140 -6
  116. package/bin/lib/source-policy.mjs +34 -0
  117. package/bin/lib/start-install-recovery.mjs +72 -2
  118. package/bin/lib/start-preview.mjs +31 -1
  119. package/bin/lib/states-transitions-presence.mjs +3 -3
  120. package/bin/lib/status-command.mjs +35 -22
  121. package/bin/lib/status-manifest.mjs +50 -30
  122. package/bin/lib/status-transition-catalog.mjs +0 -6
  123. package/bin/lib/suggestions.mjs +3 -3
  124. package/bin/lib/team-parliament-io.mjs +76 -88
  125. package/bin/lib/team-parliament.mjs +21 -10
  126. package/bin/lib/upgrade-behavior-changes.mjs +2 -2
  127. package/bin/lib/upgrade-command.mjs +2 -24
  128. package/bin/lib/upgrade-outcome.mjs +1 -1
  129. package/bin/lib/upstream-report.mjs +21 -10
  130. package/bin/lib/violations.mjs +10 -9
  131. package/bin/lib/weakest-link.mjs +13 -0
  132. package/bin/lib/write-path-capabilities.mjs +6 -4
  133. package/dist/chunk-A5EIY3HI.cjs +5 -0
  134. package/dist/chunk-AHACEKSV.js +16 -0
  135. package/dist/chunk-SVSQVGFJ.js +5 -0
  136. package/dist/chunk-UN5EH4QV.cjs +16 -0
  137. package/dist/{configTypes-Bbf_MFGe.d.ts → configTypes-GPvvdleM.d.cts} +30 -4
  138. package/dist/configTypes-GPvvdleM.d.ts +257 -0
  139. package/dist/eslint/index.cjs +18 -5
  140. package/dist/eslint/index.d.cts +368 -0
  141. package/dist/eslint/index.d.ts +139 -156
  142. package/dist/eslint/index.js +11 -5
  143. package/dist/index.cjs +36 -35
  144. package/dist/index.d.cts +5547 -0
  145. package/dist/index.d.ts +3283 -198
  146. package/dist/index.js +36 -35
  147. package/dist/inspectorListen-6DQVJ4DC.js +1 -0
  148. package/dist/inspectorListen-KGMIQ2TF.cjs +1 -0
  149. package/dist/nestjs/index.cjs +1 -18
  150. package/dist/nestjs/index.d.cts +22 -0
  151. package/dist/nestjs/index.d.ts +1 -3
  152. package/dist/nestjs/index.js +1 -18
  153. package/dist/order/index.cjs +1 -1
  154. package/dist/order/index.d.cts +155 -0
  155. package/dist/order/index.d.ts +15 -3
  156. package/dist/order/index.js +1 -1
  157. package/dist/runtime/index.cjs +13 -30
  158. package/dist/{diagnosticCatalog-35mrwAHe.d.ts → runtime/index.d.cts} +828 -276
  159. package/dist/runtime/index.d.ts +2741 -133
  160. package/dist/runtime/index.js +13 -30
  161. package/dist/{types-Bdj32QwA.d.ts → types-CnauMQQL.d.cts} +607 -163
  162. package/dist/types-CnauMQQL.d.ts +1800 -0
  163. package/docs/README.md +3 -1
  164. package/docs/agent-guide.md +217 -16
  165. package/docs/ai-gates.md +174 -26
  166. package/docs/arkorder.md +32 -15
  167. package/docs/brownfield-adoption.md +5 -1
  168. package/docs/configuration.md +230 -31
  169. package/docs/develop.md +2 -1
  170. package/docs/diagnostics.md +212 -19
  171. package/docs/package-surface.md +35 -9
  172. package/docs/product-voice.md +7 -1
  173. package/package.json +69 -19
  174. package/schemas/ark.analysis-result.schema.json +15 -0
  175. package/schemas/ark.config.schema.json +77 -12
  176. package/schemas/ark.invariant-probe.schema.json +368 -0
  177. package/schemas/ark.resolved-candidate-facts.schema.json +1 -1
  178. package/schemas/ark.status-manifest.schema.json +22 -0
  179. package/server.json +3 -9
  180. package/templates/agent-skills/README.md +1 -1
  181. package/templates/agent-skills/ark-adopt/SKILL.md +5 -1
  182. package/templates/agent-skills/ark-contract/SKILL.md +17 -0
  183. package/templates/agent-skills/ark-coverage/SKILL.md +2 -0
  184. package/templates/agent-skills/ark-explore/SKILL.md +7 -0
  185. package/templates/agent-skills/ark-fix/SKILL.md +20 -0
  186. package/templates/agent-skills/ark-order/SKILL.md +4 -2
  187. package/templates/agent-skills/ark-place/SKILL.md +8 -0
  188. package/templates/agent-skills/ark-runtime/SKILL.md +6 -1
  189. package/templates/arkrules/PersistenceAdapters.json +1 -1
  190. package/templates/arkrules/PresentationAdapters.json +1 -1
  191. package/templates/skills/ark-adopt.md +5 -1
  192. package/templates/skills/ark-contract.md +17 -0
  193. package/templates/skills/ark-coverage.md +2 -0
  194. package/templates/skills/ark-explore.md +7 -0
  195. package/templates/skills/ark-fix.md +20 -0
  196. package/templates/skills/ark-order.md +4 -2
  197. package/templates/skills/ark-place.md +8 -0
  198. package/templates/skills/ark-runtime.md +6 -1
  199. package/bin/lib/ark-order-error.mjs +0 -18
  200. package/bin/lib/ark-order-invariants.mjs +0 -346
  201. package/dist/types-Ik_OakcI.d.ts +0 -183
@@ -0,0 +1,479 @@
1
+ /**
2
+ * GENERATED FILE — do not edit by hand.
3
+ *
4
+ * Canonical algorithm: src/domain/classSourceScan.ts
5
+ * Regenerate: node scripts/generate-cli-pure.mjs
6
+ * Drift check: node scripts/generate-cli-pure.mjs --check
7
+ *
8
+ * Pure CLI helper (bin/lib/class-source-scan.mjs). Zero Node I/O.
9
+ */
10
+
11
+ const MEMBER_MODIFIERS = new Set([
12
+ 'public',
13
+ 'private',
14
+ 'protected',
15
+ 'static',
16
+ 'async',
17
+ 'readonly',
18
+ 'abstract',
19
+ 'override',
20
+ 'declare',
21
+ 'get',
22
+ 'set',
23
+ ]);
24
+ /** Characters after which a `/` starts a regular-expression literal, not a division. */
25
+ const REGEX_PRECEDING_CHARS = new Set([
26
+ '(', ',', '=', ':', '[', '!', '&', '|', '?', '{', '}', ';', '+', '-', '*', '%', '<', '>', '~', '^',
27
+ ]);
28
+ /** Keywords after which a `/` starts a regular-expression literal. */
29
+ const REGEX_PRECEDING_KEYWORDS = new Set([
30
+ 'return', 'typeof', 'case', 'do', 'else', 'in', 'of', 'new', 'delete', 'void', 'throw',
31
+ 'instanceof', 'yield', 'await',
32
+ ]);
33
+ /**
34
+ * True when the `/` at `index` sits where an expression starts (so it opens a
35
+ * regex literal). Looks back to the previous significant character; identifiers,
36
+ * numbers, `)` and `]` mean division.
37
+ */
38
+ function slashStartsRegex(src, index) {
39
+ let j = index - 1;
40
+ while (j >= 0 && /\s/.test(src[j]))
41
+ j -= 1;
42
+ if (j < 0)
43
+ return true;
44
+ const prev = src[j];
45
+ if (/[A-Za-z0-9_$]/.test(prev)) {
46
+ let k = j;
47
+ while (k >= 0 && /[A-Za-z0-9_$]/.test(src[k]))
48
+ k -= 1;
49
+ return REGEX_PRECEDING_KEYWORDS.has(src.slice(k + 1, j + 1));
50
+ }
51
+ // `*/` ends a comment; what came before it is not examined (treated as division, rare).
52
+ if (prev === '/' && src[j - 1] === '*')
53
+ return false;
54
+ return REGEX_PRECEDING_CHARS.has(prev);
55
+ }
56
+ /**
57
+ * Index after the regex literal starting at `index` (`/…/flags`), or `index` when
58
+ * none starts there. Character classes (`[/]`) and escapes are honoured; a line
59
+ * break before the closing `/` means it was not a regex after all.
60
+ */
61
+ function skipRegexLiteral(src, index) {
62
+ if (!slashStartsRegex(src, index))
63
+ return index;
64
+ let j = index + 1;
65
+ let inClass = false;
66
+ while (j < src.length) {
67
+ const ch = src[j];
68
+ if (ch === '\n' || ch === '\r')
69
+ return index;
70
+ if (ch === '\\') {
71
+ j += 2;
72
+ continue;
73
+ }
74
+ if (inClass) {
75
+ if (ch === ']')
76
+ inClass = false;
77
+ }
78
+ else if (ch === '[') {
79
+ inClass = true;
80
+ }
81
+ else if (ch === '/') {
82
+ j += 1;
83
+ while (j < src.length && /[A-Za-z]/.test(src[j]))
84
+ j += 1;
85
+ return j;
86
+ }
87
+ j += 1;
88
+ }
89
+ return index;
90
+ }
91
+ /**
92
+ * Index after a string / comment / regex literal starting at `index`, or `index`
93
+ * when none starts there.
94
+ */
95
+ function skipStringOrComment(src, index) {
96
+ const ch = src[index];
97
+ if (ch === '/' && src[index + 1] === '/') {
98
+ const nl = src.indexOf('\n', index);
99
+ return nl === -1 ? src.length : nl;
100
+ }
101
+ if (ch === '/' && src[index + 1] === '*') {
102
+ const end = src.indexOf('*/', index + 2);
103
+ return end === -1 ? src.length : end + 2;
104
+ }
105
+ if (ch === '/')
106
+ return skipRegexLiteral(src, index);
107
+ if (ch === "'" || ch === '"' || ch === '`') {
108
+ let j = index + 1;
109
+ while (j < src.length) {
110
+ if (src[j] === '\\') {
111
+ j += 2;
112
+ continue;
113
+ }
114
+ if (src[j] === ch)
115
+ return j + 1;
116
+ j += 1;
117
+ }
118
+ return src.length;
119
+ }
120
+ return index;
121
+ }
122
+ function skipWsAndComments(src, index) {
123
+ let i = index;
124
+ while (i < src.length) {
125
+ if (/\s/.test(src[i])) {
126
+ i += 1;
127
+ continue;
128
+ }
129
+ if (src[i] === '/' && (src[i + 1] === '/' || src[i + 1] === '*')) {
130
+ i = skipStringOrComment(src, i);
131
+ continue;
132
+ }
133
+ break;
134
+ }
135
+ return i;
136
+ }
137
+ function readIdent(src, index) {
138
+ const ch = src[index];
139
+ if (!ch || !/[A-Za-z_$]/.test(ch))
140
+ return null;
141
+ let j = index + 1;
142
+ while (j < src.length && /[A-Za-z0-9_$]/.test(src[j]))
143
+ j += 1;
144
+ return { ident: src.slice(index, j), end: j };
145
+ }
146
+ /**
147
+ * Index just after the delimiter closing `openCh` at `openIndex`, or null when it
148
+ * never closes. Strings and comments are skipped. For `<`/`>`, an arrow `=>` is not
149
+ * a closing angle.
150
+ */
151
+ function skipBalanced(src, openIndex, openCh, closeCh) {
152
+ if (src[openIndex] !== openCh)
153
+ return null;
154
+ let depth = 1;
155
+ let i = openIndex + 1;
156
+ while (i < src.length && depth > 0) {
157
+ const skipped = skipStringOrComment(src, i);
158
+ if (skipped !== i) {
159
+ i = skipped;
160
+ continue;
161
+ }
162
+ const ch = src[i];
163
+ if (ch === openCh)
164
+ depth += 1;
165
+ else if (ch === closeCh && !(closeCh === '>' && src[i - 1] === '='))
166
+ depth -= 1;
167
+ i += 1;
168
+ }
169
+ return depth === 0 ? i : null;
170
+ }
171
+ export function scanClassMembers(body) {
172
+ const members = [];
173
+ let i = 0;
174
+ let truncatedAt;
175
+ while (i < body.length) {
176
+ i = skipWsAndComments(body, i);
177
+ if (i >= body.length)
178
+ break;
179
+ if (body[i] === ';') {
180
+ i += 1;
181
+ continue;
182
+ }
183
+ const modifiers = [];
184
+ let cursor = i;
185
+ while (true) {
186
+ const tok = readIdent(body, cursor);
187
+ if (!tok || !MEMBER_MODIFIERS.has(tok.ident))
188
+ break;
189
+ modifiers.push(tok.ident);
190
+ cursor = skipWsAndComments(body, tok.end);
191
+ }
192
+ if (modifiers.length === 1 && modifiers[0] === 'static' && body[cursor] === '{') {
193
+ // `static { … }` is a class static initialization block, not a member.
194
+ const afterBlock = skipBalanced(body, cursor, '{', '}');
195
+ if (afterBlock == null) {
196
+ truncatedAt = body.length;
197
+ break;
198
+ }
199
+ i = afterBlock;
200
+ continue;
201
+ }
202
+ const nameTok = readIdent(body, cursor);
203
+ if (!nameTok) {
204
+ i += 1;
205
+ continue;
206
+ }
207
+ cursor = skipWsAndComments(body, nameTok.end);
208
+ if (body[cursor] === '<') {
209
+ const afterGeneric = skipBalanced(body, cursor, '<', '>');
210
+ if (afterGeneric == null) {
211
+ truncatedAt = body.length;
212
+ break;
213
+ }
214
+ cursor = skipWsAndComments(body, afterGeneric);
215
+ }
216
+ if (body[cursor] === '(') {
217
+ const afterParen = skipBalanced(body, cursor, '(', ')');
218
+ if (afterParen == null) {
219
+ truncatedAt = body.length;
220
+ break;
221
+ }
222
+ cursor = skipWsAndComments(body, afterParen);
223
+ if (body[cursor] === ':') {
224
+ cursor += 1;
225
+ while (cursor < body.length && body[cursor] !== '{' && body[cursor] !== ';') {
226
+ const skipped = skipStringOrComment(body, cursor);
227
+ if (skipped !== cursor) {
228
+ cursor = skipped;
229
+ continue;
230
+ }
231
+ cursor += 1;
232
+ }
233
+ }
234
+ if (body[cursor] === '{') {
235
+ const afterBrace = skipBalanced(body, cursor, '{', '}');
236
+ if (afterBrace == null) {
237
+ truncatedAt = body.length;
238
+ break;
239
+ }
240
+ members.push({
241
+ name: nameTok.ident,
242
+ modifiers,
243
+ kind: 'method',
244
+ body: body.slice(cursor + 1, afterBrace - 1),
245
+ });
246
+ i = afterBrace;
247
+ continue;
248
+ }
249
+ if (body[cursor] === ';') {
250
+ i = cursor + 1;
251
+ continue;
252
+ }
253
+ i = cursor + 1;
254
+ continue;
255
+ }
256
+ let depthBrace = 0;
257
+ let depthParen = 0;
258
+ let depthBracket = 0;
259
+ while (cursor < body.length) {
260
+ const skipped = skipStringOrComment(body, cursor);
261
+ if (skipped !== cursor) {
262
+ cursor = skipped;
263
+ continue;
264
+ }
265
+ const ch = body[cursor];
266
+ if (ch === '{')
267
+ depthBrace += 1;
268
+ else if (ch === '}') {
269
+ if (depthBrace === 0)
270
+ break;
271
+ depthBrace -= 1;
272
+ }
273
+ else if (ch === '(')
274
+ depthParen += 1;
275
+ else if (ch === ')')
276
+ depthParen -= 1;
277
+ else if (ch === '[')
278
+ depthBracket += 1;
279
+ else if (ch === ']')
280
+ depthBracket -= 1;
281
+ else if (ch === ';' && depthBrace === 0 && depthParen === 0 && depthBracket === 0) {
282
+ cursor += 1;
283
+ break;
284
+ }
285
+ cursor += 1;
286
+ }
287
+ if (cursor >= body.length && (depthBrace > 0 || depthParen > 0 || depthBracket > 0)) {
288
+ // An initializer that never balances: later members are not visible.
289
+ truncatedAt = body.length;
290
+ }
291
+ members.push({
292
+ name: nameTok.ident,
293
+ modifiers,
294
+ kind: 'field',
295
+ body: '',
296
+ });
297
+ i = cursor;
298
+ }
299
+ return { members, truncatedAt };
300
+ }
301
+ /**
302
+ * Walk a class header from just after its name to the opening body brace.
303
+ * Type parameters, `extends Base<{ a: 1 }>`, `implements A, B<C>` and call
304
+ * expressions in `extends mixin(Base)` are skipped with balanced delimiters.
305
+ * Returns the index of `{`, or null when the header cannot be walked.
306
+ */
307
+ function findClassBodyBrace(content, from) {
308
+ let i = from;
309
+ while (i < content.length) {
310
+ const skipped = skipWsAndComments(content, i);
311
+ if (skipped !== i) {
312
+ i = skipped;
313
+ continue;
314
+ }
315
+ const ch = content[i];
316
+ if (ch === '{')
317
+ return i;
318
+ if (ch === ';')
319
+ return null;
320
+ if (ch === '<' || ch === '(' || ch === '[') {
321
+ const close = ch === '<' ? '>' : ch === '(' ? ')' : ']';
322
+ const after = skipBalanced(content, i, ch, close);
323
+ if (after == null)
324
+ return null;
325
+ i = after;
326
+ continue;
327
+ }
328
+ if (ch === '"' || ch === "'" || ch === '`') {
329
+ i = skipStringOrComment(content, i);
330
+ continue;
331
+ }
332
+ i += 1;
333
+ }
334
+ return null;
335
+ }
336
+ /**
337
+ * Locate class declarations. `exportedOnly` keeps the historic sensor scope:
338
+ * `export class` / `export abstract class` (not `export default class`).
339
+ * Otherwise any `class Name` in code (exported, default-exported, or local).
340
+ * Declarations inside comments and strings are ignored.
341
+ */
342
+ export function findClassDeclarations(content, options = {}) {
343
+ const out = [];
344
+ const headerRe = options.exportedOnly
345
+ ? /\bexport\s+(?:abstract\s+)?class\s+([A-Za-z_$][A-Za-z0-9_$]*)/g
346
+ : /(?:\b(export)\s+(?:default\s+)?)?(?:\babstract\s+)?\bclass\s+([A-Za-z_$][A-Za-z0-9_$]*)/g;
347
+ const code = maskStringsAndComments(content);
348
+ let match;
349
+ while ((match = headerRe.exec(code)) !== null) {
350
+ const name = (options.exportedOnly ? match[1] : match[2]);
351
+ const exported = options.exportedOnly === true || match[1] === 'export';
352
+ const brace = findClassBodyBrace(content, match.index + match[0].length);
353
+ if (brace == null) {
354
+ out.push({ name, start: match.index, exported, bodyStart: null, bodyEnd: null });
355
+ continue;
356
+ }
357
+ const after = skipBalanced(content, brace, '{', '}');
358
+ out.push({
359
+ name,
360
+ start: match.index,
361
+ exported,
362
+ bodyStart: brace + 1,
363
+ bodyEnd: after == null ? null : after - 1,
364
+ });
365
+ }
366
+ return out;
367
+ }
368
+ /** Same length as `content`; string and comment characters become spaces (newlines kept). */
369
+ function maskStringsAndComments(content) {
370
+ let out = '';
371
+ let i = 0;
372
+ while (i < content.length) {
373
+ const next = skipStringOrComment(content, i);
374
+ if (next !== i) {
375
+ out += content.slice(i, next).replace(/[^\n]/g, ' ');
376
+ i = next;
377
+ continue;
378
+ }
379
+ out += content[i];
380
+ i += 1;
381
+ }
382
+ return out;
383
+ }
384
+ /**
385
+ * Locate `namespace Name {` / `module Name {`, `const Name = {` (object literal) and
386
+ * `const Name = class … {` (class expression) declarations of `name`, so a
387
+ * `Name.member` symbol can be checked for membership in them as well as in
388
+ * `class Name`. Strings, comments, and regex literals are ignored.
389
+ */
390
+ export function findMemberContainers(content, name) {
391
+ const out = [];
392
+ if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name))
393
+ return out;
394
+ const code = maskStringsAndComments(content);
395
+ const escaped = name.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&');
396
+ const re = new RegExp(`\\b(?:(namespace|module)\\s+${escaped}\\s*\\{|(?:const|let|var)\\s+${escaped}\\s*(?::[^=;]+?)?=(?!=|>)\\s*(class\\b)?)`, 'g');
397
+ let match;
398
+ while ((match = re.exec(code)) !== null) {
399
+ let brace;
400
+ let kind;
401
+ if (match[1]) {
402
+ kind = 'namespace';
403
+ brace = match.index + match[0].length - 1;
404
+ }
405
+ else if (match[2]) {
406
+ kind = 'class-expression';
407
+ brace = findClassBodyBrace(content, match.index + match[0].length);
408
+ }
409
+ else {
410
+ kind = 'object';
411
+ const at = skipWsAndComments(content, match.index + match[0].length);
412
+ brace = content[at] === '{' ? at : null;
413
+ }
414
+ if (brace == null)
415
+ continue;
416
+ const after = skipBalanced(content, brace, '{', '}');
417
+ out.push({ kind, bodyStart: brace + 1, bodyEnd: after == null ? content.length : after - 1 });
418
+ }
419
+ return out;
420
+ }
421
+ /**
422
+ * Top-level member names of an object-literal body: `name() {}`, `async name()`,
423
+ * `*name()`, `name: …`, and shorthand `name,`. Nested braces/parens/brackets are
424
+ * skipped so a key of an inner object is not a member of the outer one.
425
+ */
426
+ export function objectLiteralMemberNames(body) {
427
+ const names = [];
428
+ let i = 0;
429
+ let expectKey = true;
430
+ while (i < body.length) {
431
+ const skipped = skipStringOrComment(body, i);
432
+ if (skipped !== i) {
433
+ i = skipped;
434
+ expectKey = false;
435
+ continue;
436
+ }
437
+ const ch = body[i];
438
+ if (ch === '{' || ch === '(' || ch === '[') {
439
+ const close = ch === '{' ? '}' : ch === '(' ? ')' : ']';
440
+ i = skipBalanced(body, i, ch, close) ?? body.length;
441
+ expectKey = false;
442
+ continue;
443
+ }
444
+ if (ch === ',') {
445
+ expectKey = true;
446
+ i += 1;
447
+ continue;
448
+ }
449
+ if (expectKey && /[A-Za-z_$]/.test(ch)) {
450
+ let tok = readIdent(body, i);
451
+ // `async name()`, `get name()`, `set name(v)`, `async *name()`: the modifier is not the key.
452
+ while (tok && (tok.ident === 'async' || tok.ident === 'get' || tok.ident === 'set')) {
453
+ let next = skipWsAndComments(body, tok.end);
454
+ if (body[next] === '*')
455
+ next = skipWsAndComments(body, next + 1);
456
+ const peek = readIdent(body, next);
457
+ if (!peek)
458
+ break;
459
+ tok = peek;
460
+ }
461
+ if (tok) {
462
+ const after = skipWsAndComments(body, tok.end);
463
+ if ('(:,<'.includes(body[after] ?? '') || after >= body.length)
464
+ names.push(tok.ident);
465
+ i = tok.end;
466
+ expectKey = false;
467
+ continue;
468
+ }
469
+ }
470
+ if (expectKey && ch === '*') {
471
+ i += 1;
472
+ continue;
473
+ }
474
+ if (!/\s/.test(ch))
475
+ expectKey = false;
476
+ i += 1;
477
+ }
478
+ return names;
479
+ }