@backbencher/brain 0.0.0-stage → 0.1.1

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 (319) hide show
  1. package/CHANGELOG.md +298 -0
  2. package/EMBEDDINGS.md +355 -0
  3. package/LICENSE +21 -0
  4. package/README.md +415 -2
  5. package/bin/bb +13 -0
  6. package/dist/analysis-exclusions-7ZJDXOVZ.js +35 -0
  7. package/dist/brain-index-health-Q5APTJQL.js +17 -0
  8. package/dist/brain-relationship-enrichment-MYR2AM4F.js +558 -0
  9. package/dist/brain.sql-EIM4WS4D.js +29 -0
  10. package/dist/chunk-22V37YTK.js +1758 -0
  11. package/dist/chunk-2BSKD7O3.js +40 -0
  12. package/dist/chunk-2DE27DVC.js +1314 -0
  13. package/dist/chunk-2RRBMNBQ.js +322 -0
  14. package/dist/chunk-2WXYNLBA.js +29 -0
  15. package/dist/chunk-3CTFIJJB.js +17 -0
  16. package/dist/chunk-3ZTOHHSW.js +2104 -0
  17. package/dist/chunk-4H22TLOT.js +543 -0
  18. package/dist/chunk-5BVAH26D.js +340 -0
  19. package/dist/chunk-62DHLYW6.js +3427 -0
  20. package/dist/chunk-7AN7U5IZ.js +371 -0
  21. package/dist/chunk-7DRPNVIN.js +782 -0
  22. package/dist/chunk-7MBYT777.js +174 -0
  23. package/dist/chunk-AHDWGM7Q.js +236 -0
  24. package/dist/chunk-B5GEAAYF.js +818 -0
  25. package/dist/chunk-BEIZZ73B.js +1479 -0
  26. package/dist/chunk-BQ3OVQ3P.js +125 -0
  27. package/dist/chunk-D2Q4Y47A.js +53 -0
  28. package/dist/chunk-DIYAGQJL.js +34 -0
  29. package/dist/chunk-DWKXZ6IF.js +336 -0
  30. package/dist/chunk-DYM4FQEE.js +90 -0
  31. package/dist/chunk-ENW4BTBA.js +930 -0
  32. package/dist/chunk-EWMWK7WN.js +75 -0
  33. package/dist/chunk-EY5ZZUUA.js +626 -0
  34. package/dist/chunk-GBI7V7KY.js +69 -0
  35. package/dist/chunk-GEFFO6O2.js +152 -0
  36. package/dist/chunk-HJEWO5EV.js +247 -0
  37. package/dist/chunk-HQSPJDHI.js +613 -0
  38. package/dist/chunk-KAET47ZL.js +125 -0
  39. package/dist/chunk-KCDECRJY.js +440 -0
  40. package/dist/chunk-L5TDYKYK.js +76 -0
  41. package/dist/chunk-MLKGABMK.js +9 -0
  42. package/dist/chunk-NO3U3FJQ.js +201 -0
  43. package/dist/chunk-OWISBAOF.js +61 -0
  44. package/dist/chunk-QKTRECYS.js +1119 -0
  45. package/dist/chunk-QLK6ED4Q.js +235 -0
  46. package/dist/chunk-R2GW4F6R.js +19 -0
  47. package/dist/chunk-RCMTFSEF.js +87 -0
  48. package/dist/chunk-RM3PY6TG.js +376 -0
  49. package/dist/chunk-ROMM6KMC.js +433 -0
  50. package/dist/chunk-SGGCSHAX.js +12050 -0
  51. package/dist/chunk-SK6YVHFZ.js +211 -0
  52. package/dist/chunk-SM7RFQRO.js +214 -0
  53. package/dist/chunk-UGR4QIGS.js +67 -0
  54. package/dist/chunk-UQNQZXHY.js +711 -0
  55. package/dist/chunk-UUNHHX4M.js +13 -0
  56. package/dist/chunk-VL52PL2T.js +71 -0
  57. package/dist/chunk-WIG36E67.js +196 -0
  58. package/dist/chunk-XHK3WWGB.js +7436 -0
  59. package/dist/chunk-XTQM2IP3.js +2863 -0
  60. package/dist/chunk-ZE2LG76D.js +1296 -0
  61. package/dist/chunk-ZETTGPAW.js +6950 -0
  62. package/dist/cli/index.js +37 -0
  63. package/dist/config-HZAAYDP3.js +10 -0
  64. package/dist/content-search-7XIP555Q.js +593 -0
  65. package/dist/database-76RP44JG.js +9 -0
  66. package/dist/doctor-ANGETYMN.js +395 -0
  67. package/dist/edit-engine-CG72LDZD.js +51 -0
  68. package/dist/engine-BKEOGN7N.js +11 -0
  69. package/dist/engine-VIW6NSKO.js +18 -0
  70. package/dist/fingerprint-OBLIMZLP.js +33 -0
  71. package/dist/framework-intelligence-3LORTPOX.js +860 -0
  72. package/dist/freshness-3K6VU5OT.js +174 -0
  73. package/dist/graph-engine-HBSQNPTB.js +21 -0
  74. package/dist/graph_request-Q6NKB6N2.js +350 -0
  75. package/dist/impact_request-LYP672WE.js +68 -0
  76. package/dist/index-space-IUUN5WYV.js +15 -0
  77. package/dist/index-status-7OVGYSGW.js +34 -0
  78. package/dist/index.js +159 -0
  79. package/dist/instance-FO6QZRLH.js +19 -0
  80. package/dist/language-detection-DFCAU5DZ.js +11 -0
  81. package/dist/lsp/server.js +4301 -0
  82. package/dist/mcp/server.js +132 -0
  83. package/dist/onboard-DIQZV2NW.js +161 -0
  84. package/dist/override-companion-pass-XR7MHTP7.js +117 -0
  85. package/dist/parallel-indexer-FK2XKZXY.js +88 -0
  86. package/dist/pool-worker.js +1153 -0
  87. package/dist/project-health-6XPENVAA.js +94 -0
  88. package/dist/project-index-5SDPHITE.js +57 -0
  89. package/dist/project-registry-ZJ3DVUGO.js +355 -0
  90. package/dist/real-bash-analyzer-EJAU5LW5.js +2201 -0
  91. package/dist/real-c-analyzer-HBD3FNNH.js +5942 -0
  92. package/dist/real-clean-analyzer-4EEZLPV5.js +3470 -0
  93. package/dist/real-clojure-analyzer-Q55EBOO2.js +2818 -0
  94. package/dist/real-cpp-analyzer-IVVOD25R.js +4101 -0
  95. package/dist/real-csharp-analyzer-ZBNLNJZJ.js +3298 -0
  96. package/dist/real-css-analyzer-6XRQLUN7.js +15 -0
  97. package/dist/real-dart-analyzer-OLR72EFW.js +3944 -0
  98. package/dist/real-elixir-analyzer-MQWGAH2I.js +2192 -0
  99. package/dist/real-erlang-analyzer-7BVPQEHO.js +1731 -0
  100. package/dist/real-fsharp-analyzer-C2UPFNOA.js +2390 -0
  101. package/dist/real-go-analyzer-UKGI43YS.js +4126 -0
  102. package/dist/real-graphql-analyzer-VE2EEFII.js +1442 -0
  103. package/dist/real-groovy-analyzer-W26VACFW.js +2285 -0
  104. package/dist/real-haskell-analyzer-5CVVAJRX.js +2533 -0
  105. package/dist/real-hcl-analyzer-5ZT4U43N.js +725 -0
  106. package/dist/real-html-analyzer-JWNX3C6I.js +1759 -0
  107. package/dist/real-java-analyzer-GU42VBGQ.js +4677 -0
  108. package/dist/real-javascript-analyzer-QA4QOJ6W.js +14 -0
  109. package/dist/real-json-analyzer-MPA6PRXL.js +1640 -0
  110. package/dist/real-julia-analyzer-22CWYQMN.js +2085 -0
  111. package/dist/real-kotlin-analyzer-QDZJLXXU.js +3956 -0
  112. package/dist/real-lua-analyzer-G7NHLYKS.js +1957 -0
  113. package/dist/real-makefile-analyzer-OCY3GT5N.js +920 -0
  114. package/dist/real-markdown-analyzer-TEJK2JXJ.js +1215 -0
  115. package/dist/real-matlab-analyzer-Q5SWLN7Z.js +480 -0
  116. package/dist/real-nim-analyzer-YLXSK563.js +3603 -0
  117. package/dist/real-objective-c-analyzer-D23ACC2R.js +1518 -0
  118. package/dist/real-perl-analyzer-WWTRHYDT.js +1865 -0
  119. package/dist/real-php-analyzer-JNKJHTCL.js +9368 -0
  120. package/dist/real-powershell-analyzer-XRSNQERV.js +2915 -0
  121. package/dist/real-protobuf-analyzer-3OKUCZ7T.js +867 -0
  122. package/dist/real-python-analyzer-VXAFWI72.js +4880 -0
  123. package/dist/real-r-analyzer-ZGAIDVI4.js +2596 -0
  124. package/dist/real-ruby-analyzer-X2KTLVBH.js +2429 -0
  125. package/dist/real-rust-analyzer-OFFHPL2A.js +3359 -0
  126. package/dist/real-scala-analyzer-TSTW27J6.js +3293 -0
  127. package/dist/real-scss-analyzer-AVWYPSOQ.js +1515 -0
  128. package/dist/real-solidity-analyzer-UCI2KWUB.js +2608 -0
  129. package/dist/real-sql-analyzer-F77ZMQXR.js +3840 -0
  130. package/dist/real-svelte-analyzer-SF7NRUO5.js +2532 -0
  131. package/dist/real-swift-analyzer-MN5QT3QN.js +2311 -0
  132. package/dist/real-toml-analyzer-66L7BDCQ.js +929 -0
  133. package/dist/real-typescript-analyzer-6KEMVDWQ.js +14 -0
  134. package/dist/real-vb-analyzer-VICVPOP7.js +3145 -0
  135. package/dist/real-vba-analyzer-HXLTTHKM.js +466 -0
  136. package/dist/real-vue-analyzer-QXR56CFV.js +2680 -0
  137. package/dist/real-xml-analyzer-YL465ZJW.js +2405 -0
  138. package/dist/real-yaml-analyzer-SUBCBNSI.js +3528 -0
  139. package/dist/real-zig-analyzer-HIQLZMGY.js +2430 -0
  140. package/dist/regex-search-NJKHMZNO.js +239 -0
  141. package/dist/rules-ACHPB7QU.js +152 -0
  142. package/dist/scoped-path-MJYWJZQX.js +11 -0
  143. package/dist/setup-show-NT5ZHYNE.js +15 -0
  144. package/dist/setup-wizard-G5YYR5T6.js +24 -0
  145. package/dist/symbol-search-GZJIM2J2.js +16 -0
  146. package/dist/taint-KXUHHPIE.js +202 -0
  147. package/dist/taint-analyzer-N7YLZU3N.js +163 -0
  148. package/dist/tool-handlers-OJDC35DI.js +72 -0
  149. package/dist/tool-registry-BPEJNSZG.js +17 -0
  150. package/dist/tool-validation-Q57YMMBX.js +32 -0
  151. package/dist/type-inference-QMT2RMLV.js +13 -0
  152. package/dist/vector-binary-LMY4QGAN.js +19 -0
  153. package/dist/version-ABP2TUL2.js +9 -0
  154. package/dist/wasm-NMWO3WXY.js +16 -0
  155. package/docs/EMBEDDINGS.md +132 -0
  156. package/docs/README.md +178 -0
  157. package/docs/data/m10-baseline.json +602 -0
  158. package/docs/data/m10-benchmark-baseline.json +57 -0
  159. package/docs/data/m10-benchmark-last-run.json +127 -0
  160. package/docs/data/m10-final-audit.json +215 -0
  161. package/docs/data/m12-final-audit.json +138 -0
  162. package/docs/data/m12-surface-matrix.json +50 -0
  163. package/docs/data/public-tools.json +1902 -0
  164. package/docs/indexing/DEAD-ASSETS.md +88 -0
  165. package/docs/indexing/INDEXER-REVIEW.md +634 -0
  166. package/docs/indexing/PERFORMANCE-FINDINGS.md +106 -0
  167. package/docs/indexing/README.md +427 -0
  168. package/docs/indexing/discovery-matrix.md +41 -0
  169. package/docs/indexing/embeddings.md +325 -0
  170. package/docs/indexing/environment-variables.md +77 -0
  171. package/docs/indexing/performance.md +323 -0
  172. package/docs/indexing/resume.md +345 -0
  173. package/docs/indexing/troubleshooting.md +391 -0
  174. package/docs/man/bb.1 +190 -0
  175. package/docs/tools/bb_analyze.md +147 -0
  176. package/docs/tools/bb_annotate.md +110 -0
  177. package/docs/tools/bb_annotations.md +106 -0
  178. package/docs/tools/bb_ask.md +101 -0
  179. package/docs/tools/bb_cross_language.md +103 -0
  180. package/docs/tools/bb_doc.md +169 -0
  181. package/docs/tools/bb_edit.md +185 -0
  182. package/docs/tools/bb_graph.md +169 -0
  183. package/docs/tools/bb_health.md +156 -0
  184. package/docs/tools/bb_impact.md +117 -0
  185. package/docs/tools/bb_index.md +108 -0
  186. package/docs/tools/bb_migrate.md +135 -0
  187. package/docs/tools/bb_patterns.md +140 -0
  188. package/docs/tools/bb_search.md +150 -0
  189. package/docs/tools/bb_security.md +100 -0
  190. package/docs/tools/bb_visualize.md +184 -0
  191. package/grammars/README.md +57 -0
  192. package/grammars/THIRD-PARTY-NOTICES.md +50 -0
  193. package/grammars/tree-sitter-c/LICENSE +21 -0
  194. package/grammars/tree-sitter-clojure/LICENSE.md +21 -0
  195. package/grammars/tree-sitter-cpp/LICENSE +21 -0
  196. package/grammars/tree-sitter-csharp/LICENSE +21 -0
  197. package/grammars/tree-sitter-css/LICENSE +21 -0
  198. package/grammars/tree-sitter-css/README.md +21 -0
  199. package/grammars/tree-sitter-dart/LICENSE +20 -0
  200. package/grammars/tree-sitter-dart-upstream/LICENSE +20 -0
  201. package/grammars/tree-sitter-dart-upstream/README.md +18 -0
  202. package/grammars/tree-sitter-elixir/LICENSE +176 -0
  203. package/grammars/tree-sitter-elixir/README.md +11 -0
  204. package/grammars/tree-sitter-fsharp/LICENSE +21 -0
  205. package/grammars/tree-sitter-fsharp/README.md +45 -0
  206. package/grammars/tree-sitter-graphql/LICENSE +21 -0
  207. package/grammars/tree-sitter-graphql/README.md +106 -0
  208. package/grammars/tree-sitter-groovy/LICENSE +21 -0
  209. package/grammars/tree-sitter-groovy/README.md +4 -0
  210. package/grammars/tree-sitter-haskell/LICENSE +21 -0
  211. package/grammars/tree-sitter-haskell/README.md +394 -0
  212. package/grammars/tree-sitter-hcl/CHANGELOG.md +102 -0
  213. package/grammars/tree-sitter-hcl/LICENSE +201 -0
  214. package/grammars/tree-sitter-hcl/README.md +39 -0
  215. package/grammars/tree-sitter-html/LICENSE +21 -0
  216. package/grammars/tree-sitter-html/README.md +21 -0
  217. package/grammars/tree-sitter-julia/LICENSE +21 -0
  218. package/grammars/tree-sitter-julia/README.md +24 -0
  219. package/grammars/tree-sitter-kotlin/LICENSE +9 -0
  220. package/grammars/tree-sitter-lua/LICENSE.md +21 -0
  221. package/grammars/tree-sitter-lua/README.md +29 -0
  222. package/grammars/tree-sitter-make/LICENSE +21 -0
  223. package/grammars/tree-sitter-matlab/LICENSE +19 -0
  224. package/grammars/tree-sitter-matlab/README.md +99 -0
  225. package/grammars/tree-sitter-nim/LICENSE +373 -0
  226. package/grammars/tree-sitter-nim/README.md +46 -0
  227. package/grammars/tree-sitter-objc/LICENSE +21 -0
  228. package/grammars/tree-sitter-objc/README.md +20 -0
  229. package/grammars/tree-sitter-perl/LICENSE +7 -0
  230. package/grammars/tree-sitter-perl/README.md +100 -0
  231. package/grammars/tree-sitter-powershell/LICENSE +21 -0
  232. package/grammars/tree-sitter-powershell/README.md +7 -0
  233. package/grammars/tree-sitter-proto/LICENSE +21 -0
  234. package/grammars/tree-sitter-proto/README.md +105 -0
  235. package/grammars/tree-sitter-r/LICENSE +9 -0
  236. package/grammars/tree-sitter-r/README.md +66 -0
  237. package/grammars/tree-sitter-ruby/LICENSE +22 -0
  238. package/grammars/tree-sitter-scss/LICENSE +21 -0
  239. package/grammars/tree-sitter-scss/README.md +17 -0
  240. package/grammars/tree-sitter-solidity/LICENSE +7 -0
  241. package/grammars/tree-sitter-sql/LICENSE +21 -0
  242. package/grammars/tree-sitter-typescript/LICENSE +21 -0
  243. package/grammars/tree-sitter-vba/LICENSE +21 -0
  244. package/grammars/tree-sitter-vba/README.md +738 -0
  245. package/grammars/tree-sitter-vbnet/LICENSE +21 -0
  246. package/grammars/tree-sitter-vbnet/README.md +322 -0
  247. package/grammars/tree-sitter-xml/LICENSE +19 -0
  248. package/grammars/tree-sitter-yaml/LICENSE +20 -0
  249. package/grammars/tree-sitter-yaml/README.md +21 -0
  250. package/package.json +184 -4
  251. package/wasm/dart.wasm +0 -0
  252. package/wasm/tree-sitter-bash.wasm +0 -0
  253. package/wasm/tree-sitter-c.wasm +0 -0
  254. package/wasm/tree-sitter-c_sharp.wasm +0 -0
  255. package/wasm/tree-sitter-clean.wasm +0 -0
  256. package/wasm/tree-sitter-clojure.wasm +0 -0
  257. package/wasm/tree-sitter-core_schema.wasm +0 -0
  258. package/wasm/tree-sitter-cpp.wasm +0 -0
  259. package/wasm/tree-sitter-css.wasm +0 -0
  260. package/wasm/tree-sitter-dart.wasm +0 -0
  261. package/wasm/tree-sitter-elisp.wasm +0 -0
  262. package/wasm/tree-sitter-elixir.wasm +0 -0
  263. package/wasm/tree-sitter-elm.wasm +0 -0
  264. package/wasm/tree-sitter-embedded_template.wasm +0 -0
  265. package/wasm/tree-sitter-erlang.wasm +0 -0
  266. package/wasm/tree-sitter-fsharp.wasm +0 -0
  267. package/wasm/tree-sitter-fsharp_signature.wasm +0 -0
  268. package/wasm/tree-sitter-go.wasm +0 -0
  269. package/wasm/tree-sitter-graphql.wasm +0 -0
  270. package/wasm/tree-sitter-groovy.wasm +0 -0
  271. package/wasm/tree-sitter-haskell.wasm +0 -0
  272. package/wasm/tree-sitter-hcl.wasm +0 -0
  273. package/wasm/tree-sitter-html.wasm +0 -0
  274. package/wasm/tree-sitter-java.wasm +0 -0
  275. package/wasm/tree-sitter-javascript.wasm +0 -0
  276. package/wasm/tree-sitter-json.wasm +0 -0
  277. package/wasm/tree-sitter-json_schema.wasm +0 -0
  278. package/wasm/tree-sitter-julia.wasm +0 -0
  279. package/wasm/tree-sitter-kotlin.wasm +0 -0
  280. package/wasm/tree-sitter-lua.wasm +0 -0
  281. package/wasm/tree-sitter-make.wasm +0 -0
  282. package/wasm/tree-sitter-markdown.wasm +0 -0
  283. package/wasm/tree-sitter-markdown_inline.wasm +0 -0
  284. package/wasm/tree-sitter-matlab.wasm +0 -0
  285. package/wasm/tree-sitter-nim.wasm +0 -0
  286. package/wasm/tree-sitter-objc.wasm +0 -0
  287. package/wasm/tree-sitter-ocaml.wasm +0 -0
  288. package/wasm/tree-sitter-perl.wasm +0 -0
  289. package/wasm/tree-sitter-php.wasm +0 -0
  290. package/wasm/tree-sitter-php_only.wasm +0 -0
  291. package/wasm/tree-sitter-powershell.wasm +0 -0
  292. package/wasm/tree-sitter-protobuf.wasm +0 -0
  293. package/wasm/tree-sitter-python.wasm +0 -0
  294. package/wasm/tree-sitter-ql.wasm +0 -0
  295. package/wasm/tree-sitter-r.wasm +0 -0
  296. package/wasm/tree-sitter-rescript.wasm +0 -0
  297. package/wasm/tree-sitter-ruby.wasm +0 -0
  298. package/wasm/tree-sitter-rust.wasm +0 -0
  299. package/wasm/tree-sitter-scala.wasm +0 -0
  300. package/wasm/tree-sitter-scss.wasm +0 -0
  301. package/wasm/tree-sitter-solidity.wasm +0 -0
  302. package/wasm/tree-sitter-sql.wasm +0 -0
  303. package/wasm/tree-sitter-svelte.wasm +0 -0
  304. package/wasm/tree-sitter-swift.wasm +0 -0
  305. package/wasm/tree-sitter-systemrdl.wasm +0 -0
  306. package/wasm/tree-sitter-terraform.wasm +0 -0
  307. package/wasm/tree-sitter-tlaplus.wasm +0 -0
  308. package/wasm/tree-sitter-toml.wasm +0 -0
  309. package/wasm/tree-sitter-tsx.wasm +0 -0
  310. package/wasm/tree-sitter-typescript.wasm +0 -0
  311. package/wasm/tree-sitter-vba.wasm +0 -0
  312. package/wasm/tree-sitter-vbnet.wasm +0 -0
  313. package/wasm/tree-sitter-vue.wasm +0 -0
  314. package/wasm/tree-sitter-xml.wasm +0 -0
  315. package/wasm/tree-sitter-yaml.wasm +0 -0
  316. package/wasm/tree-sitter-zig.wasm +0 -0
  317. package/wasm/tree-sitter.wasm +0 -0
  318. package/wasm/tsx.wasm +0 -0
  319. package/wasm/typescript.wasm +0 -0
@@ -0,0 +1,3427 @@
1
+ import {
2
+ treeContainsNodeType
3
+ } from "./chunk-R2GW4F6R.js";
4
+ import {
5
+ ASTEngine
6
+ } from "./chunk-3ZTOHHSW.js";
7
+ import {
8
+ BINDING_SEP
9
+ } from "./chunk-KAET47ZL.js";
10
+
11
+ // src/ast/real-javascript-analyzer.ts
12
+ import * as path from "path";
13
+
14
+ // src/ast/js-builtin-prototypes.ts
15
+ var JS_BUILTIN_CHAINS = {
16
+ Map: ["Map", "Object"],
17
+ Set: ["Set", "Object"],
18
+ WeakMap: ["WeakMap", "Object"],
19
+ WeakSet: ["WeakSet", "Object"],
20
+ Promise: ["Promise", "Object"],
21
+ Array: ["Array", "Object"],
22
+ String: ["String", "Object"],
23
+ Number: ["Number", "Object"],
24
+ Boolean: ["Boolean", "Object"],
25
+ Error: ["Error", "Object"],
26
+ TypeError: ["TypeError", "Error", "Object"],
27
+ RangeError: ["RangeError", "Error", "Object"],
28
+ Date: ["Date", "Object"],
29
+ RegExp: ["RegExp", "Object"],
30
+ JSON: ["JSON", "Object"],
31
+ Reflect: ["Reflect", "Object"],
32
+ URL: ["URL", "Object"],
33
+ URLSearchParams: ["URLSearchParams", "Object"],
34
+ Object: ["Object"],
35
+ HTMLElement: ["HTMLElement", "Element", "Node", "EventTarget", "Object"],
36
+ Element: ["Element", "Node", "EventTarget", "Object"],
37
+ Node: ["Node", "EventTarget", "Object"],
38
+ EventTarget: ["EventTarget", "Object"]
39
+ };
40
+ var MEMBERS = {
41
+ Map: ["clear", "delete", "entries", "forEach", "get", "has", "keys", "set", "values", "size", "[Symbol.iterator]"],
42
+ Set: ["add", "clear", "delete", "entries", "forEach", "has", "keys", "size", "values", "[Symbol.iterator]"],
43
+ WeakMap: ["delete", "get", "has", "set"],
44
+ WeakSet: ["add", "delete", "has"],
45
+ Promise: ["all", "allSettled", "any", "catch", "finally", "race", "reject", "resolve", "then"],
46
+ Array: ["at", "concat", "copyWithin", "entries", "every", "fill", "filter", "find", "findIndex", "flat", "flatMap", "forEach", "includes", "indexOf", "join", "keys", "lastIndexOf", "length", "map", "pop", "push", "reduce", "reduceRight", "reverse", "shift", "slice", "some", "sort", "splice", "toString", "unshift", "values", "[Symbol.iterator]"],
47
+ String: ["at", "charAt", "charCodeAt", "codePointAt", "concat", "endsWith", "includes", "indexOf", "lastIndexOf", "length", "localeCompare", "match", "normalize", "padEnd", "padStart", "repeat", "replace", "replaceAll", "search", "slice", "split", "startsWith", "substring", "toLowerCase", "toString", "toUpperCase", "trim", "trimEnd", "trimStart"],
48
+ Number: ["toExponential", "toFixed", "toPrecision", "toString", "valueOf"],
49
+ Boolean: ["toString", "valueOf"],
50
+ Error: ["cause", "message", "name", "stack", "toString"],
51
+ Date: ["getDate", "getDay", "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds", "getTime", "getTimezoneOffset", "getUTCDate", "getUTCDay", "getUTCFullYear", "getUTCHours", "setDate", "setFullYear", "setHours", "setMinutes", "setMonth", "setSeconds", "setTime", "toISOString", "toJSON", "toLocaleDateString", "toLocaleString", "toLocaleTimeString", "toString", "toUTCString", "valueOf"],
52
+ RegExp: ["exec", "flags", "global", "lastIndex", "source", "test", "toString"],
53
+ JSON: ["parse", "stringify"],
54
+ Reflect: ["apply", "construct", "defineProperty", "deleteProperty", "get", "getOwnPropertyDescriptor", "getPrototypeOf", "getOwnPropertyDescriptor", "has", "isExtensible", "ownKeys", "preventExtensions", "set", "setPrototypeOf"],
55
+ URL: ["hash", "href", "host", "hostname", "origin", "password", "pathname", "port", "protocol", "search", "searchParams", "toJSON", "toString", "username"],
56
+ URLSearchParams: ["append", "delete", "entries", "forEach", "get", "getAll", "has", "keys", "set", "size", "sort", "toString", "values", "[Symbol.iterator]"],
57
+ Object: ["assign", "create", "defineProperty", "entries", "freeze", "fromEntries", "getOwnPropertyDescriptor", "getOwnPropertyNames", "getPrototypeOf", "hasOwn", "hasOwnProperty", "is", "isFrozen", "isSealed", "keys", "preventExtensions", "propertyIsEnumerable", "seal", "setPrototypeOf", "toString", "valueOf"],
58
+ HTMLElement: ["attachShadow", "blur", "click", "closest", "focus", "getAttribute", "getBoundingClientRect", "getClientRects", "getElementsByClassName", "getElementsByTagName", "hidden", "id", "innerHTML", "innerText", "matches", "remove", "removeAttribute", "scrollIntoView", "setAttribute", "shadowRoot", "style", "tagName", "textContent", "title"],
59
+ Element: ["closest", "getAttribute", "getAttributeNames", "hasAttribute", "matches", "removeAttribute", "setAttribute", "tagName"],
60
+ Node: ["appendChild", "childNodes", "cloneNode", "contains", "insertBefore", "nodeName", "nodeType", "parentNode", "removeChild", "replaceChild", "textContent"],
61
+ EventTarget: ["addEventListener", "removeEventListener", "dispatchEvent"]
62
+ };
63
+ var MEMBER_SETS = Object.fromEntries(
64
+ Object.entries(MEMBERS).map(([k, v]) => [k, new Set(v)])
65
+ );
66
+ function isJsBuiltin(base) {
67
+ return Object.prototype.hasOwnProperty.call(JS_BUILTIN_CHAINS, base);
68
+ }
69
+ function jsBuiltinHasMember(base, member) {
70
+ const chain = JS_BUILTIN_CHAINS[base];
71
+ if (!chain) return false;
72
+ for (const link of chain) {
73
+ const set = MEMBER_SETS[link];
74
+ if (set && set.has(member)) return true;
75
+ }
76
+ return false;
77
+ }
78
+ var HOST_GLOBALS = {
79
+ // WHATWG HTML §7.3 — the Window interface and its members.
80
+ window: "WHATWG HTML \xA77.3 Window",
81
+ document: "WHATWG HTML \xA77.3 Window",
82
+ console: "WHATWG HTML \xA77.3 Window.console",
83
+ location: "WHATWG HTML \xA77.3 Window.location",
84
+ navigator: "WHATWG HTML \xA77.3 Window.navigator",
85
+ history: "WHATWG HTML \xA77.3 Window.history",
86
+ screen: "WHATWG HTML \xA77.3 Window.screen",
87
+ localStorage: "WHATWG HTML \xA77.3 WindowOrWorkerGlobalScope.localStorage",
88
+ sessionStorage: "WHATWG HTML \xA77.3 WindowOrWorkerGlobalScope.sessionStorage",
89
+ fetch: "WHATWG HTML \xA77.3 WindowOrWorkerGlobalScope.fetch",
90
+ setTimeout: "WHATWG HTML \xA77.3 WindowOrWorkerGlobalScope.setTimeout",
91
+ clearTimeout: "WHATWG HTML \xA77.3 WindowOrWorkerGlobalScope.clearTimeout",
92
+ setInterval: "WHATWG HTML \xA77.3 WindowOrWorkerGlobalScope.setInterval",
93
+ clearInterval: "WHATWG HTML \xA77.3 WindowOrWorkerGlobalScope.clearInterval",
94
+ requestAnimationFrame: "WHATWG HTML \xA77.3 Window.requestAnimationFrame",
95
+ cancelAnimationFrame: "WHATWG HTML \xA77.3 Window.cancelAnimationFrame",
96
+ alert: "WHATWG HTML \xA77.3 Window.alert",
97
+ confirm: "WHATWG HTML \xA77.3 Window.confirm",
98
+ prompt: "WHATWG HTML \xA77.3 Window.prompt",
99
+ atob: "WHATWG HTML \xA77.3 WindowOrWorkerGlobalScope.atob",
100
+ btoa: "WHATWG HTML \xA77.3 WindowOrWorkerGlobalScope.btoa",
101
+ queueMicrotask: "WHATWG HTML \xA77.3 WindowOrWorkerGlobalScope.queueMicrotask",
102
+ structuredClone: "WHATWG HTML \xA77.3 WindowOrWorkerGlobalScope.structuredClone",
103
+ performance: "WHATWG HTML \xA77.3 Window.performance",
104
+ customElements: "WHATWG HTML \xA77.3 Window.customElements",
105
+ // ECMA-262 §19–§26 — the global object's own properties.
106
+ globalThis: "ECMA-262 \xA719.1 globalThis",
107
+ JSON: "ECMA-262 \xA725.5 JSON",
108
+ Math: "ECMA-262 \xA721.3 Math",
109
+ Object: "ECMA-262 \xA720.1 Object",
110
+ Array: "ECMA-262 \xA723.1 Array",
111
+ String: "ECMA-262 \xA722.1 String",
112
+ Number: "ECMA-262 \xA721.1 Number",
113
+ Boolean: "ECMA-262 \xA720.3 Boolean",
114
+ Date: "ECMA-262 \xA721.4 Date",
115
+ RegExp: "ECMA-262 \xA722.2 RegExp",
116
+ Promise: "ECMA-262 \xA727.2 Promise",
117
+ Map: "ECMA-262 \xA724.1 Map",
118
+ Set: "ECMA-262 \xA724.2 Set",
119
+ WeakMap: "ECMA-262 \xA724.3 WeakMap",
120
+ WeakSet: "ECMA-262 \xA724.4 WeakSet",
121
+ Symbol: "ECMA-262 \xA720.4 Symbol",
122
+ Proxy: "ECMA-262 \xA728.2 Proxy",
123
+ Reflect: "ECMA-262 \xA728.1 Reflect",
124
+ Error: "ECMA-262 \xA720.5 Error",
125
+ TypeError: "ECMA-262 \xA720.5.6 TypeError",
126
+ RangeError: "ECMA-262 \xA720.5.5 RangeError",
127
+ parseInt: "ECMA-262 \xA719.2.5 parseInt",
128
+ parseFloat: "ECMA-262 \xA719.2.4 parseFloat",
129
+ isNaN: "ECMA-262 \xA719.2.3 isNaN",
130
+ isFinite: "ECMA-262 \xA719.2.2 isFinite",
131
+ encodeURIComponent: "ECMA-262 \xA719.2.6 encodeURIComponent",
132
+ decodeURIComponent: "ECMA-262 \xA719.2.7 decodeURIComponent",
133
+ encodeURI: "ECMA-262 \xA719.2.8 encodeURI",
134
+ decodeURI: "ECMA-262 \xA719.2.9 decodeURI",
135
+ NaN: "ECMA-262 \xA76.1.6 NaN",
136
+ Infinity: "ECMA-262 \xA76.1.6 Infinity",
137
+ undefined: "ECMA-262 \xA76.1.1 undefined"
138
+ };
139
+ function hostGlobalCitation(name) {
140
+ return HOST_GLOBALS[name] ?? null;
141
+ }
142
+ function memberChainClaim(receiverRoot, _member) {
143
+ if (!receiverRoot) return null;
144
+ const root = receiverRoot.trim();
145
+ if (!root) return null;
146
+ const cite = hostGlobalCitation(root);
147
+ return cite ? `${cite} (member of a host global; receiver root \`${root}\`)` : null;
148
+ }
149
+
150
+ // src/ast/js-runtime-globals.ts
151
+ var RUNTIME_GLOBALS = {
152
+ // ── Node.js host surface ──────────────────────────────────────────────────────────────────────
153
+ process: "Node.js API \xA7process (https://nodejs.org/api/process.html) \u2014 a global",
154
+ Buffer: "Node.js API \xA7Buffer (https://nodejs.org/api/buffer.html) \u2014 a global class",
155
+ global: "Node.js API \xA7global (https://nodejs.org/api/globals.html#global)",
156
+ setImmediate: "Node.js API \xA7setImmediate (https://nodejs.org/api/timers.html#setimmediatecallback-args)",
157
+ clearImmediate: "Node.js API \xA7clearImmediate (https://nodejs.org/api/timers.html#clearimmediateimmediate)",
158
+ require: "Node.js API \xA7require (https://nodejs.org/api/modules.html#requireid) \u2014 the CommonJS wrapper local, available bare",
159
+ module: "Node.js API \xA7module (https://nodejs.org/api/modules.html#the-module-object) \u2014 the CommonJS wrapper local, available bare",
160
+ exports: "Node.js API \xA7exports (https://nodejs.org/api/modules.html#exports) \u2014 the CommonJS wrapper local, available bare",
161
+ __dirname: "Node.js API \xA7__dirname (https://nodejs.org/api/modules.html#__dirname)",
162
+ __filename: "Node.js API \xA7__filename (https://nodejs.org/api/modules.html#__filename)",
163
+ // ── Test-runner globals (Jest-canonical spellings; Vitest mirrors them by design) ────────────
164
+ describe: "Jest Globals \xA7describe (https://jestjs.io/docs/api) \u2014 also a Mocha BDD global (https://mochajs.org/) and mirrored by Vitest (https://vitest.dev/api/)",
165
+ context: "Mocha BDD \xA7context (https://mochajs.org/) \u2014 an alias of describe",
166
+ it: "Jest Globals \xA7test, of which `it` is the alias (https://jestjs.io/docs/api) \u2014 also a Mocha BDD global and mirrored by Vitest",
167
+ test: "Jest Globals \xA7test (https://jestjs.io/docs/api) \u2014 also a Mocha BDD global and mirrored by Vitest",
168
+ specify: "Mocha BDD \xA7specify (https://mochajs.org/) \u2014 an alias of it",
169
+ expect: "Jest API \xA7expect (https://jestjs.io/docs/expect) \u2014 mirrored by Vitest (https://vitest.dev/api/)",
170
+ beforeAll: "Jest Globals \xA7beforeAll (https://jestjs.io/docs/api) \u2014 mirrored by Vitest",
171
+ afterAll: "Jest Globals \xA7afterAll (https://jestjs.io/docs/api) \u2014 mirrored by Vitest",
172
+ beforeEach: "Jest Globals \xA7beforeEach (https://jestjs.io/docs/api) \u2014 also a Mocha BDD global; mirrored by Vitest",
173
+ afterEach: "Jest Globals \xA7afterEach (https://jestjs.io/docs/api) \u2014 also a Mocha BDD global; mirrored by Vitest",
174
+ before: "Mocha BDD \xA7before (https://mochajs.org/) \u2014 the TDD/BDD hook",
175
+ after: "Mocha BDD \xA7after (https://mochajs.org/) \u2014 the TDD/BDD hook",
176
+ jest: "Jest Object (https://jestjs.io/docs/jest-object) \u2014 a global",
177
+ vi: "Vitest API \xA7vi (https://vitest.dev/api/) \u2014 a global",
178
+ QUnit: "QUnit API (https://qunitjs.com/api/) \u2014 QUnit exposes its API through the QUnit global"
179
+ };
180
+ function runtimeGlobalCitation(name) {
181
+ return RUNTIME_GLOBALS[name] ?? null;
182
+ }
183
+ function runtimeGlobalReason(name) {
184
+ if (!isRuntimeGlobal(name)) return null;
185
+ return NODE_RUNTIME_NAMES.has(name) ? "node-runtime-global" : "test-runner-global";
186
+ }
187
+ var NODE_RUNTIME_NAMES = /* @__PURE__ */ new Set([
188
+ "process",
189
+ "Buffer",
190
+ "global",
191
+ "setImmediate",
192
+ "clearImmediate",
193
+ "require",
194
+ "module",
195
+ "exports",
196
+ "__dirname",
197
+ "__filename"
198
+ ]);
199
+ function isRuntimeGlobal(name) {
200
+ return Object.prototype.hasOwnProperty.call(RUNTIME_GLOBALS, name);
201
+ }
202
+
203
+ // src/ast/real-javascript-analyzer.ts
204
+ var JS_CONSOLE_BUILTINS = /* @__PURE__ */ new Set([
205
+ "console.log",
206
+ "console.warn",
207
+ "console.error",
208
+ "console.info",
209
+ "console.debug",
210
+ "console.trace",
211
+ "console.table",
212
+ "console.group",
213
+ "console.groupEnd",
214
+ "console.groupCollapsed",
215
+ "console.time",
216
+ "console.timeEnd",
217
+ "console.assert",
218
+ "console.count",
219
+ "console.countReset",
220
+ "console.dir",
221
+ "console.dirxml",
222
+ "console.clear",
223
+ "console.profile",
224
+ "console.profileEnd"
225
+ ]);
226
+ var JS_GLOBAL_CONSTRUCTORS = /* @__PURE__ */ new Set([
227
+ "Promise",
228
+ "Date",
229
+ "Map",
230
+ "Set",
231
+ "WeakMap",
232
+ "WeakSet",
233
+ "Symbol",
234
+ "Proxy",
235
+ "Error",
236
+ "TypeError",
237
+ "RangeError",
238
+ "SyntaxError",
239
+ "URIError",
240
+ "EvalError",
241
+ "AggregateError",
242
+ "RegExp",
243
+ "Array",
244
+ "Object",
245
+ "Function",
246
+ "Boolean",
247
+ "Number",
248
+ "String",
249
+ "BigInt",
250
+ "ArrayBuffer",
251
+ "SharedArrayBuffer",
252
+ "DataView",
253
+ "Int8Array",
254
+ "Uint8Array",
255
+ "Uint8ClampedArray",
256
+ "Int16Array",
257
+ "Uint16Array",
258
+ "Int32Array",
259
+ "Uint32Array",
260
+ "Float32Array",
261
+ "Float64Array",
262
+ "BigInt64Array",
263
+ "BigUint64Array",
264
+ "WeakRef",
265
+ "FinalizationRegistry",
266
+ "AbortController",
267
+ "FormData",
268
+ "Blob",
269
+ "File",
270
+ "URL",
271
+ "URLSearchParams",
272
+ "Headers",
273
+ "Request",
274
+ "Response",
275
+ "Intl",
276
+ "Image",
277
+ "Worker",
278
+ "SharedWorker",
279
+ "CustomEvent",
280
+ "Event",
281
+ "MouseEvent",
282
+ "KeyboardEvent",
283
+ "ResizeObserver",
284
+ "MutationObserver",
285
+ "IntersectionObserver",
286
+ "PerformanceObserver",
287
+ "FileReader",
288
+ "XMLHttpRequest",
289
+ "WebSocket",
290
+ "TextEncoder",
291
+ "TextDecoder",
292
+ "DOMParser",
293
+ "Notification",
294
+ "BroadcastChannel",
295
+ "MessageChannel",
296
+ "FocusEvent",
297
+ "InputEvent",
298
+ "PointerEvent",
299
+ "TouchEvent",
300
+ "DragEvent",
301
+ "ErrorEvent",
302
+ "UIEvent",
303
+ "StorageEvent",
304
+ "PromiseRejectionEvent",
305
+ "HashChangeEvent",
306
+ "PageTransitionEvent",
307
+ "PopStateEvent",
308
+ "ProgressEvent",
309
+ "SpeechSynthesisEvent",
310
+ "AnimationEvent",
311
+ "TransitionEvent",
312
+ "ClipboardEvent",
313
+ "WheelEvent",
314
+ "CompositionEvent",
315
+ "BeforeUnloadEvent"
316
+ ]);
317
+ var JS_OBJECT_LEAF_METHODS = /* @__PURE__ */ new Set([
318
+ "toString",
319
+ "valueOf",
320
+ "hasOwnProperty",
321
+ "isPrototypeOf",
322
+ "propertyIsEnumerable",
323
+ "toLocaleString",
324
+ "__defineGetter__",
325
+ "__defineSetter__"
326
+ ]);
327
+ function unquote(text) {
328
+ if (text.length >= 2) {
329
+ const first = text[0];
330
+ if ((first === '"' || first === "'" || first === "`") && text[text.length - 1] === first) {
331
+ return text.slice(1, -1);
332
+ }
333
+ }
334
+ return text;
335
+ }
336
+ function stringContent(node) {
337
+ for (let i = 0; i < node.namedChildCount; i++) {
338
+ const c = node.namedChild(i);
339
+ if (c && c.type === "string_fragment") return c.text;
340
+ }
341
+ return unquote(node.text);
342
+ }
343
+ function stripCommentMarkers(text) {
344
+ if (text.startsWith("//")) return text.slice(2).trimStart();
345
+ if (text.startsWith("/*")) {
346
+ const end = text.endsWith("*/") ? text.length - 2 : text.length;
347
+ return text.slice(2, end).trim();
348
+ }
349
+ return text.trim();
350
+ }
351
+ function writtenSegment(prop, lhs) {
352
+ if (prop) return prop;
353
+ if (lhs.type === "subscript_expression") return lhs.childForFieldName("index");
354
+ return null;
355
+ }
356
+ function segmentName(node) {
357
+ return node.type === "string" ? stringContent(node) : node.text;
358
+ }
359
+ function isPlainJsIdentifier(text) {
360
+ if (text.length === 0) return false;
361
+ for (let i = 0; i < text.length; i++) {
362
+ const c = text.charCodeAt(i);
363
+ const isLetter = c >= 65 && c <= 90 || c >= 97 && c <= 122;
364
+ const isDigit = c >= 48 && c <= 57;
365
+ const isStart = isLetter || c === 95 || c === 36;
366
+ if (i === 0) {
367
+ if (!isStart) return false;
368
+ continue;
369
+ }
370
+ if (!isStart && !isDigit) return false;
371
+ }
372
+ return true;
373
+ }
374
+ function jsIdentifierPrefixEnd(text) {
375
+ let end = 0;
376
+ for (let i = 0; i < text.length; i++) {
377
+ const c = text.charCodeAt(i);
378
+ const isLetter = c >= 65 && c <= 90 || c >= 97 && c <= 122;
379
+ const isDigit = c >= 48 && c <= 57;
380
+ const isStart = isLetter || c === 95 || c === 36;
381
+ if (end === 0 ? !isStart : !(isStart || isDigit)) break;
382
+ end = i + 1;
383
+ }
384
+ return end;
385
+ }
386
+ var JS_MODULE_LOCALITY_CACHE = /* @__PURE__ */ new WeakMap();
387
+ function jsProjectModuleLocalityOf(index) {
388
+ const cached = JS_MODULE_LOCALITY_CACHE.get(index);
389
+ if (cached) return cached;
390
+ const modules = index.modules ?? /* @__PURE__ */ new Set();
391
+ const paths = [];
392
+ const stems = /* @__PURE__ */ new Set();
393
+ const dirs = /* @__PURE__ */ new Set();
394
+ const allSegments = /* @__PURE__ */ new Set();
395
+ for (const raw of modules) {
396
+ const m = String(raw).split("\\").join("/");
397
+ if (m.length === 0) continue;
398
+ paths.push(m);
399
+ const slash = m.lastIndexOf("/");
400
+ const base = slash >= 0 ? m.slice(slash + 1) : m;
401
+ const dot = base.lastIndexOf(".");
402
+ if (dot > 0) {
403
+ const stem = base.slice(0, dot);
404
+ if (stem.length > 0) stems.add(stem);
405
+ }
406
+ if (slash > 0) {
407
+ const dir = m.slice(0, slash);
408
+ const d = dir.lastIndexOf("/");
409
+ dirs.add(d >= 0 ? dir.slice(d + 1) : dir);
410
+ }
411
+ for (const seg of m.split("/")) if (seg.length > 0) allSegments.add(seg);
412
+ }
413
+ const JS_EXTS = [".js", ".jsx", ".mjs", ".cjs", ".json"];
414
+ const tailNamesIndexedFile = (spec) => {
415
+ for (const p of paths) {
416
+ if (p.endsWith(`/${spec}`) || p === spec) return true;
417
+ for (const ext of JS_EXTS) {
418
+ if (p.endsWith(`/${spec}${ext}`)) return true;
419
+ }
420
+ }
421
+ return false;
422
+ };
423
+ const stripQueryAndHash = (text) => {
424
+ for (let i = 0; i < text.length; i++) {
425
+ const ch = text[i];
426
+ if (ch === "?" || ch === "#") return text.slice(0, i);
427
+ }
428
+ return text;
429
+ };
430
+ const predicate = (spec) => {
431
+ const clean = stripQueryAndHash(spec.trim());
432
+ if (clean.length === 0) return "unknown";
433
+ const segments = clean.split("/");
434
+ const root = segments[0];
435
+ const rootOf = root.startsWith("@") && segments.length >= 2 ? `${root}/${segments[1]}` : root;
436
+ const rootOfOwn = root.startsWith("@") ? root : rootOf;
437
+ if (stems.has(rootOf) || dirs.has(rootOf) || dirs.has(rootOfOwn) || stems.has(rootOfOwn) || allSegments.has(rootOf) || allSegments.has(rootOfOwn)) return "project-local";
438
+ if (segments.length > 1 && tailNamesIndexedFile(clean)) return "project-local";
439
+ return "third-party";
440
+ };
441
+ JS_MODULE_LOCALITY_CACHE.set(index, predicate);
442
+ return predicate;
443
+ }
444
+ var NAME_NODE_TYPES = /* @__PURE__ */ new Set(["identifier", "property_identifier", "private_property_identifier"]);
445
+ function isNameNode(node) {
446
+ return !!node && NAME_NODE_TYPES.has(String(node.type ?? ""));
447
+ }
448
+ function hasBraceEquals(text) {
449
+ for (let i = 0; i < text.length; i++) {
450
+ const ch = text[i];
451
+ if (ch === "{" || ch === "}" || ch === ";" || ch === "=") return true;
452
+ }
453
+ return false;
454
+ }
455
+ function jsDecoratorName(decorator) {
456
+ const inner = decorator.namedChild(0);
457
+ const nameText = inner && (inner.type === "call_expression" || inner.type === "member_expression" || inner.type === "identifier") ? inner.type === "call_expression" ? inner.childForFieldName("function")?.text ?? inner.text.split("(")[0] : inner.text.split("(")[0] : decorator.text;
458
+ return stripLeadingAt(nameText) || stripLeadingAt(decorator.text);
459
+ }
460
+ function stripLeadingAt(text) {
461
+ return text.startsWith("@") ? text.slice(1) : text;
462
+ }
463
+ function stripWhitespace(text) {
464
+ let out = "";
465
+ for (let i = 0; i < text.length; i++) {
466
+ const ch = text[i];
467
+ if (ch !== " " && ch !== " " && ch !== "\n" && ch !== "\r") out += ch;
468
+ }
469
+ return out;
470
+ }
471
+ function isNodeInDeadSpans(spans, n) {
472
+ if (spans.length === 0) return false;
473
+ let p = n;
474
+ while (p) {
475
+ for (const d of spans) {
476
+ if (d.s <= p.startIndex && p.endIndex <= d.e) return true;
477
+ }
478
+ p = p.parent;
479
+ }
480
+ return false;
481
+ }
482
+ function scopeOwnerName(scopes) {
483
+ for (let i = scopes.length - 1; i >= 0; i--) {
484
+ if (scopes[i].kind === "class" /* Class */) return scopes[i].name;
485
+ }
486
+ return void 0;
487
+ }
488
+ function bindingNameAbove(value) {
489
+ let q = value.parent;
490
+ while (q) {
491
+ if (q.type === "variable_declarator") {
492
+ const nm = q.childForFieldName("name");
493
+ return nm && nm.type === "identifier" ? nm.text : void 0;
494
+ }
495
+ if (q.type === "assignment_expression") {
496
+ const l = q.childForFieldName("left");
497
+ return l && l.type === "identifier" ? l.text : void 0;
498
+ }
499
+ if (q.type === "pair" || q.type === "object" || q.type === "return_statement" || q.type === "expression_statement" || q.type === "export_statement") {
500
+ q = q.parent;
501
+ continue;
502
+ }
503
+ return void 0;
504
+ }
505
+ return void 0;
506
+ }
507
+ function containerOwnerName(n) {
508
+ let p = n.parent;
509
+ while (p) {
510
+ if (p.type === "class_declaration") return p.childForFieldName("name")?.text;
511
+ if (p.type === "class") {
512
+ return bindingNameAbove(p);
513
+ }
514
+ if (p.type === "object") {
515
+ return bindingNameAbove(p);
516
+ }
517
+ if (p.type === "function_declaration" || p.type === "function_expression" || p.type === "arrow_function" || p.type === "generator_function_declaration" || p.type === "method_definition" || p.type === "function") return void 0;
518
+ p = p.parent;
519
+ }
520
+ return void 0;
521
+ }
522
+ function isExternalSpecifier(specifier) {
523
+ if (specifier.length === 0) return false;
524
+ if (specifier.startsWith(".") || specifier.startsWith("/")) return false;
525
+ const low = specifier.toLowerCase();
526
+ if (low.startsWith("workspace:") || low.startsWith("file:") || low.startsWith("link:")) return false;
527
+ if (specifier.length >= 3) {
528
+ const c0 = specifier.charCodeAt(0);
529
+ const isLetter = c0 >= 65 && c0 <= 90 || c0 >= 97 && c0 <= 122;
530
+ const s1 = specifier[1];
531
+ const s2 = specifier[2];
532
+ if (isLetter && s1 === ":" && (s2 === "\\" || s2 === "/")) return false;
533
+ }
534
+ return true;
535
+ }
536
+ function scopeString(scopes) {
537
+ const parts = [];
538
+ for (let i = 1; i < scopes.length; i++) {
539
+ const s = scopes[i];
540
+ if (s.kind !== "global" /* Global */) parts.push(s.name);
541
+ }
542
+ return parts.join(".");
543
+ }
544
+ var FILE_LEVEL_SOURCE = "(file)";
545
+ var RealJavaScriptAnalyzer = class {
546
+ /**
547
+ * Whether the file currently being analyzed contains any `decorator` node.
548
+ * Set once per `analyze` call; see the pre-pass at its assignment for the
549
+ * measurement that made it worth having.
550
+ */
551
+ hasAnyDecoratorNode = false;
552
+ /**
553
+ * The positional lexical scope model for the file currently being analyzed (built once per
554
+ * `analyze`, before the relationship extraction passes, so every emission site can resolve its
555
+ * own node position exactly). Null outside an `analyze` call.
556
+ */
557
+ bindingModel = null;
558
+ analyze(rootNode, content, filePath, context) {
559
+ const symbols = [];
560
+ const relationships = [];
561
+ const imports = [];
562
+ const exports = [];
563
+ const errors = [];
564
+ const frameworks = { JavaScript: true };
565
+ const patterns = {};
566
+ const scopes = [{ name: "__global__", kind: "global" /* Global */ }];
567
+ this.hasAnyDecoratorNode = treeContainsNodeType(rootNode, "decorator");
568
+ const moduleName = path.basename(filePath, path.extname(filePath));
569
+ if (rootNode.hasError) {
570
+ const errStack = [rootNode];
571
+ while (errStack.length > 0) {
572
+ const n = errStack.pop();
573
+ if (n.type === "ERROR" || n.isMissing) {
574
+ errors.push({
575
+ message: `Syntax ${n.type} detected. The Brain Engine is bypassing this broken node.`,
576
+ range: this.nodeToRange(n),
577
+ severity: "warning"
578
+ });
579
+ }
580
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
581
+ const child = n.namedChild(i);
582
+ if (child) errStack.push(child);
583
+ }
584
+ }
585
+ }
586
+ const stack = [{ node: rootNode, scopes, inClassBody: false, functionDepth: 0 }];
587
+ let cyclomaticComplexity = 1;
588
+ const seenMethodIdx = /* @__PURE__ */ new Map();
589
+ const deadSpans = [];
590
+ const b5Zombies = [];
591
+ const relSpans = [];
592
+ while (stack.length > 0) {
593
+ const { node, scopes: frameScopes, inClassBody, functionDepth } = stack.pop();
594
+ const scopeStack = [...frameScopes];
595
+ let nextInClassBody = inClassBody;
596
+ let nextFunctionDepth = functionDepth;
597
+ if (node.type === "if_statement" || node.type === "for_statement" || node.type === "for_in_statement" || node.type === "while_statement" || node.type === "do_statement" || node.type === "catch_clause" || node.type === "switch_case" || node.type === "ternary_expression") {
598
+ cyclomaticComplexity++;
599
+ } else if (node.type === "binary_expression") {
600
+ const op = node.childForFieldName("operator")?.text;
601
+ if (op === "&&" || op === "||" || op === "??") cyclomaticComplexity++;
602
+ }
603
+ const relMark = relationships.length;
604
+ switch (node.type) {
605
+ case "class_declaration": {
606
+ const name = node.childForFieldName("name")?.text;
607
+ if (name) {
608
+ const comments = this.collectComments(node);
609
+ const dec = this.collectClassDecoratorDetails(node);
610
+ const decorators = dec.names;
611
+ symbols.push({
612
+ name,
613
+ kind: ASTEngine.SymbolKind.Class,
614
+ range: this.nodeToRange(node),
615
+ scope: scopeString(scopeStack),
616
+ visibility: "public",
617
+ decorators: decorators.length > 0 ? decorators : void 0,
618
+ comments,
619
+ metadata: {
620
+ isClass: true,
621
+ // M49 #868: parallel to `decorators`, so a decorator's
622
+ // reference edge can name the line the decorator is written
623
+ // on (the M59 #860 contract the typescript twin already has).
624
+ ...dec.lines.length > 0 ? { decoratorLines: dec.lines } : {}
625
+ }
626
+ });
627
+ scopeStack.push({ name, kind: "class" /* Class */ });
628
+ nextInClassBody = true;
629
+ const heritage = this.findChildByType(node, "class_heritage");
630
+ if (heritage) {
631
+ const base = this.findChildByType(heritage, "identifier") ?? this.findChildByType(heritage, "member_expression");
632
+ if (base) {
633
+ const boundBase = base.type === "identifier" ? this.localBindingTarget(base, base.text, name) : null;
634
+ const importedBase = boundBase ? null : base.type === "identifier" ? this.importedBindingFor(base, base.text) : null;
635
+ relationships.push({
636
+ source: name,
637
+ target: boundBase ?? base.text,
638
+ type: ASTEngine.RelationshipType.Extends,
639
+ confidence: 1,
640
+ ...this.lineField(base),
641
+ metadata: boundBase ? { localBinding: true } : importedBase ? { importedBinding: importedBase } : {}
642
+ });
643
+ } else {
644
+ const rows = this.callShapedHeritageRows(heritage, name);
645
+ if (rows.base) {
646
+ relationships.push({
647
+ source: name,
648
+ target: rows.base.target,
649
+ type: ASTEngine.RelationshipType.Extends,
650
+ confidence: 1,
651
+ ...this.lineField(rows.base.node),
652
+ metadata: rows.base.metadata
653
+ });
654
+ }
655
+ for (const mix of rows.mixins) {
656
+ relationships.push({
657
+ source: name,
658
+ target: mix.target,
659
+ type: ASTEngine.RelationshipType.MixesIn,
660
+ confidence: 1,
661
+ ...this.lineField(mix.node),
662
+ metadata: { ...mix.metadata, isMixin: true }
663
+ });
664
+ }
665
+ }
666
+ }
667
+ }
668
+ break;
669
+ }
670
+ case "class": {
671
+ const ownName = node.childForFieldName("name")?.text ?? bindingNameAbove(node);
672
+ const heritage = this.findChildByType(node, "class_heritage");
673
+ if (heritage && ownName) {
674
+ const base = this.findChildByType(heritage, "identifier") ?? this.findChildByType(heritage, "member_expression");
675
+ if (base) {
676
+ const boundBaseExpr = base.type === "identifier" ? this.localBindingTarget(base, base.text, ownName) : null;
677
+ const importedBaseExpr = boundBaseExpr ? null : base.type === "identifier" ? this.importedBindingFor(base, base.text) : null;
678
+ relationships.push({
679
+ source: ownName,
680
+ target: boundBaseExpr ?? base.text,
681
+ type: ASTEngine.RelationshipType.Extends,
682
+ confidence: 1,
683
+ ...this.lineField(base),
684
+ metadata: boundBaseExpr ? { localBinding: true } : importedBaseExpr ? { importedBinding: importedBaseExpr } : {}
685
+ });
686
+ } else {
687
+ const rows = this.callShapedHeritageRows(heritage, ownName);
688
+ if (rows.base) {
689
+ relationships.push({
690
+ source: ownName,
691
+ target: rows.base.target,
692
+ type: ASTEngine.RelationshipType.Extends,
693
+ confidence: 1,
694
+ ...this.lineField(rows.base.node),
695
+ metadata: rows.base.metadata
696
+ });
697
+ }
698
+ for (const mix of rows.mixins) {
699
+ relationships.push({
700
+ source: ownName,
701
+ target: mix.target,
702
+ type: ASTEngine.RelationshipType.MixesIn,
703
+ confidence: 1,
704
+ ...this.lineField(mix.node),
705
+ metadata: { ...mix.metadata, isMixin: true }
706
+ });
707
+ }
708
+ }
709
+ }
710
+ break;
711
+ }
712
+ case "method_definition": {
713
+ const name = node.childForFieldName("name")?.text;
714
+ if (name) {
715
+ const inObjectLiteral = node.parent?.type === "object";
716
+ const classOwner = inObjectLiteral ? void 0 : scopeOwnerName(scopeStack);
717
+ const owner = classOwner ?? containerOwnerName(node);
718
+ const isConstructor = name === "constructor";
719
+ const isAccessor = this.hasToken(node, "get") || this.hasToken(node, "set");
720
+ const kindSlot = isAccessor ? this.hasToken(node, "get") ? ":get" : ":set" : "";
721
+ const parentType = node.parent?.type;
722
+ const containerKey = parentType === "object" ? `obj@${node.parent.startIndex}` : parentType === "class_body" ? `cls@${node.parent.startIndex}` : owner;
723
+ const dedupKey = `${containerKey ?? "module"}:${name}${kindSlot}`;
724
+ const kind = isConstructor ? ASTEngine.SymbolKind.Constructor : isAccessor ? ASTEngine.SymbolKind.Property : ASTEngine.SymbolKind.Method;
725
+ const isStatic = this.hasToken(node, "static");
726
+ const isAsync = this.hasToken(node, "async");
727
+ const isGenerator = this.hasToken(node, "*");
728
+ const isPrivateMethod = name.startsWith("#");
729
+ let seenIdx = seenMethodIdx.get(dedupKey);
730
+ let seenKey = seenIdx === void 0 ? void 0 : dedupKey;
731
+ if (seenIdx === void 0) {
732
+ if (kindSlot === "") {
733
+ for (const opp of [":get", ":set"]) {
734
+ const k2 = dedupKey + opp;
735
+ const i2 = seenMethodIdx.get(k2);
736
+ if (i2 !== void 0) {
737
+ seenIdx = i2;
738
+ seenKey = k2;
739
+ break;
740
+ }
741
+ }
742
+ } else {
743
+ const k2 = dedupKey.slice(0, dedupKey.length - kindSlot.length);
744
+ const i2 = seenMethodIdx.get(k2);
745
+ if (i2 !== void 0) {
746
+ seenIdx = i2;
747
+ seenKey = k2;
748
+ }
749
+ }
750
+ }
751
+ {
752
+ const params = this.extractParameters(node.childForFieldName("parameters"));
753
+ const comments = this.collectComments(node);
754
+ const dec = this.collectOwnDecoratorDetails(node);
755
+ const decorators = dec.names;
756
+ var methodSymbol = {
757
+ // #813: the declared spelling IS the member's name (every edge
758
+ // that reads it carries the `#`; see the TS site's note).
759
+ name,
760
+ kind,
761
+ range: this.nodeToRange(node),
762
+ scope: scopeString(scopeStack),
763
+ ownerName: owner,
764
+ ownerKind: classOwner ? ASTEngine.SymbolKind.Class : void 0,
765
+ // '#' members are class-private regardless of other tokens.
766
+ visibility: isPrivateMethod ? "private" : "public",
767
+ isStatic,
768
+ parameters: params,
769
+ decorators: decorators.length > 0 ? decorators : void 0,
770
+ comments,
771
+ complexity: this.methodComplexity(node),
772
+ metadata: {
773
+ isAccessor,
774
+ isAsync,
775
+ isGenerator,
776
+ ...kindSlot ? { accessorKind: kindSlot === ":get" ? "get" : "set" } : {},
777
+ // M49 #868: the decorator's own line, parallel to
778
+ // `decorators` (same lockstep as the typescript twin).
779
+ ...dec.lines.length > 0 ? { decoratorLines: dec.lines } : {}
780
+ }
781
+ };
782
+ }
783
+ if (seenIdx === void 0) {
784
+ seenMethodIdx.set(dedupKey, symbols.length);
785
+ symbols.push(methodSymbol);
786
+ } else {
787
+ const oldRange = symbols[seenIdx].range;
788
+ if (oldRange && oldRange.startIndex !== void 0 && oldRange.endIndex !== void 0) deadSpans.push({ s: oldRange.startIndex, e: oldRange.endIndex });
789
+ if (kindSlot === "") {
790
+ const otherKey = seenKey === dedupKey + ":get" ? dedupKey + ":set" : seenKey === dedupKey + ":set" ? dedupKey + ":get" : void 0;
791
+ const otherIdx = otherKey !== void 0 ? seenMethodIdx.get(otherKey) : void 0;
792
+ if (otherIdx !== void 0 && otherIdx !== seenIdx) {
793
+ const rj = symbols[otherIdx]?.range;
794
+ if (rj && rj.startIndex !== void 0 && rj.endIndex !== void 0) deadSpans.push({ s: rj.startIndex, e: rj.endIndex });
795
+ b5Zombies.push(otherIdx);
796
+ }
797
+ }
798
+ symbols[seenIdx] = methodSymbol;
799
+ if (seenKey) seenMethodIdx.delete(seenKey);
800
+ seenMethodIdx.set(dedupKey, seenIdx);
801
+ }
802
+ scopeStack.push({ name, kind: "function" /* Function */ });
803
+ nextFunctionDepth = functionDepth + 1;
804
+ nextInClassBody = false;
805
+ }
806
+ break;
807
+ }
808
+ case "field_definition": {
809
+ const prop = node.childForFieldName("property");
810
+ if (prop) {
811
+ const name = prop.text;
812
+ const classOwner = scopeOwnerName(scopeStack);
813
+ const owner = classOwner ?? containerOwnerName(node);
814
+ const dec = this.collectOwnDecoratorDetails(node);
815
+ symbols.push({
816
+ name,
817
+ kind: ASTEngine.SymbolKind.Property,
818
+ range: this.nodeToRange(node),
819
+ scope: scopeString(scopeStack),
820
+ ownerName: owner,
821
+ ownerKind: classOwner ? ASTEngine.SymbolKind.Class : void 0,
822
+ // J9 (review wave, TS parity): `#x` fields are class-private —
823
+ // the old hardcoded "public" contradicted metadata.isPrivate
824
+ // on the same symbol.
825
+ visibility: prop.type === "private_property_identifier" ? "private" : "public",
826
+ decorators: dec.names.length > 0 ? dec.names : void 0,
827
+ metadata: {
828
+ isPrivate: prop.type === "private_property_identifier",
829
+ ...dec.lines.length > 0 ? { decoratorLines: dec.lines } : {}
830
+ }
831
+ });
832
+ }
833
+ break;
834
+ }
835
+ case "function_declaration":
836
+ case "generator_function_declaration": {
837
+ const name = node.childForFieldName("name")?.text;
838
+ if (name) {
839
+ const comments = this.collectComments(node);
840
+ const params = this.extractParameters(node.childForFieldName("parameters"));
841
+ const isAsync = this.hasToken(node, "async");
842
+ symbols.push({
843
+ name,
844
+ kind: ASTEngine.SymbolKind.Function,
845
+ range: this.nodeToRange(node),
846
+ scope: scopeString(scopeStack),
847
+ visibility: "public",
848
+ parameters: params,
849
+ comments,
850
+ complexity: this.methodComplexity(node),
851
+ metadata: {
852
+ isGenerator: node.type === "generator_function_declaration",
853
+ isAsync
854
+ }
855
+ });
856
+ scopeStack.push({ name, kind: "function" /* Function */ });
857
+ nextFunctionDepth = functionDepth + 1;
858
+ }
859
+ break;
860
+ }
861
+ case "variable_declarator": {
862
+ const nameNode = node.childForFieldName("name");
863
+ const valueNode = node.childForFieldName("value");
864
+ const name = nameNode && nameNode.type === "identifier" ? nameNode.text : void 0;
865
+ const isFunctionValue = !!valueNode && (valueNode.type === "arrow_function" || valueNode.type === "function_expression" || valueNode.type === "generator_function");
866
+ const isRequireBinding = !!valueNode && valueNode.type === "call_expression" && valueNode.childForFieldName("function")?.text === "require";
867
+ if (name && isRequireBinding) {
868
+ break;
869
+ }
870
+ if (name && functionDepth === 0 && !inClassBody) {
871
+ if (isFunctionValue) {
872
+ const comments = this.collectComments(node);
873
+ const params = this.extractParameters(valueNode.childForFieldName("parameters"));
874
+ const isAsync = this.hasToken(valueNode, "async");
875
+ symbols.push({
876
+ name,
877
+ kind: ASTEngine.SymbolKind.Function,
878
+ range: this.nodeToRange(node),
879
+ scope: scopeString(scopeStack),
880
+ visibility: "public",
881
+ parameters: params,
882
+ comments,
883
+ complexity: this.methodComplexity(valueNode),
884
+ metadata: { isArrowFunction: valueNode.type === "arrow_function", isAsync }
885
+ });
886
+ scopeStack.push({ name, kind: "function" /* Function */ });
887
+ nextFunctionDepth = functionDepth + 1;
888
+ } else {
889
+ const isConst = this.isConstDeclarator(node);
890
+ symbols.push({
891
+ name,
892
+ kind: isConst ? ASTEngine.SymbolKind.Constant : ASTEngine.SymbolKind.Variable,
893
+ range: this.nodeToRange(node),
894
+ scope: scopeString(scopeStack),
895
+ visibility: "public",
896
+ metadata: { isConst }
897
+ });
898
+ }
899
+ } else if (nameNode && (nameNode.type === "object_pattern" || nameNode.type === "array_pattern") && functionDepth === 0 && !inClassBody) {
900
+ const isConst = this.isConstDeclarator(node);
901
+ const pairs = this.collectPatternPairs(nameNode);
902
+ for (const bound of pairs) {
903
+ symbols.push({
904
+ name: bound.name,
905
+ kind: isConst ? ASTEngine.SymbolKind.Constant : ASTEngine.SymbolKind.Variable,
906
+ // M59 (#825): each binding ranges at its own NAME, not the
907
+ // whole declarator (a wrapped binding list stamped `const {`
908
+ // for every member — measured on element-plus drawer.vue).
909
+ range: this.nodeToRange(bound.node),
910
+ scope: scopeString(scopeStack),
911
+ visibility: "public",
912
+ metadata: { isConst, isDestructured: true }
913
+ });
914
+ }
915
+ } else if (isFunctionValue) {
916
+ scopeStack.push({ name: name ?? "anonymous", kind: "function" /* Function */ });
917
+ nextFunctionDepth = functionDepth + 1;
918
+ }
919
+ break;
920
+ }
921
+ case "function_expression":
922
+ case "arrow_function": {
923
+ const parent = node.parent;
924
+ if (parent && parent.type !== "variable_declarator") {
925
+ const nm = `anonymous_function_${node.startIndex}`;
926
+ scopeStack.push({ name: nm, kind: "function" /* Function */ });
927
+ nextFunctionDepth = functionDepth + 1;
928
+ }
929
+ break;
930
+ }
931
+ case "assignment_expression": {
932
+ const left = node.childForFieldName("left");
933
+ const right = node.childForFieldName("right");
934
+ if (left && right) {
935
+ if (left.type === "identifier" && left.text === "exports") {
936
+ patterns["CommonJS Exports"] = true;
937
+ this.collectModuleExports(right, exports, node, false, "exports");
938
+ }
939
+ if (left.type === "member_expression" || left.type === "subscript_expression") {
940
+ const obj = left.childForFieldName("object");
941
+ const prop = left.childForFieldName("property");
942
+ const objText = obj ? obj.text : "";
943
+ const propText = prop ? prop.text : "";
944
+ if (objText === "module" && propText === "exports") {
945
+ patterns["CommonJS Exports"] = true;
946
+ this.collectModuleExports(right, exports, node, true, "module-exports");
947
+ } else if (obj && writtenSegment(prop, left)) {
948
+ const surface = this.cjsSurfacePath(obj);
949
+ if (surface) {
950
+ patterns["CommonJS Exports"] = true;
951
+ const seg = writtenSegment(prop, left);
952
+ const exportName = [...surface, segmentName(seg)].join(".");
953
+ exports.push({
954
+ name: exportName,
955
+ isDefault: false,
956
+ range: this.nodeToRange(seg),
957
+ metadata: { viaCommonJs: true, cjsForm: "deep-member-assignment" }
958
+ });
959
+ }
960
+ }
961
+ const isFunctionRight = right.type === "function_expression" || right.type === "arrow_function" || right.type === "generator_function";
962
+ if (isFunctionRight && obj && obj.type === "member_expression") {
963
+ const protoObj = obj.childForFieldName("object");
964
+ const protoProp = obj.childForFieldName("property");
965
+ if (protoProp && protoProp.text === "prototype" && protoObj && propText) {
966
+ patterns["Prototype Inheritance"] = true;
967
+ const className = protoObj.text;
968
+ const methodName = propText;
969
+ const dedupKey = `${className}:${methodName}`;
970
+ {
971
+ const params = this.extractParameters(right.childForFieldName("parameters"));
972
+ var protoSymbol = {
973
+ name: methodName,
974
+ kind: ASTEngine.SymbolKind.Method,
975
+ range: this.nodeToRange(right),
976
+ scope: className,
977
+ ownerName: className,
978
+ ownerKind: ASTEngine.SymbolKind.Class,
979
+ visibility: "public",
980
+ parameters: params,
981
+ complexity: this.methodComplexity(right),
982
+ metadata: { isPrototypeMethod: true, prototypeOwner: className }
983
+ };
984
+ }
985
+ const protoIdx = seenMethodIdx.get(dedupKey);
986
+ if (protoIdx === void 0) {
987
+ seenMethodIdx.set(dedupKey, symbols.length);
988
+ symbols.push(protoSymbol);
989
+ } else {
990
+ const oldRange = symbols[protoIdx].range;
991
+ if (oldRange && oldRange.startIndex !== void 0 && oldRange.endIndex !== void 0) deadSpans.push({ s: oldRange.startIndex, e: oldRange.endIndex });
992
+ symbols[protoIdx] = protoSymbol;
993
+ }
994
+ scopeStack.push({ name: methodName, kind: "function" /* Function */ });
995
+ nextFunctionDepth = functionDepth + 1;
996
+ }
997
+ }
998
+ }
999
+ }
1000
+ break;
1001
+ }
1002
+ case "call_expression": {
1003
+ const fn = node.childForFieldName("function");
1004
+ const isImportCall = !!fn && (fn.type === "import" || fn.type === "identifier" && fn.text === "import");
1005
+ if (fn && (fn.type === "identifier" && fn.text === "require" || isImportCall)) {
1006
+ const args = node.childForFieldName("arguments");
1007
+ if (args && args.namedChildCount > 0) {
1008
+ const arg = args.namedChild(0);
1009
+ if (arg && (arg.type === "string" || arg.type === "template_string")) {
1010
+ const reqPath = stringContent(arg);
1011
+ const isDynamic = isImportCall;
1012
+ let requireIsDefault = true;
1013
+ if (!isDynamic) {
1014
+ let vp = node.parent;
1015
+ while (vp && vp.type !== "variable_declarator" && vp.type !== "lexical_declaration" && vp.type !== "variable_declaration" && vp.type !== "export_statement" && vp.type !== "expression_statement" && vp.type !== "program") {
1016
+ vp = vp.parent;
1017
+ }
1018
+ if (vp && vp.type === "variable_declarator") {
1019
+ const bindName = vp.childForFieldName("name");
1020
+ requireIsDefault = !!bindName && bindName.type === "identifier";
1021
+ } else {
1022
+ requireIsDefault = false;
1023
+ }
1024
+ }
1025
+ imports.push({
1026
+ module: reqPath,
1027
+ items: ["*"],
1028
+ isDefault: requireIsDefault,
1029
+ range: this.nodeToRange(node),
1030
+ metadata: { type: isDynamic ? "dynamic-import" : "require" }
1031
+ });
1032
+ patterns[isDynamic ? "Dynamic Imports" : "CommonJS Imports"] = true;
1033
+ relationships.push({
1034
+ source: moduleName,
1035
+ target: `${reqPath}:*`,
1036
+ type: ASTEngine.RelationshipType.Imports,
1037
+ // JD-J12 (M60, 2026-09-27): a `*` target names NO SYMBOL.
1038
+ //
1039
+ // `require("m")` and `import("m")` are module-level: the
1040
+ // source declares that the module is loaded, not which member
1041
+ // is taken from it. Publishing `<module>:*` at 1.0 states a
1042
+ // certainty the file does not contain — and this is the ONLY
1043
+ // shape a CJS require ever takes, so for an ES5 corpus it is
1044
+ // the whole import class.
1045
+ //
1046
+ // Measured on the two javascript corpora at 300 files: 310
1047
+ // edges carry a `*` leaf and **every one is at confidence
1048
+ // 1.0** — 233 of 1,606 in svelte, and 77 of 77 in lodash
1049
+ // (whose entire import surface is `require`). No named-leaf
1050
+ // import edge exists there at all.
1051
+ //
1052
+ // This is the same defect shape as java's inferred JPA table
1053
+ // name (`mapped_table`, fixed earlier in this review): an
1054
+ // inference published at maximum confidence. The edge is KEPT
1055
+ // — the module dependency is real and worth recording — but it
1056
+ // is marked and demoted below the 0.7 fabrication threshold,
1057
+ // so it is declared suspicion rather than a counted
1058
+ // relationship, and a consumer can filter on `unresolved`.
1059
+ confidence: 0.6,
1060
+ // The module path argument is the name this edge is about.
1061
+ ...this.lineField(arg),
1062
+ metadata: {
1063
+ // THE EVIDENCE KEY for the arbiter's import facts: the module specifier as written.
1064
+ // The ES-import rows already carried `module`; the require/import() rows did not, and
1065
+ // the first arbiter smoke measured the cost exactly: SIX importedBinding claims were
1066
+ // REFUTED because their evidence edge had no `module` to key file->specifier->bindings
1067
+ // with (every one of the six was a require() binding: fs, path, an absolute pg path,
1068
+ // ../utils/logger, ../../services/...). The claim's `specifier` and this field must be
1069
+ // the SAME spelling — both come from stringContent() on the written argument.
1070
+ module: reqPath,
1071
+ isRequire: !isDynamic,
1072
+ isDynamicImport: isDynamic,
1073
+ unresolved: true,
1074
+ // THE EVIDENCE the imported-binding claim is verified against: the local names this
1075
+ // module load binds IN THIS FILE (a destructured `const { a } = require("m")` binds a,
1076
+ // a bare `const fs = require("m")` binds fs). Dynamic specifiers are refused earlier,
1077
+ // so they reach no claim — the contract's never-claim (c).
1078
+ importedBindings: (() => {
1079
+ let owner = node.parent;
1080
+ while (owner && owner.type !== "variable_declarator" && owner.type !== "lexical_declaration" && owner.type !== "variable_declaration" && owner.type !== "export_statement" && owner.type !== "expression_statement" && owner.type !== "program") {
1081
+ owner = owner.parent;
1082
+ }
1083
+ const bindName = owner && owner.type === "variable_declarator" ? owner.childForFieldName("name") : null;
1084
+ return bindName ? this.collectPatternPairs(bindName).map((pair) => pair.name) : [];
1085
+ })()
1086
+ }
1087
+ });
1088
+ }
1089
+ }
1090
+ }
1091
+ break;
1092
+ }
1093
+ case "import_statement": {
1094
+ patterns["ES6 Imports"] = true;
1095
+ const srcNode = node.childForFieldName("source");
1096
+ const srcPath = srcNode ? stringContent(srcNode) : "";
1097
+ const items = [];
1098
+ const clause = this.findChildByType(node, "import_clause");
1099
+ let hasDefaultImport = false;
1100
+ if (clause) {
1101
+ for (let i = 0; i < clause.namedChildCount; i++) {
1102
+ const c = clause.namedChild(i);
1103
+ if (!c) continue;
1104
+ if (c.type === "identifier") {
1105
+ items.push({ text: c.text, node: c, declared: "default", kind: "default" });
1106
+ hasDefaultImport = true;
1107
+ } else if (c.type === "named_imports") {
1108
+ for (let j = 0; j < c.namedChildCount; j++) {
1109
+ const spec = c.namedChild(j);
1110
+ if (spec && spec.type === "import_specifier") {
1111
+ const n = this.findChildByType(spec, "identifier");
1112
+ const alias = spec.childForFieldName("alias");
1113
+ const item = alias ?? n;
1114
+ if (item) {
1115
+ items.push({ text: item.text, node: item, declared: n?.text ?? item.text, kind: "named" });
1116
+ }
1117
+ }
1118
+ }
1119
+ } else if (c.type === "namespace_import") {
1120
+ const nsName = c.childForFieldName("name") ?? c.namedChild(0);
1121
+ items.push({ text: "*", node: nsName ?? c, declared: "*", kind: "namespace" });
1122
+ }
1123
+ }
1124
+ }
1125
+ if (srcPath) {
1126
+ imports.push({
1127
+ module: srcPath,
1128
+ items: items.map((i) => i.text),
1129
+ isDefault: hasDefaultImport,
1130
+ range: this.nodeToRange(node),
1131
+ metadata: { type: "import" }
1132
+ });
1133
+ for (const item of items) {
1134
+ const isNamespaceItem = item.text === "*";
1135
+ const jsItemName = item.text.split(" as ")[0];
1136
+ const bareLocalItem = item.kind === "named" && !isNamespaceItem && jsItemName !== "" && (srcPath.startsWith(".") || srcPath.startsWith("/"));
1137
+ const localNamedTarget = item.kind === "named" ? item.declared || jsItemName : jsItemName;
1138
+ relationships.push({
1139
+ source: moduleName,
1140
+ // FOR A LOCAL MODULE the target is the symbol the edge refers to;
1141
+ // for an external one it stays `<specifier>:<local binding>`,
1142
+ // which is what the importing fixtures pin.
1143
+ target: bareLocalItem ? localNamedTarget : `${srcPath}:${isNamespaceItem ? "*" : jsItemName}`,
1144
+ type: ASTEngine.RelationshipType.Imports,
1145
+ confidence: isNamespaceItem ? 0.6 : 1,
1146
+ ...this.lineField(item.node),
1147
+ // JD2b, additive: what the MODULE declares, so the resolver can
1148
+ // reach the right symbol for an aliased or default import. The
1149
+ // edge target above is untouched.
1150
+ metadata: {
1151
+ // THE MODULE, NOW THAT IT IS NO LONGER GLUED INTO THE TARGET. TypeScript's site
1152
+ // already carried `module`; this one did not, so a bare item name here would have
1153
+ // LOST the module half rather than moved it — and an item name with no module is
1154
+ // unresolvable by construction (which module's `readJson`?).
1155
+ module: srcPath,
1156
+ declaredName: item.declared,
1157
+ declaredKind: item.kind,
1158
+ // THE LOCAL NAME THIS IMPORT BINDS — the evidence for the imported-binding claim.
1159
+ // Default/named: the item's local spelling (alias when present); namespace: the
1160
+ // local `ns` identifier the clause writes (the item's text is `*`).
1161
+ importedBindings: isNamespaceItem ? String(item.node?.text ?? "") ? [String(item.node.text)] : [] : item.text ? [item.text] : [],
1162
+ ...isNamespaceItem ? { unresolved: true } : {}
1163
+ }
1164
+ });
1165
+ }
1166
+ }
1167
+ break;
1168
+ }
1169
+ case "export_statement": {
1170
+ patterns["ES6 Exports"] = true;
1171
+ this.collectExportStatement(node, exports);
1172
+ const exportSource = node.childForFieldName("source");
1173
+ if (exportSource) {
1174
+ const modPath = stringContent(exportSource);
1175
+ const namedSpecs = [];
1176
+ const clauseNames = [];
1177
+ const clause2 = this.findChildByType(node, "export_clause");
1178
+ if (clause2) {
1179
+ for (let i = 0; i < clause2.namedChildCount; i++) {
1180
+ const spec = clause2.namedChild(i);
1181
+ if (spec && spec.type === "export_specifier") {
1182
+ const nameNode = spec.childForFieldName("name") ?? spec.namedChild(0);
1183
+ const alias = spec.childForFieldName("alias");
1184
+ const member = nameNode ? nameNode.type === "string" ? stringContent(nameNode) : String(nameNode.text ?? "") : "";
1185
+ if (nameNode && member) {
1186
+ namedSpecs.push({ member, exportedAs: alias ? String(alias.text) : member, node: nameNode });
1187
+ clauseNames.push(alias ? String(alias.text) : member);
1188
+ }
1189
+ }
1190
+ }
1191
+ }
1192
+ const starOnly = !clause2 && this.hasToken(node, "*");
1193
+ const namespaceExport = this.findChildByType(node, "namespace_export");
1194
+ const namespaceAlias = namespaceExport ? String(namespaceExport.childForFieldName("name") ?? namespaceExport.namedChild(0)?.text ?? "") : "";
1195
+ imports.push({
1196
+ module: modPath,
1197
+ items: starOnly ? ["*"] : clauseNames,
1198
+ isDefault: false,
1199
+ range: this.nodeToRange(node),
1200
+ metadata: { reExport: true }
1201
+ });
1202
+ if (namedSpecs.length) {
1203
+ for (const spec of namedSpecs) {
1204
+ relationships.push({
1205
+ source: FILE_LEVEL_SOURCE,
1206
+ // The MEMBER in the source module — not the alias this module
1207
+ // exports it under, which is what an `exports` edge is about.
1208
+ target: `${modPath}:${spec.member}`,
1209
+ type: ASTEngine.RelationshipType.Imports,
1210
+ // 1.0 for the same reason a named import keeps 1.0: the member
1211
+ // name is WRITTEN in this file, and ESM fixes its meaning (the
1212
+ // source module must export `member`), so this is a stated
1213
+ // dependency and not an inference. `export *` / `export * as ns`
1214
+ // keep 0.6 and `unresolved` — they write no member name.
1215
+ confidence: 1,
1216
+ // The specifier's OWN line: a clause is a list, and the name the
1217
+ // edge is about is written on its own line.
1218
+ ...this.lineField(spec.node),
1219
+ metadata: {
1220
+ isReExport: true,
1221
+ declaredName: spec.member,
1222
+ declaredKind: "named",
1223
+ // A re-export binds NO local name in this file (never-claim (d): a different
1224
+ // surface). The key is published on EVERY imports edge so the arbiter never has
1225
+ // to distinguish "no bindings" from "this row forgot the evidence".
1226
+ importedBindings: [],
1227
+ ...spec.exportedAs !== spec.member ? { exportedAs: spec.exportedAs } : {}
1228
+ }
1229
+ });
1230
+ }
1231
+ } else {
1232
+ relationships.push({
1233
+ source: FILE_LEVEL_SOURCE,
1234
+ target: `${modPath}:*`,
1235
+ type: ASTEngine.RelationshipType.Imports,
1236
+ // JD-J12 (M60, 2026-09-27): a clause-less re-export (`export *
1237
+ // from "m"`) names the MODULE and no member of it — `*` again. A
1238
+ // module path is not a symbol, so 1.0 overstated it the same way
1239
+ // it overstated `require` and the namespace import. Kept, marked,
1240
+ // and demoted: the re-export dependency is real.
1241
+ confidence: 0.6,
1242
+ // A re-export with no clause names the MODULE, so the written
1243
+ // name is the module-path argument itself.
1244
+ ...this.lineField(exportSource),
1245
+ // `starForm` is the discriminator, and it is the one piece of
1246
+ // evidence a resolver needs to tell a REDISTRIBUTION from a
1247
+ // namespace binding: only `all` may be followed for a name this
1248
+ // file does not publish.
1249
+ metadata: {
1250
+ isReExport: true,
1251
+ unresolved: true,
1252
+ starForm: namespaceAlias ? "namespace" : "all",
1253
+ // A clause-less re-export binds NO local name (never-claim (d)). Published on every
1254
+ // imports edge so the arbiter never has to distinguish "no bindings" from "missing
1255
+ // evidence".
1256
+ importedBindings: [],
1257
+ ...namespaceAlias ? { namespaceName: namespaceAlias } : {}
1258
+ }
1259
+ });
1260
+ }
1261
+ patterns["ES6 ReExports"] = true;
1262
+ }
1263
+ break;
1264
+ }
1265
+ }
1266
+ for (let i = relMark; i < relationships.length; i++) {
1267
+ relSpans.push({ idx: i, s: node.startIndex, e: node.endIndex });
1268
+ }
1269
+ for (let i = node.namedChildCount - 1; i >= 0; i--) {
1270
+ const child = node.namedChild(i);
1271
+ if (child) {
1272
+ stack.push({ node: child, scopes: scopeStack, inClassBody: nextInClassBody, functionDepth: nextFunctionDepth });
1273
+ }
1274
+ }
1275
+ }
1276
+ {
1277
+ const deadIdx = new Set(b5Zombies);
1278
+ if (deadSpans.length > 0) {
1279
+ for (let i = 0; i < symbols.length; i++) {
1280
+ const r = symbols[i].range;
1281
+ if (!r || r.startIndex === void 0 || r.endIndex === void 0) continue;
1282
+ for (const d of deadSpans) {
1283
+ if (d.s <= r.startIndex && r.endIndex <= d.e && (d.s !== r.startIndex || r.endIndex !== d.e)) {
1284
+ deadIdx.add(i);
1285
+ break;
1286
+ }
1287
+ }
1288
+ }
1289
+ }
1290
+ if (deadIdx.size > 0) {
1291
+ for (let i = symbols.length - 1; i >= 0; i--) if (deadIdx.has(i)) symbols.splice(i, 1);
1292
+ }
1293
+ }
1294
+ if (deadSpans.length > 0 && relSpans.length > 0) {
1295
+ const drop = /* @__PURE__ */ new Set();
1296
+ for (const r of relSpans) {
1297
+ for (const d of deadSpans) {
1298
+ if (d.s <= r.s && r.e <= d.e) {
1299
+ drop.add(r.idx);
1300
+ break;
1301
+ }
1302
+ }
1303
+ }
1304
+ if (drop.size > 0) {
1305
+ for (let i = relationships.length - 1; i >= 0; i--) if (drop.has(i)) relationships.splice(i, 1);
1306
+ }
1307
+ }
1308
+ {
1309
+ const bySource = /* @__PURE__ */ new Map();
1310
+ const qcOwnerOf = (n) => {
1311
+ let p = n.parent;
1312
+ while (p) {
1313
+ if (p.type === "class_declaration") return p.childForFieldName("name")?.text;
1314
+ if (p.type === "variable_declarator" || p.type === "object" || p.type === "pair" || p.type === "function_declaration" || p.type === "function_expression" || p.type === "arrow_function") return void 0;
1315
+ p = p.parent;
1316
+ }
1317
+ return void 0;
1318
+ };
1319
+ const qcResolve = (n, current) => {
1320
+ const fnName = n.childForFieldName?.("name")?.text;
1321
+ if ((n.type === "function_declaration" || n.type === "generator_function_declaration" || n.type === "method_definition") && fnName) {
1322
+ const owner = n.type === "method_definition" ? qcOwnerOf(n) : void 0;
1323
+ return owner ? `${owner}::${fnName}` : fnName;
1324
+ }
1325
+ if (n.type === "variable_declarator") {
1326
+ const nameNode = n.childForFieldName("name");
1327
+ const valueNode = n.childForFieldName("value");
1328
+ if (nameNode && valueNode && (valueNode.type === "arrow_function" || valueNode.type === "function_expression" || valueNode.type === "generator_function")) {
1329
+ return nameNode.text;
1330
+ }
1331
+ }
1332
+ if (n.type === "assignment_expression") {
1333
+ const left = n.childForFieldName("left");
1334
+ const right = n.childForFieldName("right");
1335
+ if (left && right && (right.type === "function_expression" || right.type === "arrow_function") && left.type === "member_expression") {
1336
+ const obj = left.childForFieldName("object");
1337
+ const prop = left.childForFieldName("property");
1338
+ if (obj && obj.type === "member_expression" && prop) {
1339
+ const protoProp = obj.childForFieldName("property");
1340
+ if (protoProp && protoProp.text === "prototype") {
1341
+ const owner = obj.childForFieldName("object")?.text;
1342
+ return owner ? `${owner}::${prop.text}` : prop.text;
1343
+ }
1344
+ }
1345
+ }
1346
+ }
1347
+ return current;
1348
+ };
1349
+ const qcWalk = (n, current) => {
1350
+ if (isNodeInDeadSpans(deadSpans, n)) return;
1351
+ current = qcResolve(n, current);
1352
+ if (n.type === "call_expression") {
1353
+ const fn = n.childForFieldName("function");
1354
+ const isImportCallee = !!fn && (fn.type === "import" || fn.type === "identifier" && fn.text === "import");
1355
+ if (fn && !(fn.type === "identifier" && fn.text === "require") && !isImportCallee) {
1356
+ const parts = [];
1357
+ let cur = fn;
1358
+ while (cur) {
1359
+ if (cur.type === "member_expression") {
1360
+ const prop2 = cur.childForFieldName("property");
1361
+ parts.unshift(prop2 ? prop2.text : cur.namedChild(cur.namedChildCount - 1)?.text ?? "");
1362
+ cur = cur.childForFieldName("object");
1363
+ } else if (cur.type === "call_expression") {
1364
+ cur = cur.namedChild(0);
1365
+ } else {
1366
+ parts.unshift(cur.text);
1367
+ break;
1368
+ }
1369
+ }
1370
+ const path2 = parts.filter(Boolean).join(".");
1371
+ if (current && path2) {
1372
+ const list = bySource.get(current) ?? [];
1373
+ list.push(path2);
1374
+ bySource.set(current, list);
1375
+ }
1376
+ }
1377
+ }
1378
+ for (let i = 0; i < n.namedChildCount; i++) {
1379
+ const c = n.namedChild(i);
1380
+ if (c) qcWalk(c, current);
1381
+ }
1382
+ };
1383
+ qcWalk(rootNode, null);
1384
+ for (const sym of symbols) {
1385
+ const key = sym.ownerName && sym.ownerKind === ASTEngine.SymbolKind.Class ? `${sym.ownerName}::${sym.name}` : sym.name;
1386
+ const list = bySource.get(key);
1387
+ if (list && list.length > 0) sym.qualified_calls = [...new Set(list)];
1388
+ }
1389
+ }
1390
+ this.bindingModel = this.collectBindingModel(rootNode);
1391
+ this.addContainmentRelationships(symbols, relationships);
1392
+ this.extractOverrideRelationships(symbols, relationships, context?.overrideCompanion);
1393
+ const receiverTypes = this.collectLocalReceiverTypes(rootNode);
1394
+ this.extractCallRelationships(rootNode, symbols, relationships, moduleName, receiverTypes, deadSpans);
1395
+ if (deadSpans.length > 0 && exports.length > 0) {
1396
+ for (let i = exports.length - 1; i >= 0; i--) {
1397
+ const r = exports[i].range;
1398
+ if (r && r.startIndex !== void 0 && r.endIndex !== void 0) {
1399
+ for (const d of deadSpans) {
1400
+ if (d.s <= r.startIndex && r.endIndex <= d.e) {
1401
+ exports.splice(i, 1);
1402
+ break;
1403
+ }
1404
+ }
1405
+ }
1406
+ }
1407
+ }
1408
+ this.extractExportRelationships(
1409
+ exports,
1410
+ relationships,
1411
+ FILE_LEVEL_SOURCE,
1412
+ new Set(symbols.map((sym) => String(sym?.name ?? "")).filter((n) => n.length > 0))
1413
+ );
1414
+ this.extractDecoratorRelationships(symbols, relationships);
1415
+ const architecturalScore = Math.min(100, Math.max(0, 100 - errors.length * 5));
1416
+ const detectedPatterns = Object.keys(patterns);
1417
+ const detectedFrameworks = Object.keys(frameworks);
1418
+ if (this.detectsJsx(rootNode)) {
1419
+ detectedFrameworks.push("React");
1420
+ detectedPatterns.push("JSX Components");
1421
+ }
1422
+ const frameworkPatterns = detectedPatterns.map((p) => ({
1423
+ framework: "JavaScript",
1424
+ patterns: [{ type: "usage", detected: true, description: p }]
1425
+ }));
1426
+ this.collectFieldAccessUses(rootNode, relationships, moduleName, receiverTypes, deadSpans);
1427
+ this.extractInstanceofRefs(rootNode, relationships, moduleName);
1428
+ for (const r of relationships) {
1429
+ if (r.source !== FILE_LEVEL_SOURCE) continue;
1430
+ r.metadata = { ...r.metadata ?? {}, fileLevel: true, scopeName: moduleName };
1431
+ }
1432
+ for (let i = relationships.length - 1; i >= 0; i--) {
1433
+ const r = relationships[i];
1434
+ if (!r.source || !r.target) relationships.splice(i, 1);
1435
+ }
1436
+ this.markExternalClaims(rootNode, relationships, symbols, context);
1437
+ this.classifyEvidenceTier(relationships);
1438
+ return {
1439
+ language: "javascript",
1440
+ symbols,
1441
+ relationships,
1442
+ imports,
1443
+ exports,
1444
+ errors,
1445
+ metadata: {
1446
+ // Real cyclomatic complexity — was the synthetic heuristic
1447
+ // symbols.length + relationships.length, which is not a complexity
1448
+ // metric at all (same defect previously fixed on groovy/scala).
1449
+ complexity: cyclomaticComplexity,
1450
+ linesOfCode: content.split("\n").length,
1451
+ cyclomaticComplexity,
1452
+ framework: detectedFrameworks.length > 1 ? detectedFrameworks[1] : detectedFrameworks[0],
1453
+ patterns: detectedPatterns,
1454
+ detectedFrameworks,
1455
+ frameworkPatterns,
1456
+ architecturalInsights: [],
1457
+ architecturalScore,
1458
+ codeQuality: errors.length === 0 ? "Excellent" : "Fair",
1459
+ hasTypeInference: false,
1460
+ hasFrameworkIntelligence: true,
1461
+ analysisVersion: "3.1.0-m12.10-family-contract"
1462
+ }
1463
+ };
1464
+ }
1465
+ // ============================================================================
1466
+ // RELATIONSHIP PASSES
1467
+ // ============================================================================
1468
+ addContainmentRelationships(symbols, relationships) {
1469
+ for (const symbol of symbols) {
1470
+ if (symbol.ownerName) {
1471
+ relationships.push({
1472
+ source: symbol.ownerName,
1473
+ target: symbol.name,
1474
+ type: ASTEngine.RelationshipType.Contains,
1475
+ confidence: 1,
1476
+ // The MEMBER's declaration line — the edge names the member.
1477
+ ...symbol.range?.startLine ? { line_number: symbol.range.startLine } : {},
1478
+ metadata: {}
1479
+ });
1480
+ }
1481
+ }
1482
+ }
1483
+ extractOverrideRelationships(symbols, relationships, overrideCompanion) {
1484
+ const byName = /* @__PURE__ */ new Map();
1485
+ for (const s of symbols) {
1486
+ const list = byName.get(s.name) ?? [];
1487
+ list.push(s);
1488
+ byName.set(s.name, list);
1489
+ }
1490
+ const baseByClass = /* @__PURE__ */ new Map();
1491
+ for (const r of relationships) {
1492
+ if (r.type === ASTEngine.RelationshipType.Extends && r.source && r.target) {
1493
+ baseByClass.set(r.source, r.target);
1494
+ }
1495
+ }
1496
+ const overrideSeen = /* @__PURE__ */ new Set();
1497
+ for (const s of symbols) {
1498
+ const isAccessorMember = s.kind === ASTEngine.SymbolKind.Property && s.metadata?.isAccessor === true;
1499
+ if ((s.kind === ASTEngine.SymbolKind.Method || s.kind === ASTEngine.SymbolKind.Constructor || isAccessorMember) && s.ownerName) {
1500
+ if (s.isStatic) continue;
1501
+ const base = baseByClass.get(s.ownerName);
1502
+ if (!base) continue;
1503
+ const isCtor = s.kind === ASTEngine.SymbolKind.Constructor || s.name === "constructor";
1504
+ const inFileDeclares = !isCtor && (byName.get(s.name)?.some((bm) => bm.ownerName === base && (bm.kind === ASTEngine.SymbolKind.Method || bm.kind === ASTEngine.SymbolKind.Property && bm.metadata?.isAccessor === true)) ?? false);
1505
+ if (inFileDeclares || !isCtor && overrideCompanion?.get(base)?.has(s.name)) {
1506
+ const ovKey = `${s.ownerName}.${s.name}->${base}.${s.name}`;
1507
+ if (overrideSeen.has(ovKey)) continue;
1508
+ overrideSeen.add(ovKey);
1509
+ relationships.push({
1510
+ source: `${s.ownerName}.${s.name}`,
1511
+ // THE TARGET IS THE MEMBER NAME, NOT `Owner.member`: the resolver binds against
1512
+ // DECLARATIONS, and a declaration is named by its leaf. Measured: 100% of this lane's
1513
+ // overrides targets are dotted, 100% have their leaf declared in the edge's own file, and
1514
+ // 100% classify unresolved — a naming problem, not a mechanism problem. `localEdgeName`
1515
+ // derives the leaf AFTER the paren deletion `localEdgeLabel` performs, so a dotted
1516
+ // parameter list cannot be cut into a non-name (see its doc).
1517
+ target: s.name,
1518
+ type: ASTEngine.RelationshipType.Overrides,
1519
+ confidence: 0.95,
1520
+ // The OVERRIDING member's own line, not the class's.
1521
+ ...s.range?.startLine ? { line_number: s.range.startLine } : {},
1522
+ metadata: { reasonCode: "verified-in-file" }
1523
+ });
1524
+ } else if (!isCtor && !overrideCompanion?.has(base)) {
1525
+ const baseInFile = symbols.some((s2) => s2.name === base && s2.kind === ASTEngine.SymbolKind.Class);
1526
+ if (baseInFile) continue;
1527
+ if (s.name.startsWith("#")) continue;
1528
+ if (isJsBuiltin(base) && !jsBuiltinHasMember(base, s.name)) continue;
1529
+ const guessKey = `${s.ownerName}.${s.name}->${base}.${s.name}`;
1530
+ if (overrideSeen.has(guessKey)) continue;
1531
+ overrideSeen.add(guessKey);
1532
+ relationships.push({
1533
+ source: `${s.ownerName}.${s.name}`,
1534
+ // THE TARGET IS THE MEMBER NAME, NOT `Owner.member`: the resolver binds against
1535
+ // DECLARATIONS, and a declaration is named by its leaf. Measured: 100% of this lane's
1536
+ // overrides targets are dotted, 100% have their leaf declared in the edge's own file, and
1537
+ // 100% classify unresolved — a naming problem, not a mechanism problem. `localEdgeName`
1538
+ // derives the leaf AFTER the paren deletion `localEdgeLabel` performs, so a dotted
1539
+ // parameter list cannot be cut into a non-name (see its doc).
1540
+ target: s.name,
1541
+ type: ASTEngine.RelationshipType.Overrides,
1542
+ confidence: 0.5,
1543
+ ...s.range?.startLine ? { line_number: s.range.startLine } : {},
1544
+ metadata: { reasonCode: "first-parent-guess" }
1545
+ });
1546
+ }
1547
+ }
1548
+ }
1549
+ }
1550
+ /**
1551
+ * RECEIVER TYPES for a member call on a LOCAL variable — the JavaScript twin of
1552
+ * the TypeScript pass (`collectLocalReceiverTypes` there, where the three-file
1553
+ * measurement and the reasoning live).
1554
+ *
1555
+ * JavaScript states a receiver's type in exactly one syntactic form: an
1556
+ * initialiser. `const c = new Foo()` is the whole rule here; there are no type
1557
+ * annotations to read, which is why this map is smaller than the TS one rather
1558
+ * than narrower by choice. A name declared with two different constructors in
1559
+ * one file is dropped: shadowing makes the file-level answer a guess, and a
1560
+ * wrong owner rewrites a binding that is currently right.
1561
+ */
1562
+ collectLocalReceiverTypes(root) {
1563
+ const types = /* @__PURE__ */ new Map();
1564
+ const conflicted = /* @__PURE__ */ new Set();
1565
+ const record = (name, type, evidence) => {
1566
+ if (name.length === 0 || type.length === 0) return;
1567
+ const previous = types.get(name);
1568
+ if (previous === void 0) {
1569
+ if (!conflicted.has(name)) types.set(name, { type, evidence });
1570
+ } else if (previous.type !== type) {
1571
+ types.delete(name);
1572
+ conflicted.add(name);
1573
+ }
1574
+ };
1575
+ const newCtorLeaf = (value) => {
1576
+ if (!value || value.type !== "new_expression") return null;
1577
+ const ctor = value.childForFieldName("constructor") ?? value.namedChild(0);
1578
+ if (ctor?.type === "identifier") return ctor.text;
1579
+ if (ctor?.type === "member_expression") return ctor.childForFieldName("property")?.text ?? null;
1580
+ return null;
1581
+ };
1582
+ const precedingDoc = (node) => {
1583
+ let stmt = node;
1584
+ while (stmt.parent && stmt.parent.type !== "program" && stmt.parent.type !== "statement_block" && stmt.parent.type !== "class_body" && stmt.parent.type !== "switch_body") {
1585
+ stmt = stmt.parent;
1586
+ }
1587
+ const prev = stmt.previousSibling;
1588
+ if (!prev || prev.type !== "comment") return null;
1589
+ const text = prev.text;
1590
+ return text.startsWith("/**") ? text : null;
1591
+ };
1592
+ const jsdocTypeOf = (node) => {
1593
+ const text = precedingDoc(node);
1594
+ if (text === null) return null;
1595
+ const at = text.indexOf("@type");
1596
+ if (at < 0) return null;
1597
+ const open = text.indexOf("{", at + 5);
1598
+ if (open < 0) return null;
1599
+ const close = text.indexOf("}", open + 1);
1600
+ if (close < 0) return null;
1601
+ const inner = text.slice(open + 1, close).trim();
1602
+ return isPlainJsIdentifier(inner) ? inner : null;
1603
+ };
1604
+ const jsdocParamsOf = (node) => {
1605
+ const out = /* @__PURE__ */ new Map();
1606
+ const text = precedingDoc(node);
1607
+ if (text === null) return out;
1608
+ let from = 0;
1609
+ for (; ; ) {
1610
+ const at = text.indexOf("@param", from);
1611
+ if (at < 0) break;
1612
+ from = at + 6;
1613
+ const open = text.indexOf("{", from);
1614
+ if (open < 0) break;
1615
+ const close = text.indexOf("}", open + 1);
1616
+ if (close < 0) break;
1617
+ const type = text.slice(open + 1, close).trim();
1618
+ let i = close + 1;
1619
+ while (i < text.length && (text[i] === " " || text[i] === " ")) i++;
1620
+ let end = i;
1621
+ while (end < text.length) {
1622
+ const ch = text[end];
1623
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "-" || ch === "*" || ch === "/") break;
1624
+ end++;
1625
+ }
1626
+ const name = text.slice(i, end);
1627
+ if (isPlainJsIdentifier(type) && isPlainJsIdentifier(name)) out.set(name, type);
1628
+ from = close + 1;
1629
+ }
1630
+ return out;
1631
+ };
1632
+ const paramScopes = [];
1633
+ {
1634
+ const st = [root];
1635
+ while (st.length > 0) {
1636
+ const n = st.pop();
1637
+ if (n.type === "function_declaration" || n.type === "function_expression" || n.type === "generator_function_declaration" || n.type === "generator_function" || n.type === "arrow_function" || n.type === "method_definition") {
1638
+ const types2 = jsdocParamsOf(n);
1639
+ if (types2.size > 0) {
1640
+ paramScopes.push({ startLine: n.startPosition.row + 1, endLine: n.endPosition.row + 1, types: types2 });
1641
+ }
1642
+ }
1643
+ for (let i = 0; i < n.namedChildCount; i++) {
1644
+ const c = n.namedChild(i);
1645
+ if (c) st.push(c);
1646
+ }
1647
+ }
1648
+ }
1649
+ const jsdocParamTypeAt = (line, name) => {
1650
+ let best = null;
1651
+ for (const scope of paramScopes) {
1652
+ if (line < scope.startLine || line > scope.endLine) continue;
1653
+ if (!scope.types.has(name)) continue;
1654
+ if (best === null || scope.endLine - scope.startLine < best.endLine - best.startLine) best = scope;
1655
+ }
1656
+ return best === null ? null : best.types.get(name) ?? null;
1657
+ };
1658
+ const stack = [root];
1659
+ while (stack.length) {
1660
+ const n = stack.pop();
1661
+ if (n.type === "variable_declarator") {
1662
+ const nameNode = n.childForFieldName("name");
1663
+ const value = n.childForFieldName("value");
1664
+ if (nameNode && nameNode.type === "identifier") {
1665
+ const ctor = newCtorLeaf(value);
1666
+ if (ctor !== null) record(nameNode.text, ctor, "new");
1667
+ else {
1668
+ const docType = jsdocTypeOf(n);
1669
+ if (docType !== null) record(nameNode.text, docType, "jsdoc");
1670
+ }
1671
+ }
1672
+ } else if (n.type === "field_definition") {
1673
+ const prop = n.childForFieldName("property");
1674
+ const value = n.childForFieldName("value");
1675
+ const ctor = newCtorLeaf(value);
1676
+ if (prop?.type === "property_identifier" && ctor !== null) {
1677
+ record(`this.${prop.text}`, ctor, "assignment");
1678
+ }
1679
+ } else if (n.type === "assignment_expression") {
1680
+ const left = n.childForFieldName("left");
1681
+ const right = n.childForFieldName("right");
1682
+ const ctor = newCtorLeaf(right);
1683
+ const line = n.startPosition.row + 1;
1684
+ if (left && ctor !== null) {
1685
+ if (left.type === "identifier") record(left.text, ctor, "assignment");
1686
+ else if (left.type === "member_expression") {
1687
+ const obj = left.childForFieldName("object");
1688
+ const prop = left.childForFieldName("property");
1689
+ if (obj?.type === "this" && prop?.type === "property_identifier") {
1690
+ record(`this.${prop.text}`, ctor, "assignment");
1691
+ }
1692
+ }
1693
+ } else if (left && right && left.type === "member_expression" && right.type === "identifier") {
1694
+ const obj = left.childForFieldName("object");
1695
+ const prop = left.childForFieldName("property");
1696
+ if (obj?.type === "this" && prop?.type === "property_identifier") {
1697
+ const paramType = jsdocParamTypeAt(line, right.text);
1698
+ if (paramType !== null) record(`this.${prop.text}`, paramType, "jsdoc");
1699
+ }
1700
+ }
1701
+ }
1702
+ for (let i = 0; i < n.namedChildCount; i++) {
1703
+ const child = n.namedChild(i);
1704
+ if (child) stack.push(child);
1705
+ }
1706
+ }
1707
+ return types;
1708
+ }
1709
+ /**
1710
+ * THE CLASS `this` IS BOUND TO AT A CALL SITE — `null` when the call's `this` is not a
1711
+ * class instance (a plain function's dynamic `this`, or no class at all).
1712
+ *
1713
+ * PURE SYNTAX, no inference: an arrow function does not rebind `this`, so it is stepped
1714
+ * through; a `method_definition`'s own class is found by the same walk (the class ancestor
1715
+ * is ABOVE it), and a nested `function` expression/declaration rebinds `this`, so the walk
1716
+ * stops there and answers null. A static method or static block answers the CLASS, which is
1717
+ * what a class-like owner symbol is for.
1718
+ */
1719
+ enclosingThisClass(n) {
1720
+ let p = n.parent;
1721
+ while (p) {
1722
+ if (p.type === "class_declaration") return p.childForFieldName("name")?.text ?? null;
1723
+ if (p.type === "function_declaration" || p.type === "function_expression" || p.type === "generator_function_declaration" || p.type === "generator_function") return null;
1724
+ p = p.parent;
1725
+ }
1726
+ return null;
1727
+ }
1728
+ extractCallRelationships(node, symbols, relationships, moduleName, receiverTypes, deadSpans = []) {
1729
+ const resolveCurrent = (n, current) => {
1730
+ const fnName = n.childForFieldName?.("name")?.text;
1731
+ if ((n.type === "function_declaration" || n.type === "generator_function_declaration" || n.type === "method_definition" || n.type === "function_expression") && fnName) {
1732
+ return fnName;
1733
+ }
1734
+ if (n.type === "variable_declarator") {
1735
+ const nameNode = n.childForFieldName("name");
1736
+ const valueNode = n.childForFieldName("value");
1737
+ if (nameNode && nameNode.type === "identifier" && valueNode && (valueNode.type === "arrow_function" || valueNode.type === "function_expression")) {
1738
+ return nameNode.text;
1739
+ }
1740
+ }
1741
+ if (n.type === "class_declaration") {
1742
+ const nm = n.childForFieldName("name")?.text;
1743
+ if (nm) return nm;
1744
+ }
1745
+ if (n.type === "assignment_expression") {
1746
+ const left = n.childForFieldName("left");
1747
+ const right = n.childForFieldName("right");
1748
+ if (left && right && (right.type === "function_expression" || right.type === "arrow_function") && left.type === "member_expression") {
1749
+ const obj = left.childForFieldName("object");
1750
+ const prop = left.childForFieldName("property");
1751
+ if (obj && obj.type === "member_expression") {
1752
+ const protoProp = obj.childForFieldName("property");
1753
+ if (protoProp && protoProp.text === "prototype" && prop) {
1754
+ return prop.text;
1755
+ }
1756
+ }
1757
+ }
1758
+ }
1759
+ return current;
1760
+ };
1761
+ const processNode = (n, current) => {
1762
+ if (isNodeInDeadSpans(deadSpans, n)) return;
1763
+ if (this.inDecoratorPosition(n)) return;
1764
+ current = resolveCurrent(n, current);
1765
+ const callSource = current || moduleName;
1766
+ if (n.type === "call_expression") {
1767
+ const fn = n.childForFieldName("function");
1768
+ const isImportCallee = !!fn && (fn.type === "import" || fn.type === "identifier" && fn.text === "import");
1769
+ if (fn && !(fn.type === "identifier" && fn.text === "require" || isImportCallee)) {
1770
+ let targetName = fn.text;
1771
+ let fullCallee = fn.text;
1772
+ let skip = false;
1773
+ let nameNode = fn;
1774
+ if (fn.type === "member_expression") {
1775
+ const prop = fn.childForFieldName("property");
1776
+ if (prop) {
1777
+ targetName = prop.text;
1778
+ nameNode = prop;
1779
+ }
1780
+ } else if (fn.type === "subscript_expression") {
1781
+ skip = true;
1782
+ }
1783
+ if (skip) {
1784
+ } else if (targetName.length > 100 || targetName.includes("\n") || targetName.includes("`") || targetName.includes("${") || hasBraceEquals(targetName) || !isNameNode(nameNode)) {
1785
+ } else if (this.isJSBuiltinCall(targetName, fullCallee)) {
1786
+ } else {
1787
+ const boundCallee = fn.type === "identifier" ? this.localBindingTarget(fn, targetName, callSource) : null;
1788
+ const importedCallee = boundCallee ? null : fn.type === "identifier" ? this.importedBindingFor(fn, targetName) : null;
1789
+ let receiverImport = null;
1790
+ if (!boundCallee && !importedCallee && fn.type === "member_expression") {
1791
+ let recvRoot = fn.childForFieldName("object");
1792
+ while (recvRoot && recvRoot.type === "member_expression") recvRoot = recvRoot.childForFieldName("object");
1793
+ if (recvRoot?.type === "identifier") {
1794
+ const candidate = this.importedBindingFor(recvRoot, recvRoot.text);
1795
+ if (candidate && isExternalSpecifier(candidate.specifier)) receiverImport = candidate;
1796
+ }
1797
+ if (receiverImport && receiverImport.binding === targetName) receiverImport = null;
1798
+ }
1799
+ relationships.push({
1800
+ source: callSource,
1801
+ target: boundCallee ?? targetName,
1802
+ type: ASTEngine.RelationshipType.Calls,
1803
+ confidence: 0.9,
1804
+ // The callee LEAF — for `a.b.c()` the property node, so a chain's
1805
+ // call does not take the chain's opening line.
1806
+ ...this.lineField(fn.type === "member_expression" ? fn.childForFieldName("property") ?? fn : fn),
1807
+ metadata: {
1808
+ calleeExpression: fullCallee,
1809
+ ...boundCallee ? { localBinding: true } : {},
1810
+ ...importedCallee ? { importedBinding: importedCallee } : {},
1811
+ ...receiverImport ? {
1812
+ receiverImport,
1813
+ receiverImportSpecifier: receiverImport.specifier,
1814
+ receiverImportBinding: receiverImport.binding
1815
+ } : {},
1816
+ // The receiver's stated type, on the store's EXISTING channel: `metadata.owner`
1817
+ // is what `member-owner-resolution.ts` (claimOwnerCarryingTargets +
1818
+ // resolveMemberTargetsByOwner) reads on BOTH production resolution paths — it
1819
+ // re-resolves the row inside that class, which is what stops `c.run()` binding
1820
+ // to another file's same-named method. `ownerEvidence` says WHICH syntactic
1821
+ // proof produced the owner ({new, assignment, this, jsdoc, class-static,
1822
+ // this-alias}) so coverage is a
1823
+ // countable number per evidence class rather than one asserted figure. NO new
1824
+ // channel is introduced: a wrong owner is worse than none, because the store
1825
+ // withdraws the name binding for an owner-carrying row and then declines.
1826
+ ...fn.type === "member_expression" ? (() => {
1827
+ const object = fn.childForFieldName("object");
1828
+ if (!object) return {};
1829
+ let recv;
1830
+ if (object.type === "identifier") {
1831
+ recv = receiverTypes.get(object.text) ?? this.staticClassReceiver(object, object.text) ?? this.thisAliasOwnerAt(object, object.text) ?? void 0;
1832
+ } else if (object.type === "this") {
1833
+ const cls = this.enclosingThisClass(fn);
1834
+ if (cls) recv = { type: cls, evidence: "this" };
1835
+ } else if (object.type === "member_expression") {
1836
+ const inner = object.childForFieldName("object");
1837
+ const prop = object.childForFieldName("property");
1838
+ if (inner?.type === "this" && prop) recv = receiverTypes.get(`this.${prop.text}`);
1839
+ }
1840
+ return recv ? { owner: recv.type, ownerEvidence: recv.evidence } : {};
1841
+ })() : {}
1842
+ }
1843
+ });
1844
+ }
1845
+ }
1846
+ } else if (n.type === "new_expression") {
1847
+ const ctor = n.childForFieldName("constructor");
1848
+ if (ctor) {
1849
+ let ctorName = "";
1850
+ if (ctor.type === "identifier") ctorName = ctor.text;
1851
+ else if (ctor.type === "member_expression") {
1852
+ const prop = ctor.childForFieldName("property");
1853
+ ctorName = prop ? prop.text : ctor.text;
1854
+ }
1855
+ if (ctorName && !JS_GLOBAL_CONSTRUCTORS.has(ctorName)) {
1856
+ const boundCtor = ctor.type === "identifier" ? this.localBindingTarget(ctor, ctorName, callSource) : null;
1857
+ const importedCtor = boundCtor ? null : ctor.type === "identifier" ? this.importedBindingFor(ctor, ctorName) : null;
1858
+ relationships.push({
1859
+ source: callSource,
1860
+ target: boundCtor ?? ctorName,
1861
+ type: ASTEngine.RelationshipType.Instantiates,
1862
+ confidence: 0.95,
1863
+ // The CLASS NAME node: for `new ns.Thing()` the property leaf,
1864
+ // since the target is the leaf (TSD3 leaf identity).
1865
+ ...this.lineField(ctor.type === "member_expression" ? ctor.childForFieldName("property") ?? ctor : ctor),
1866
+ metadata: boundCtor ? { localBinding: true } : importedCtor ? { importedBinding: importedCtor } : {}
1867
+ });
1868
+ }
1869
+ }
1870
+ } else if (n.type === "jsx_element" || n.type === "jsx_self_closing_element") {
1871
+ const opening = n.type === "jsx_self_closing_element" ? n : n.namedChild(0);
1872
+ if (opening && (opening.type === "jsx_self_closing_element" || opening.type === "jsx_opening_element")) {
1873
+ const identifier = opening.namedChild(0);
1874
+ if (identifier && identifier.type === "identifier") {
1875
+ const char = identifier.text[0];
1876
+ if (char && char === char.toUpperCase() && char !== char.toLowerCase()) {
1877
+ const boundJsx = this.localBindingTarget(identifier, identifier.text, callSource);
1878
+ const importedJsx = boundJsx ? null : this.importedBindingFor(identifier, identifier.text);
1879
+ relationships.push({
1880
+ source: callSource,
1881
+ target: boundJsx ?? identifier.text,
1882
+ type: ASTEngine.RelationshipType.Renders,
1883
+ confidence: 0.9,
1884
+ ...this.lineField(identifier),
1885
+ metadata: {
1886
+ jsxElement: identifier.text,
1887
+ ...boundJsx ? { localBinding: true } : {},
1888
+ ...importedJsx ? { importedBinding: importedJsx } : {}
1889
+ }
1890
+ });
1891
+ }
1892
+ }
1893
+ }
1894
+ }
1895
+ for (let i = 0; i < n.namedChildCount; i++) {
1896
+ const child = n.namedChild(i);
1897
+ if (child) processNode(child, current);
1898
+ }
1899
+ };
1900
+ processNode(node, null);
1901
+ }
1902
+ extractExportRelationships(exports, relationships, moduleActor, declaredNames) {
1903
+ const CJS_PROPERTY_CLAIM_FORMS = /* @__PURE__ */ new Set([
1904
+ "module-exports-object",
1905
+ "exports-object",
1906
+ "deep-member-assignment"
1907
+ ]);
1908
+ for (const item of exports) {
1909
+ const isCjs = item.metadata?.viaCommonJs === true;
1910
+ const localName = typeof item.metadata?.localName === "string" ? String(item.metadata.localName) : "";
1911
+ const defaultForm = isCjs ? "" : item.metadata?.viaDefaultDeclaration === true ? "named-declaration" : item.metadata?.viaAliasDefault === true ? "export-alias" : item.metadata?.viaDefaultExpression === true ? "expression" : item.metadata?.viaAnonymousDefaultExpression === true ? "anonymous" : "anonymous";
1912
+ const claimMarkers = {};
1913
+ if (item.isDefault && !isCjs) {
1914
+ claimMarkers.defaultExportForm = defaultForm;
1915
+ if (localName) claimMarkers.defaultExportLocalName = localName;
1916
+ claimMarkers.defaultExport = { form: defaultForm, ...localName ? { localName } : {} };
1917
+ } else if (isCjs && !item.isDefault && !declaredNames.has(item.name) && CJS_PROPERTY_CLAIM_FORMS.has(String(item.metadata?.cjsForm ?? ""))) {
1918
+ const cjsForm = String(item.metadata?.cjsForm ?? "module-exports-unknown");
1919
+ claimMarkers.moduleExportsProperty = item.name;
1920
+ claimMarkers.moduleExportsForm = cjsForm;
1921
+ claimMarkers.moduleExports = { property: item.name, form: cjsForm };
1922
+ }
1923
+ relationships.push({
1924
+ source: moduleActor,
1925
+ target: item.name,
1926
+ type: ASTEngine.RelationshipType.Exports,
1927
+ confidence: 1,
1928
+ // The exported NAME's own line, from the export's range when the
1929
+ // collector recorded one. Absent rather than invented when it did not.
1930
+ ...item.range?.startLine ? { line_number: item.range.startLine } : {},
1931
+ // THE RECORD'S OWN metadata is carried through, not replaced by
1932
+ // `{isDefault}`. JSD53's ruling: JD-J14 decided a NAMED default
1933
+ // declaration is exported under `default` (ECMAScript) and kept the
1934
+ // declaration's own name in `metadata.localName` "so nothing is lost"
1935
+ // — and this line then dropped it, so the loss was total. The row's
1936
+ // target `default` matches no symbol (it is in the symbol denylist),
1937
+ // so `import Def from "./m"` could not resolve for any module with a
1938
+ // named default export, and `script/m40/verify-import-resolution.ts`
1939
+ // JD2.4 has been red since. `loadImportInputs` reads exactly this
1940
+ // `localName` (src/ast/import-resolution.ts) to recover the
1941
+ // declaration in the row's own file. Additive: `isDefault` stays, and
1942
+ // the engine's own evidenceClass/fileLevel are added later.
1943
+ metadata: { isDefault: item.isDefault, ...item.metadata ?? {}, ...claimMarkers }
1944
+ });
1945
+ }
1946
+ }
1947
+ // ============================================================================
1948
+ // HELPERS
1949
+ // ============================================================================
1950
+ /**
1951
+ * The CJS public-surface path BENEATH the base, read structurally.
1952
+ *
1953
+ * `module.exports.a.b = 1` -> ["a","b"]; `exports.g.x = 1` -> ["g","x"];
1954
+ * `exports.tag = 1` -> [] (an empty array, which is truthy — the caller
1955
+ * relies on that to tell "CJS surface" from "not CJS surface").
1956
+ *
1957
+ * Returns null when the chain is not a module surface (`svc.limit = 1`, whose
1958
+ * obj is a plain identifier) or when a segment is not statically nameable
1959
+ * (`module[expr].a = 1`, `(freeModule.exports = lodash)._ = 1`). An unnameable
1960
+ * segment yields NO export rather than a fabricated one — the defect class the
1961
+ * regex policy exists to prevent, measured on minified lodash where the text
1962
+ * form emitted the literal export name "(ie.exports=we)._".
1963
+ */
1964
+ cjsSurfacePath(obj) {
1965
+ const segs = [];
1966
+ let cur = obj;
1967
+ while (cur && (cur.type === "member_expression" || cur.type === "subscript_expression")) {
1968
+ if (cur.type === "subscript_expression") {
1969
+ const idx = cur.childForFieldName("index");
1970
+ if (!idx) return null;
1971
+ if (idx.type !== "string" && idx.type !== "number") return null;
1972
+ segs.unshift(stringContent(idx));
1973
+ } else {
1974
+ const prop = cur.childForFieldName("property");
1975
+ if (!prop) return null;
1976
+ segs.unshift(prop.text);
1977
+ }
1978
+ cur = cur.childForFieldName("object");
1979
+ }
1980
+ if (!cur) return null;
1981
+ if (cur.type === "member_expression" || cur.type === "subscript_expression") return null;
1982
+ if (cur.type !== "identifier") return null;
1983
+ if (cur.text === "module") return segs[0] === "exports" ? segs.slice(1) : null;
1984
+ if (cur.text === "exports") return segs;
1985
+ return null;
1986
+ }
1987
+ isJSBuiltinCall(targetName, fullCallee) {
1988
+ if (targetName === "super") return true;
1989
+ if (JS_CONSOLE_BUILTINS.has(fullCallee || "")) return true;
1990
+ const trimmed = stripWhitespace(fullCallee || "");
1991
+ if (JS_CONSOLE_BUILTINS.has(trimmed)) return true;
1992
+ if (JS_OBJECT_LEAF_METHODS.has(targetName)) return true;
1993
+ return false;
1994
+ }
1995
+ /**
1996
+ * M49 #868: the JS twin of the typescript analyzer's `decoratorLines`. The
1997
+ * name list is what the store reads on the symbol; the PARALLEL line list
1998
+ * lets the decorator's reference edge name the line the decorator is
1999
+ * written on. A name already recorded keeps its FIRST position, so the two
2000
+ * lists stay in lockstep by construction.
2001
+ */
2002
+ collectOwnDecoratorDetails(node) {
2003
+ const names = [];
2004
+ const lines = [];
2005
+ for (let i = 0; i < node.namedChildCount; i++) {
2006
+ const c = node.namedChild(i);
2007
+ if (!c || c.type !== "decorator") continue;
2008
+ const name = jsDecoratorName(c);
2009
+ if (name.length === 0 || names.includes(name)) continue;
2010
+ names.push(name);
2011
+ lines.push(c.startPosition.row + 1);
2012
+ }
2013
+ return { names, lines };
2014
+ }
2015
+ /**
2016
+ * M49 #868 — is this node written in DECORATOR position?
2017
+ *
2018
+ * A decorator is METADATA on the symbol it decorates, not a call or a read
2019
+ * made by whoever lexically encloses it. The family already rules this way
2020
+ * (typescript: `inDecoratorPosition`, real-typescript-analyzer.ts; the JVM
2021
+ * analyzers attach annotations as metadata and emit no edge; the python
2022
+ * oracle documents PYD1). javascript was the exception: a class-body
2023
+ * `@inject("db")` is syntactically a call expression, so the walk attributed
2024
+ * it to the enclosing CLASS — `calls Service -> inject` beside the
2025
+ * member-scoped edge that should carry it — and the bare-read pass turned
2026
+ * every decorator name into a `uses` edge from the decorated symbol.
2027
+ *
2028
+ * The walk stops at the enclosing statement/declaration boundary rather than
2029
+ * walking to the root: a decorator on a METHOD cannot make the method's own
2030
+ * body a decorator position (its decorators are siblings, never ancestors of
2031
+ * the body).
2032
+ */
2033
+ inDecoratorPosition(n) {
2034
+ if (!this.hasAnyDecoratorNode) return false;
2035
+ let p = n.parent;
2036
+ while (p) {
2037
+ if (p.type === "decorator") return true;
2038
+ if (p.type === "class_declaration" || p.type === "class" || p.type === "function_declaration" || p.type === "generator_function_declaration" || p.type === "method_definition" || p.type === "statement_block" || p.type === "program") {
2039
+ return false;
2040
+ }
2041
+ p = p.parent;
2042
+ }
2043
+ return false;
2044
+ }
2045
+ /**
2046
+ * M49 #868 — CLASS decorators, which the JS grammar places in TWO spots:
2047
+ * own children of `class_declaration`, or own children of the WRAPPING
2048
+ * `export_statement` when the class is exported (probe:
2049
+ * `export_statement children=[decorator@2, export@3, class_declaration@3]`).
2050
+ * The own-child reader alone therefore dropped every decorator on an
2051
+ * exported class — `@sealed export class A {}` produced no decorators on `A`
2052
+ * and no reference edge at all, while the typescript twin's
2053
+ * `collectDecoratorsForClass` already checks the export_statement.
2054
+ */
2055
+ collectClassDecoratorDetails(node) {
2056
+ const own = this.collectOwnDecoratorDetails(node);
2057
+ const wrapping = node.parent?.type === "export_statement" ? this.collectOwnDecoratorDetails(node.parent) : { names: [], lines: [] };
2058
+ if (wrapping.names.length === 0) return own;
2059
+ const names = [...own.names];
2060
+ const lines = [...own.lines];
2061
+ for (let i = 0; i < wrapping.names.length; i++) {
2062
+ if (names.includes(wrapping.names[i])) continue;
2063
+ names.push(wrapping.names[i]);
2064
+ lines.push(wrapping.lines[i]);
2065
+ }
2066
+ return { names, lines };
2067
+ }
2068
+ /**
2069
+ * M49 #868: a decorator is metadata on the symbol it decorates, so its name
2070
+ * is reachable as a REFERENCE from that symbol (the typescript twin's
2071
+ * `extractDecoratorRelationships`, and the shape the M59 #860 fixture pins
2072
+ * there). Emitted for every decorated symbol, with the decorator's OWN line:
2073
+ * a decorator whose line was never recorded stays line-less rather than
2074
+ * borrowing the declaration's line (never invent a line, #823).
2075
+ */
2076
+ extractDecoratorRelationships(symbols, relationships) {
2077
+ for (const symbol of symbols) {
2078
+ if (!symbol.decorators || symbol.decorators.length === 0) continue;
2079
+ const lines = symbol.metadata?.decoratorLines;
2080
+ for (let i = 0; i < symbol.decorators.length; i++) {
2081
+ const decorator = symbol.decorators[i];
2082
+ const line = lines?.[i];
2083
+ relationships.push({
2084
+ source: symbol.name,
2085
+ target: decorator,
2086
+ type: ASTEngine.RelationshipType.Decorates,
2087
+ confidence: 1,
2088
+ ...typeof line === "number" ? { line_number: line } : {},
2089
+ metadata: {
2090
+ isDecorator: true,
2091
+ decoratorLine: typeof line === "number" ? line : null
2092
+ }
2093
+ });
2094
+ }
2095
+ }
2096
+ }
2097
+ hasToken(node, token) {
2098
+ for (let i = 0; i < node.childCount; i++) {
2099
+ const child = node.child(i);
2100
+ if (!child || child.isNamed) continue;
2101
+ if (child.type === token) return true;
2102
+ }
2103
+ return false;
2104
+ }
2105
+ isConstDeclarator(node) {
2106
+ const parent = node.parent;
2107
+ if (parent && (parent.type === "lexical_declaration" || parent.type === "variable_declaration")) {
2108
+ const kind = parent.childForFieldName("kind");
2109
+ if (kind) return kind.text === "const";
2110
+ }
2111
+ return false;
2112
+ }
2113
+ /**
2114
+ * M59 (#825) follow-up: the destructuring walk carries the binding NODE so each
2115
+ * destructured symbol ranges at its own NAME
2116
+ * instead of the whole declarator (`const {` for a wrapped binding list).
2117
+ * Names and nodes stay in lockstep by construction (single walk).
2118
+ */
2119
+ collectPatternPairs(pattern) {
2120
+ const out = [];
2121
+ const walk = (n) => {
2122
+ switch (n.type) {
2123
+ case "shorthand_property_identifier":
2124
+ case "shorthand_property_identifier_pattern":
2125
+ case "identifier":
2126
+ if (n.text) out.push({ name: n.text, node: n });
2127
+ break;
2128
+ case "pair_pattern": {
2129
+ const value = n.childForFieldName("value");
2130
+ if (value) walk(value);
2131
+ break;
2132
+ }
2133
+ case "assignment_pattern":
2134
+ case "object_assignment_pattern": {
2135
+ const left = n.childForFieldName("left");
2136
+ if (left) walk(left);
2137
+ break;
2138
+ }
2139
+ case "rest_pattern": {
2140
+ for (let i = 0; i < n.namedChildCount; i++) {
2141
+ const c = n.namedChild(i);
2142
+ if (c) walk(c);
2143
+ }
2144
+ break;
2145
+ }
2146
+ default:
2147
+ for (let i = 0; i < n.namedChildCount; i++) {
2148
+ const c = n.namedChild(i);
2149
+ if (c) walk(c);
2150
+ }
2151
+ }
2152
+ };
2153
+ walk(pattern);
2154
+ return out;
2155
+ }
2156
+ extractParameters(paramsNode) {
2157
+ if (!paramsNode) return [];
2158
+ const out = [];
2159
+ for (let i = 0; i < paramsNode.namedChildCount; i++) {
2160
+ const p = paramsNode.namedChild(i);
2161
+ if (!p) continue;
2162
+ if (p.type === "identifier") {
2163
+ out.push({ name: p.text });
2164
+ } else if (p.type === "assignment_pattern") {
2165
+ const left = p.childForFieldName("left");
2166
+ if (left && left.type === "identifier") out.push({ name: left.text });
2167
+ else if (left && (left.type === "object_pattern" || left.type === "array_pattern")) {
2168
+ out.push({ name: p.text.slice(0, 60) });
2169
+ }
2170
+ } else if (p.type === "object_pattern" || p.type === "array_pattern") {
2171
+ out.push({ name: p.text.slice(0, 60) });
2172
+ } else if (p.type === "rest_pattern") {
2173
+ const inner = this.findChildByType(p, "identifier");
2174
+ out.push({ name: inner ? inner.text : "..." });
2175
+ }
2176
+ }
2177
+ return out;
2178
+ }
2179
+ methodComplexity(node) {
2180
+ let c = 1;
2181
+ const stack = [node];
2182
+ while (stack.length) {
2183
+ const n = stack.pop();
2184
+ if (n.type === "if_statement" || n.type === "for_statement" || n.type === "for_in_statement" || n.type === "do_statement" || n.type === "while_statement" || n.type === "catch_clause" || n.type === "switch_case" || n.type === "ternary_expression") {
2185
+ c++;
2186
+ } else if (n.type === "binary_expression") {
2187
+ const op = n.childForFieldName("operator")?.text;
2188
+ if (op === "&&" || op === "||" || op === "??") c++;
2189
+ }
2190
+ for (let i = 0; i < n.namedChildCount; i++) {
2191
+ const child = n.namedChild(i);
2192
+ if (child) stack.push(child);
2193
+ }
2194
+ }
2195
+ return c;
2196
+ }
2197
+ collectComments(node) {
2198
+ const comments = [];
2199
+ const scanNode = node.parent && node.parent.type === "export_statement" ? node.parent : node;
2200
+ let sibling = scanNode.previousSibling;
2201
+ let anchorRow = scanNode.startPosition.row;
2202
+ while (sibling) {
2203
+ if (sibling.type === "comment") {
2204
+ if (sibling.endPosition.row + 1 < anchorRow) break;
2205
+ comments.unshift(stripCommentMarkers(sibling.text));
2206
+ anchorRow = sibling.startPosition.row;
2207
+ sibling = sibling.previousSibling;
2208
+ } else {
2209
+ break;
2210
+ }
2211
+ }
2212
+ return comments.length > 0 ? comments : void 0;
2213
+ }
2214
+ collectModuleExports(right, exports, node, isDefault, surface) {
2215
+ const form = (kind) => `${surface}-${kind}`;
2216
+ if (right.type === "object") {
2217
+ for (let i = 0; i < right.namedChildCount; i++) {
2218
+ const c = right.namedChild(i);
2219
+ if (!c) continue;
2220
+ if (c.type === "shorthand_property_identifier" || c.type === "identifier") {
2221
+ exports.push({ name: c.text, isDefault: false, range: this.nodeToRange(c), metadata: { viaCommonJs: true, cjsForm: form("object") } });
2222
+ } else if (c.type === "pair") {
2223
+ const key = c.childForFieldName("key");
2224
+ if (key && (key.type === "property_identifier" || key.type === "identifier")) {
2225
+ exports.push({ name: key.text, isDefault: false, range: this.nodeToRange(key), metadata: { viaCommonJs: true, cjsForm: form("object") } });
2226
+ }
2227
+ } else if (c.type === "method_definition") {
2228
+ const nm = c.childForFieldName("name");
2229
+ if (nm) exports.push({ name: nm.text, isDefault: false, range: this.nodeToRange(nm), metadata: { viaCommonJs: true, cjsForm: form("object") } });
2230
+ }
2231
+ }
2232
+ } else if (right.type === "identifier") {
2233
+ exports.push({ name: right.text, isDefault, range: this.nodeToRange(right), metadata: { viaCommonJs: true, cjsForm: form("binding") } });
2234
+ } else if (right.type === "function_expression" || right.type === "arrow_function" || right.type === "class_declaration" || right.type === "class") {
2235
+ const nm = right.type === "class_declaration" || right.type === "class" ? right.childForFieldName("name")?.text : void 0;
2236
+ exports.push({ name: nm ?? "default", isDefault, range: this.nodeToRange(right), metadata: { viaCommonJs: true, cjsForm: form("expression") } });
2237
+ }
2238
+ {
2239
+ const seen = /* @__PURE__ */ new Set();
2240
+ for (let i = exports.length - 1; i >= 0; i--) {
2241
+ if (seen.has(exports[i].name)) exports.splice(i, 1);
2242
+ else seen.add(exports[i].name);
2243
+ }
2244
+ }
2245
+ }
2246
+ collectExportStatement(node, exports) {
2247
+ let isDefault = false;
2248
+ for (let i = 0; i < node.childCount; i++) {
2249
+ const t = node.child(i);
2250
+ if (t && !t.isNamed && t.text === "default") {
2251
+ isDefault = true;
2252
+ break;
2253
+ }
2254
+ }
2255
+ let foundNamed = false;
2256
+ for (let i = 0; i < node.namedChildCount; i++) {
2257
+ const child = node.namedChild(i);
2258
+ if (!child) continue;
2259
+ if (child.type === "identifier" && !isDefault) {
2260
+ foundNamed = true;
2261
+ exports.push({ name: child.text, isDefault, range: this.nodeToRange(child) });
2262
+ } else if (child.type === "export_clause") {
2263
+ for (let j = 0; j < child.namedChildCount; j++) {
2264
+ const spec = child.namedChild(j);
2265
+ if (spec && spec.type === "export_specifier") {
2266
+ const exportName = spec.childForFieldName("alias")?.text ?? spec.childForFieldName("name")?.text ?? this.findChildByType(spec, "identifier")?.text;
2267
+ const aliasNode = spec.childForFieldName("alias");
2268
+ const isAsDefault = !!aliasNode && aliasNode.text === "default";
2269
+ if (exportName) {
2270
+ foundNamed = true;
2271
+ exports.push({
2272
+ name: isAsDefault ? "default" : exportName,
2273
+ isDefault: isAsDefault,
2274
+ range: this.nodeToRange(spec),
2275
+ metadata: isAsDefault ? { viaAliasDefault: true, localName: spec.childForFieldName("name")?.text } : void 0
2276
+ });
2277
+ }
2278
+ }
2279
+ }
2280
+ } else if (child.type === "function_declaration" || child.type === "generator_function_declaration" || child.type === "class_declaration" || child.type === "function_expression" || child.type === "arrow_function" || child.type === "class") {
2281
+ const n = child.childForFieldName("name");
2282
+ foundNamed = true;
2283
+ exports.push({
2284
+ name: isDefault ? "default" : n?.text ?? "default",
2285
+ isDefault,
2286
+ range: this.nodeToRange(child),
2287
+ ...isDefault && n?.text ? { metadata: { viaDefaultDeclaration: true, localName: n.text } } : {}
2288
+ });
2289
+ } else if (child.type === "lexical_declaration" || child.type === "variable_declaration") {
2290
+ for (let j = 0; j < child.namedChildCount; j++) {
2291
+ const decl = child.namedChild(j);
2292
+ if (decl && decl.type === "variable_declarator") {
2293
+ const nm = decl.childForFieldName("name");
2294
+ if (nm && nm.type === "identifier") {
2295
+ foundNamed = true;
2296
+ exports.push({ name: nm.text, isDefault, range: this.nodeToRange(nm) });
2297
+ }
2298
+ }
2299
+ }
2300
+ }
2301
+ }
2302
+ if (isDefault && !foundNamed) {
2303
+ const valueNode = node.childForFieldName("value");
2304
+ const isIdentifierValue = valueNode?.type === "identifier";
2305
+ exports.push({
2306
+ name: "default",
2307
+ isDefault: true,
2308
+ range: this.nodeToRange(node),
2309
+ // EVIDENCE for the exports claim: the statement form, and — when the defaulted expression IS
2310
+ // a plain identifier — the local binding it names (`export default foo`). An anonymous
2311
+ // expression (literal, object, call, anonymous function) states no local name and says so
2312
+ // rather than inventing one.
2313
+ metadata: isIdentifierValue ? { viaDefaultExpression: true, localName: valueNode.text } : { viaAnonymousDefaultExpression: true }
2314
+ });
2315
+ }
2316
+ {
2317
+ let sawDefault = false;
2318
+ for (let i = exports.length - 1; i >= 0; i--) {
2319
+ if (exports[i].name !== "default") continue;
2320
+ if (sawDefault) exports.splice(i, 1);
2321
+ else sawDefault = true;
2322
+ }
2323
+ }
2324
+ {
2325
+ const seenNames = /* @__PURE__ */ new Set();
2326
+ for (let i = exports.length - 1; i >= 0; i--) {
2327
+ if (seenNames.has(exports[i].name)) exports.splice(i, 1);
2328
+ else seenNames.add(exports[i].name);
2329
+ }
2330
+ }
2331
+ }
2332
+ findChildByType(node, type) {
2333
+ for (let i = 0; i < node.namedChildCount; i++) {
2334
+ const child = node.namedChild(i);
2335
+ if (child && child.type === type) return child;
2336
+ }
2337
+ return null;
2338
+ }
2339
+ /**
2340
+ * M14 / R3 — THE CALL-SHAPED CLASS HERITAGE. A class's supertype may be a CALL, and there are
2341
+ * TWO shapes of it, and they put the base and the mixin in OPPOSITE positions:
2342
+ *
2343
+ * (A) `class X extends Base.extend(M1, M2)` — a MEMBER call. The RECEIVER names the BASE and
2344
+ * the ARGUMENTS name the MIXINS composed into X's supertype chain. This is the Ember /
2345
+ * Backbone class-extension idiom, and it is the shape the corpus overwhelmingly has.
2346
+ * (B) `class X extends MixinFn(Base)` — a plain FUNCTION call. The CALLEE names the MIXIN and
2347
+ * the ARGUMENT names the BASE. This is the TypeScript/Redwood mixin-factory idiom, where
2348
+ * `MixinFn(Base)` evaluates to a class that extends Base.
2349
+ *
2350
+ * WHY THE SHAPE, NOT A NAME, DECIDES: C2 forbids a naming convention as the discriminator.
2351
+ * `Route.extend(M)` and `_.extend(obj, props)` are the same member-call shape, and only the
2352
+ * callee's SHAPE (member vs plain) tells the two apart structurally. The discriminator here is
2353
+ * the node shape of the heritage's own expression — `member_expression` versus `identifier` —
2354
+ * and the role of each operand follows from that shape. It is not "the method is called extend".
2355
+ *
2356
+ * WHY IT MATTERS: `findChildByType` scans the DIRECT named children of the node it is given, and
2357
+ * a call-shaped heritage's single direct named child is the `call_expression` — so the base's
2358
+ * `identifier` and `member_expression` are GRANDCHILDREN of it. Before this, every call-shaped
2359
+ * heritage published NO edge at all: neither the base nor the mixin. Measured over the admitted
2360
+ * population by `script/m14/javascript-heritage-census.ts`: 76 call-shaped clauses, of which the
2361
+ * analyzer's own lookup pair found 0.
2362
+ *
2363
+ * WHAT IT DELIBERATELY DOES NOT DO: it does not look inside the call for a base, so it never
2364
+ * claims a base that the call computes rather than names — the 11 arguments that are themselves
2365
+ * calls (`SortableFactory([...])`) contribute a mixin FACTORY, and this publishes the factory's
2366
+ * own name only when the argument names something.
2367
+ */
2368
+ callShapedHeritage(heritage) {
2369
+ const call = this.findChildByType(heritage, "call_expression");
2370
+ if (!call) return null;
2371
+ const callee = call.childForFieldName("function");
2372
+ if (!callee) return null;
2373
+ const args = this.findChildByType(call, "arguments");
2374
+ const argNodes = [];
2375
+ if (args) {
2376
+ for (let i = 0; i < args.namedChildCount; i++) {
2377
+ const a = args.namedChild(i);
2378
+ if (a) argNodes.push(a);
2379
+ }
2380
+ }
2381
+ if (callee.type === "member_expression") {
2382
+ const receiver = callee.childForFieldName("object");
2383
+ return { base: receiver ?? null, mixins: argNodes, shape: "member" };
2384
+ }
2385
+ if (callee.type === "identifier") {
2386
+ return { base: argNodes[0] ?? null, mixins: [callee], shape: "function" };
2387
+ }
2388
+ return null;
2389
+ }
2390
+ /**
2391
+ * M14 / R3 — the relationship rows a call-shaped heritage publishes. The base becomes an
2392
+ * `extends` edge and each mixin becomes a `mixes_in` edge, with the same binding resolution the
2393
+ * identifier form already uses, so the rows are resolvable on exactly the same terms.
2394
+ */
2395
+ callShapedHeritageRows(heritage, source) {
2396
+ const shape = this.callShapedHeritage(heritage);
2397
+ const out = { base: null, mixins: [] };
2398
+ if (!shape) return out;
2399
+ const resolve = (node) => {
2400
+ if (!node) return null;
2401
+ if (node.type === "identifier") {
2402
+ const bound = this.localBindingTarget(node, node.text, source);
2403
+ const imported = bound ? null : this.importedBindingFor(node, node.text);
2404
+ return {
2405
+ target: bound ?? node.text,
2406
+ node,
2407
+ metadata: bound ? { localBinding: true } : imported ? { importedBinding: imported } : {}
2408
+ };
2409
+ }
2410
+ if (node.type === "member_expression") {
2411
+ return { target: node.text, node, metadata: {} };
2412
+ }
2413
+ return null;
2414
+ };
2415
+ const base = resolve(shape.base);
2416
+ if (base) out.base = base;
2417
+ for (const m of shape.mixins) {
2418
+ const r = resolve(m);
2419
+ if (r) out.mixins.push(r);
2420
+ }
2421
+ return out;
2422
+ }
2423
+ detectsJsx(rootNode) {
2424
+ const stack = [rootNode];
2425
+ while (stack.length > 0) {
2426
+ const node = stack.pop();
2427
+ if (node.type === "jsx_element" || node.type === "jsx_self_closing_element") return true;
2428
+ for (let i = 0; i < node.namedChildCount; i++) {
2429
+ const child = node.namedChild(i);
2430
+ if (child) stack.push(child);
2431
+ }
2432
+ }
2433
+ return false;
2434
+ }
2435
+ /**
2436
+ * Field-access uses: member_expression nodes emit root-anchored Uses edges
2437
+ * @0.6 with isFieldAccess marker - parity with java/groovy/kotlin
2438
+ * collectors. Nested member_expressions mean each chain segment appears as
2439
+ * a target (anti-skip guarantee); optional chains (?.) are
2440
+ * member_expression nodes with optional_chain tokens in this grammar.
2441
+ */
2442
+ /**
2443
+ * M14 — instanceof type references. `x instanceof T` is JavaScript's only
2444
+ * unambiguous named-type usage; emitted as References from the enclosing
2445
+ * function/class. The right operand lexes as identifier (expression
2446
+ * position), so no type_identifier filtering applies.
2447
+ */
2448
+ extractInstanceofRefs(rootNode, relationships, moduleName) {
2449
+ const emitted = /* @__PURE__ */ new Set();
2450
+ const resolveSource = (start) => {
2451
+ let p = start.parent;
2452
+ while (p) {
2453
+ if (p.type === "class_declaration" || p.type === "function_declaration" || p.type === "generator_function_declaration" || p.type === "method_definition" || // M14 #419 J6: top-level const r = x instanceof T attributes the
2454
+ // reference to r (the enclosing declaration), never the global
2455
+ // placeholder (TS same-wave fix).
2456
+ p.type === "variable_declarator") {
2457
+ for (let i = 0; i < p.namedChildCount; i++) {
2458
+ const c = p.namedChild(i);
2459
+ if (!c) continue;
2460
+ if ((c.type === "identifier" || c.type === "property_identifier") && c.text) return c.text;
2461
+ }
2462
+ }
2463
+ p = p.parent;
2464
+ }
2465
+ return moduleName;
2466
+ };
2467
+ const walk = (n) => {
2468
+ if (n.type === "binary_expression") {
2469
+ let isInstanceof = false;
2470
+ for (let i = 0; i < n.childCount; i++) {
2471
+ const c = n.child(i);
2472
+ if (c && !c.isNamed && c.text === "instanceof") {
2473
+ isInstanceof = true;
2474
+ break;
2475
+ }
2476
+ }
2477
+ if (isInstanceof && n.namedChildCount >= 2) {
2478
+ const right = n.namedChild(n.namedChildCount - 1);
2479
+ if (right) {
2480
+ const source = resolveSource(n);
2481
+ const key = `${source}|${right.text}`;
2482
+ if (!emitted.has(key)) {
2483
+ emitted.add(key);
2484
+ const boundRef = right.type === "identifier" ? this.localBindingTarget(right, right.text, source) : null;
2485
+ const importedRef = boundRef ? null : right.type === "identifier" ? this.importedBindingFor(right, right.text) : null;
2486
+ relationships.push({
2487
+ source,
2488
+ target: boundRef ?? right.text,
2489
+ type: ASTEngine.RelationshipType.References,
2490
+ confidence: 0.95,
2491
+ ...this.lineField(right),
2492
+ metadata: {
2493
+ relationshipType: "instanceof_check",
2494
+ ...boundRef ? { localBinding: true } : {},
2495
+ ...importedRef ? { importedBinding: importedRef } : {}
2496
+ }
2497
+ });
2498
+ }
2499
+ }
2500
+ }
2501
+ }
2502
+ for (let i = 0; i < n.namedChildCount; i++) {
2503
+ const c = n.namedChild(i);
2504
+ if (c) walk(c);
2505
+ }
2506
+ };
2507
+ walk(rootNode);
2508
+ }
2509
+ collectFieldAccessUses(rootNode, relationships, moduleName, receiverTypes, deadSpans = []) {
2510
+ const stack = [
2511
+ { node: rootNode, owner: null, method: null, module: moduleName }
2512
+ ];
2513
+ const seen = /* @__PURE__ */ new Set();
2514
+ while (stack.length > 0) {
2515
+ const item = stack.pop();
2516
+ const node = item.node;
2517
+ const owner = item.owner;
2518
+ const method = item.method;
2519
+ if (this.inDecoratorPosition(node)) continue;
2520
+ if (deadSpans.length > 0 && isNodeInDeadSpans(deadSpans, node)) continue;
2521
+ if (node.type === "class_declaration" || node.type === "class") {
2522
+ const nameNode = node.childForFieldName?.("name") ?? node.namedChildren.find((c) => c.type === "identifier");
2523
+ const nextOwner2 = nameNode ? nameNode.text : owner;
2524
+ for (let i = node.namedChildCount - 1; i >= 0; i--) {
2525
+ const child = node.namedChild(i);
2526
+ if (child) stack.push({ node: child, owner: nextOwner2, method: null, module: moduleName });
2527
+ }
2528
+ continue;
2529
+ }
2530
+ let nextMethod = method;
2531
+ let nextOwner = owner;
2532
+ if (node.type === "function_declaration" || node.type === "generator_function_declaration" || node.type === "method_definition") {
2533
+ const nameNode = node.childForFieldName?.("name");
2534
+ if (nameNode) nextMethod = nameNode.text;
2535
+ } else if (node.type === "variable_declarator") {
2536
+ const v = node.childForFieldName("value");
2537
+ const nm = node.childForFieldName("name")?.text;
2538
+ if (v && nm && (v.type === "arrow_function" || v.type === "function_expression")) nextMethod = nm;
2539
+ } else if (node.type === "arrow_function" || node.type === "function_expression") {
2540
+ const parent = node.parent;
2541
+ if (parent?.type === "assignment_expression") {
2542
+ const left = parent.childForFieldName("left");
2543
+ const obj = left?.childForFieldName("object");
2544
+ const prop = left?.childForFieldName("property");
2545
+ if (obj?.type === "member_expression" && obj.childForFieldName("property")?.text === "prototype" && prop) {
2546
+ nextMethod = prop.text;
2547
+ nextOwner = obj.childForFieldName("object")?.text ?? owner;
2548
+ }
2549
+ }
2550
+ }
2551
+ if (node.type === "member_expression") {
2552
+ const isCallCallee = node.parent?.type === "call_expression" && node.parent.childForFieldName("function")?.id === node.id;
2553
+ if (isCallCallee) {
2554
+ let root = node.childForFieldName("object");
2555
+ while (root && root.type === "member_expression") root = root.childForFieldName("object");
2556
+ const head = root && root.type === "identifier" ? root.text : "";
2557
+ if (head && head !== "this" && head !== "super") {
2558
+ const recvSource = method || owner || moduleName;
2559
+ const recvScopeId = this.localBindingScopeId(root, head);
2560
+ const recvBaseKey = `${recvSource}.#${head}`;
2561
+ const recvKey = recvScopeId ? `${recvBaseKey}|${recvScopeId}` : recvBaseKey;
2562
+ if (!seen.has(recvKey)) {
2563
+ seen.add(recvKey);
2564
+ seen.add(recvBaseKey);
2565
+ const boundRecv = this.localBindingTarget(root, head, recvSource);
2566
+ const importedRecv = boundRecv ? null : this.importedBindingFor(root, head);
2567
+ relationships.push({
2568
+ source: recvSource,
2569
+ target: boundRecv ?? head,
2570
+ type: ASTEngine.RelationshipType.Uses,
2571
+ confidence: 0.6,
2572
+ ...this.lineField(root),
2573
+ metadata: {
2574
+ isFieldAccess: true,
2575
+ bareRead: true,
2576
+ callReceiver: true,
2577
+ ...boundRecv ? { localBinding: true } : {},
2578
+ ...recvScopeId ? { localBindingScope: recvScopeId } : {},
2579
+ ...importedRecv ? { importedBinding: importedRecv } : {}
2580
+ }
2581
+ });
2582
+ }
2583
+ }
2584
+ }
2585
+ if (!isCallCallee) {
2586
+ const parts = [];
2587
+ let cur = node;
2588
+ while (cur && cur.type === "member_expression") {
2589
+ const prop = cur.childForFieldName("property");
2590
+ if (prop) parts.unshift({ seg: prop.text, node: prop });
2591
+ cur = cur.childForFieldName("object");
2592
+ }
2593
+ const head = cur ? cur.text : "";
2594
+ if (head && parts.length >= 1) {
2595
+ const pType = node.parent?.type;
2596
+ let isWrite = false;
2597
+ if (pType === "assignment_expression" || pType === "augmented_assignment_expression") {
2598
+ const lhs = node.parent.childForFieldName("left") ?? node.parent.namedChild(0);
2599
+ isWrite = !!lhs && lhs.startIndex === node.startIndex && lhs.endIndex === node.endIndex;
2600
+ }
2601
+ if (!isWrite) {
2602
+ if (head && head !== "this" && head !== "super" && cur?.type === "identifier") {
2603
+ const rootSource = method || owner || moduleName;
2604
+ const rootScopeId = this.localBindingScopeId(cur, head);
2605
+ const rootBaseKey = `${rootSource}.#${head}`;
2606
+ const rootKey = rootScopeId ? `${rootBaseKey}|${rootScopeId}` : rootBaseKey;
2607
+ if (!seen.has(rootKey)) {
2608
+ seen.add(rootKey);
2609
+ seen.add(rootBaseKey);
2610
+ const boundRoot = cur?.type === "identifier" ? this.localBindingTarget(cur, head, rootSource) : null;
2611
+ const importedRoot = boundRoot ? null : cur?.type === "identifier" ? this.importedBindingFor(cur, head) : null;
2612
+ relationships.push({
2613
+ source: rootSource,
2614
+ target: boundRoot ?? head,
2615
+ type: ASTEngine.RelationshipType.Uses,
2616
+ confidence: 0.6,
2617
+ // The root identifier's own line (the chain may open on a
2618
+ // line the segments do not share).
2619
+ ...this.lineField(cur),
2620
+ metadata: {
2621
+ isFieldAccess: true,
2622
+ chainRoot: true,
2623
+ ...boundRoot ? { localBinding: true } : {},
2624
+ ...rootScopeId ? { localBindingScope: rootScopeId } : {},
2625
+ ...importedRoot ? { importedBinding: importedRoot } : {}
2626
+ }
2627
+ });
2628
+ }
2629
+ }
2630
+ for (const { seg, node: segNode } of parts) {
2631
+ const source = method || owner || moduleName;
2632
+ const key = seg.startsWith("#") ? `${source}.$private${seg}` : `${source}.${seg}`;
2633
+ if (!seen.has(key)) {
2634
+ seen.add(key);
2635
+ const recvOwner = (() => {
2636
+ if (cur?.type === "identifier" && head) {
2637
+ return receiverTypes.get(head) ?? this.staticClassReceiver(cur, head) ?? this.thisAliasOwnerAt(cur, head) ?? void 0;
2638
+ }
2639
+ if (cur?.type === "this") {
2640
+ const cls = this.enclosingThisClass(node);
2641
+ return cls ? { type: cls, evidence: "this" } : void 0;
2642
+ }
2643
+ return void 0;
2644
+ })();
2645
+ relationships.push({
2646
+ source,
2647
+ target: seg,
2648
+ type: ASTEngine.RelationshipType.Uses,
2649
+ confidence: 0.6,
2650
+ ...this.lineField(segNode),
2651
+ // `receiverRoot` is the chain's FIRST identifier when it IS an identifier
2652
+ // (`location.hash.substring` -> `location`), and empty for a call-rooted chain
2653
+ // (`$(el).removeClass` -> ""). It is published so the terminal claim pass can ask
2654
+ // memberChainClaim the question the rule is defined on — the RECEIVER ROOT — without
2655
+ // re-deriving the chain from text. Additive; consumers that do not read it are unaffected.
2656
+ metadata: {
2657
+ isFieldAccess: true,
2658
+ ...cur?.type === "identifier" && head ? { receiverRoot: head } : {},
2659
+ ...recvOwner ? { owner: recvOwner.type, ownerEvidence: recvOwner.evidence } : {}
2660
+ }
2661
+ });
2662
+ }
2663
+ }
2664
+ }
2665
+ }
2666
+ }
2667
+ }
2668
+ if (node.type === "identifier") {
2669
+ const txt = node.text;
2670
+ if (txt !== "this" && txt !== "super" && txt !== "undefined" && txt !== "arguments") {
2671
+ const p7 = node.parent;
2672
+ const calleeFn = p7?.type === "call_expression" ? p7.childForFieldName("function") : null;
2673
+ const isCallee = !!calleeFn && calleeFn.id === node.id;
2674
+ let isWrite2 = false;
2675
+ if (p7?.type === "assignment_expression" || p7?.type === "augmented_assignment_expression") {
2676
+ const lhs2 = p7.childForFieldName("left") ?? p7.namedChild(0);
2677
+ isWrite2 = !!lhs2 && lhs2.id === node.id;
2678
+ }
2679
+ const isDeclName = p7?.type === "variable_declarator" && p7.childForFieldName("name")?.id === node.id || p7?.type === "function_declaration" || p7?.type === "function_expression" || p7?.type === "generator_function_declaration" || p7?.type === "generator_function" || p7?.type === "method_definition" || p7?.type === "class_declaration" || p7?.type === "class";
2680
+ const isPropKey = (p7?.type === "pair" || p7?.type === "pair_pattern") && p7.childForFieldName("key")?.id === node.id;
2681
+ const isMemberPart = p7?.type === "member_expression";
2682
+ const isPropName = p7?.type === "property_identifier";
2683
+ const isJsxName = p7?.type.startsWith("jsx_") ?? false;
2684
+ const isLabel = p7?.type === "labeled_statement";
2685
+ const isShorthandProp = p7?.type === "shorthand_property_identifier";
2686
+ const isImportBinding = p7?.type === "import_clause" || p7?.type === "import_specifier" || p7?.type === "namespace_import";
2687
+ const isParamBinding = p7?.type === "formal_parameters" || p7?.type === "arrow_function" || p7?.type === "catch_clause";
2688
+ const isPatternBinding = p7?.type === "rest_pattern" || p7?.type === "array_pattern" || p7?.type === "pair_pattern" || // `a = 1` in a pattern: the LEFT is the binding, the RIGHT is a
2689
+ // genuine read (`function f(a = b)` must still read b).
2690
+ p7?.type === "assignment_pattern" && (p7.childForFieldName("left") ?? p7.namedChild(0))?.id === node.id;
2691
+ const isExportSpecifier = p7?.type === "export_specifier";
2692
+ const isBindingSite = isImportBinding || isParamBinding || isPatternBinding || isExportSpecifier;
2693
+ if (!isCallee && !isWrite2 && !isDeclName && !isPropKey && !isMemberPart && !isPropName && !isLabel && !isShorthandProp && !isJsxName && !isBindingSite) {
2694
+ const bSource = method || owner || moduleName;
2695
+ const bScopeId = this.localBindingScopeId(node, txt);
2696
+ const bBaseKey = `${bSource}.#${txt}`;
2697
+ const bKey = bScopeId ? `${bBaseKey}|${bScopeId}` : bBaseKey;
2698
+ if (!seen.has(bKey)) {
2699
+ seen.add(bKey);
2700
+ seen.add(bBaseKey);
2701
+ const boundRead = this.localBindingTarget(node, txt, bSource);
2702
+ const importedRead = boundRead ? null : this.importedBindingFor(node, txt);
2703
+ relationships.push({
2704
+ source: bSource,
2705
+ target: boundRead ?? txt,
2706
+ type: ASTEngine.RelationshipType.Uses,
2707
+ confidence: 0.6,
2708
+ ...this.lineField(node),
2709
+ metadata: {
2710
+ isFieldAccess: true,
2711
+ bareRead: true,
2712
+ ...boundRead ? { localBinding: true } : {},
2713
+ ...bScopeId ? { localBindingScope: bScopeId } : {},
2714
+ ...importedRead ? { importedBinding: importedRead } : {}
2715
+ }
2716
+ });
2717
+ }
2718
+ }
2719
+ }
2720
+ }
2721
+ if (node.type === "template_substitution") {
2722
+ const inCalleeOrWritePosition = (n) => {
2723
+ let cur = n;
2724
+ while (cur) {
2725
+ const p = cur.parent;
2726
+ if (!p || p.type === "template_substitution") break;
2727
+ if (p.type === "call_expression") {
2728
+ const fn = p.childForFieldName("function");
2729
+ if (fn && fn.startIndex <= n.startIndex && n.endIndex <= fn.endIndex) return true;
2730
+ }
2731
+ if (p.type === "assignment_expression" || p.type === "augmented_assignment_expression") {
2732
+ const left = p.childForFieldName("left") ?? p.namedChild(0);
2733
+ if (left && left.startIndex <= n.startIndex && n.endIndex <= left.endIndex) return true;
2734
+ }
2735
+ cur = p;
2736
+ }
2737
+ return false;
2738
+ };
2739
+ const sub = (n) => {
2740
+ for (let i = 0; i < n.namedChildCount; i++) {
2741
+ const c = n.namedChild(i);
2742
+ if (!c) continue;
2743
+ if (c.type === "identifier") {
2744
+ if (inCalleeOrWritePosition(c)) continue;
2745
+ const src2 = method || owner || moduleName;
2746
+ const key = `${src2}.${c.text}`;
2747
+ if (!seen.has(key)) {
2748
+ seen.add(key);
2749
+ seen.add(`${src2}.#${c.text}`);
2750
+ const boundTmpl = this.localBindingTarget(c, c.text, src2);
2751
+ const importedTmpl = boundTmpl ? null : this.importedBindingFor(c, c.text);
2752
+ relationships.push({
2753
+ source: src2,
2754
+ target: boundTmpl ?? c.text,
2755
+ type: ASTEngine.RelationshipType.Uses,
2756
+ confidence: 0.85,
2757
+ ...this.lineField(c),
2758
+ metadata: {
2759
+ isTemplateInterpolation: true,
2760
+ ...boundTmpl ? { localBinding: true } : {},
2761
+ ...importedTmpl ? { importedBinding: importedTmpl } : {}
2762
+ }
2763
+ });
2764
+ }
2765
+ } else if (c.type !== "identifier") {
2766
+ sub(c);
2767
+ }
2768
+ }
2769
+ };
2770
+ sub(node);
2771
+ }
2772
+ for (let i = node.namedChildCount - 1; i >= 0; i--) {
2773
+ const child = node.namedChild(i);
2774
+ if (child) stack.push({ node: child, owner: nextOwner, method: nextMethod, module: moduleName });
2775
+ }
2776
+ }
2777
+ }
2778
+ /**
2779
+ * BUILD THE POSITIONAL LEXICAL SCOPE MODEL — one walk of the tree, ranges in node offsets.
2780
+ *
2781
+ * THE RULES ARE THE LANGUAGE'S, not an approximation:
2782
+ * · scopes: program, every function form (declaration / expression / generator / arrow /
2783
+ * method / constructor / static block), blocks (`statement_block`, `switch_body`), loop
2784
+ * headers (`for`/`for_in`/`for_of`), catch clauses;
2785
+ * · `var` HOISTS to the nearest function-or-program scope; `let`/`const` bind in the current
2786
+ * block; parameters bind in the function scope; a catch parameter binds in its catch scope;
2787
+ * a function or class DECLARATION binds in its enclosing scope;
2788
+ * · a later same-scope redeclaration keeps one name (a Set), and an inner binding shadows an
2789
+ * outer one because resolution walks the scope chain from the innermost containing scope;
2790
+ * · import bindings and `require(...)` / `import(...)` declarators are recorded in
2791
+ * `moduleBindings` and are NEVER local-binding claims — they are the imported-binding seam;
2792
+ * · destructured/rest/default parameter and declarator patterns bind their VALUE sides
2793
+ * (`collectPatternPairs`, the same reader the symbol walk uses).
2794
+ */
2795
+ collectBindingModel(rootNode) {
2796
+ const scopes = [];
2797
+ const allNames = /* @__PURE__ */ new Set();
2798
+ const moduleBindings = /* @__PURE__ */ new Set();
2799
+ const moduleBindingSpecs = /* @__PURE__ */ new Map();
2800
+ const publishedClasses = /* @__PURE__ */ new Set();
2801
+ const program = {
2802
+ kind: "program",
2803
+ startIndex: rootNode.startIndex,
2804
+ endIndex: rootNode.endIndex,
2805
+ published: true,
2806
+ parent: null,
2807
+ names: /* @__PURE__ */ new Set(),
2808
+ decls: /* @__PURE__ */ new Map()
2809
+ };
2810
+ scopes.push(program);
2811
+ const addName = (scope, name, declNode) => {
2812
+ if (!name) return;
2813
+ scope.names.add(name);
2814
+ if (!scope.decls.has(name)) scope.decls.set(name, declNode?.startIndex ?? scope.startIndex);
2815
+ allNames.add(name);
2816
+ };
2817
+ const newScope = (node, kind, parent, published) => {
2818
+ const scope = { kind, startIndex: node.startIndex, endIndex: node.endIndex, published, parent, names: /* @__PURE__ */ new Set(), decls: /* @__PURE__ */ new Map() };
2819
+ scopes.push(scope);
2820
+ return scope;
2821
+ };
2822
+ const hoistTarget = (scope) => {
2823
+ let cur = scope;
2824
+ while (cur.kind !== "function" && cur.kind !== "program" && cur.parent) cur = cur.parent;
2825
+ return cur;
2826
+ };
2827
+ const addParams = (fnNode, fnScope) => {
2828
+ const paramsNode = fnNode.childForFieldName("parameters");
2829
+ if (paramsNode) {
2830
+ for (let i = 0; i < paramsNode.namedChildCount; i++) {
2831
+ const param = paramsNode.namedChild(i);
2832
+ if (!param) continue;
2833
+ for (const pair of this.collectPatternPairs(param)) addName(fnScope, pair.name, pair.node);
2834
+ }
2835
+ }
2836
+ const single = fnNode.childForFieldName("parameter");
2837
+ if (single) for (const pair of this.collectPatternPairs(single)) addName(fnScope, pair.name, pair.node);
2838
+ };
2839
+ const addImportBindings = (importNode) => {
2840
+ const clause = this.findChildByType(importNode, "import_clause");
2841
+ if (!clause) return;
2842
+ const sourceNode = importNode.childForFieldName("source");
2843
+ const specifier = sourceNode ? stringContent(sourceNode) : "";
2844
+ const add = (name, declNode) => {
2845
+ if (!name) return;
2846
+ addName(program, name, declNode);
2847
+ moduleBindings.add(name);
2848
+ if (specifier) moduleBindingSpecs.set(name, specifier);
2849
+ };
2850
+ for (let i = 0; i < clause.namedChildCount; i++) {
2851
+ const c = clause.namedChild(i);
2852
+ if (!c) continue;
2853
+ if (c.type === "identifier") add(c.text, c);
2854
+ else if (c.type === "namespace_import") {
2855
+ const nm = c.childForFieldName("name") ?? c.namedChild(0);
2856
+ if (nm) add(nm.text, nm);
2857
+ } else if (c.type === "named_imports") {
2858
+ for (let j = 0; j < c.namedChildCount; j++) {
2859
+ const spec = c.namedChild(j);
2860
+ if (!spec || spec.type !== "import_specifier") continue;
2861
+ const alias = spec.childForFieldName("alias");
2862
+ const nm = spec.childForFieldName("name") ?? spec.namedChild(0);
2863
+ const local = alias ?? nm;
2864
+ if (local) add(local.text, local);
2865
+ }
2866
+ }
2867
+ }
2868
+ };
2869
+ const isModuleLoad = (value) => {
2870
+ if (!value || value.type !== "call_expression") return false;
2871
+ const fn = value.childForFieldName("function");
2872
+ if (!fn) return false;
2873
+ return fn.type === "import" || fn.type === "identifier" && (fn.text === "require" || fn.text === "import");
2874
+ };
2875
+ const moduleLoadSpecifier = (value) => {
2876
+ if (!value || value.type !== "call_expression") return "";
2877
+ const args = value.childForFieldName("arguments");
2878
+ const arg = args?.namedChild(0);
2879
+ if (!arg) return "";
2880
+ if (arg.type !== "string" && arg.type !== "template_string") return "";
2881
+ return stringContent(arg);
2882
+ };
2883
+ const walk = (n, scope) => {
2884
+ const t = n.type;
2885
+ if (t === "function_declaration" || t === "generator_function_declaration") {
2886
+ const nm = n.childForFieldName("name");
2887
+ if (nm) addName(scope, nm.text, nm);
2888
+ const fnScope = newScope(n, "function", scope, false);
2889
+ addParams(n, fnScope);
2890
+ for (let i = 0; i < n.namedChildCount; i++) {
2891
+ const c = n.namedChild(i);
2892
+ if (c) walk(c, fnScope);
2893
+ }
2894
+ return;
2895
+ }
2896
+ if (t === "function_expression" || t === "generator_function") {
2897
+ const fnScope = newScope(n, "function", scope, false);
2898
+ const nm = n.childForFieldName("name");
2899
+ if (nm) addName(fnScope, nm.text, nm);
2900
+ addParams(n, fnScope);
2901
+ for (let i = 0; i < n.namedChildCount; i++) {
2902
+ const c = n.namedChild(i);
2903
+ if (c) walk(c, fnScope);
2904
+ }
2905
+ return;
2906
+ }
2907
+ if (t === "arrow_function" || t === "method_definition" || t === "class_static_block") {
2908
+ const fnScope = newScope(n, "function", scope, false);
2909
+ if (t !== "class_static_block") addParams(n, fnScope);
2910
+ for (let i = 0; i < n.namedChildCount; i++) {
2911
+ const c = n.namedChild(i);
2912
+ if (c) walk(c, fnScope);
2913
+ }
2914
+ return;
2915
+ }
2916
+ if (t === "import_statement") {
2917
+ addImportBindings(n);
2918
+ for (let i = 0; i < n.namedChildCount; i++) {
2919
+ const c = n.namedChild(i);
2920
+ if (c) walk(c, scope);
2921
+ }
2922
+ return;
2923
+ }
2924
+ if (t === "variable_declarator") {
2925
+ const nameNode = n.childForFieldName("name");
2926
+ const value = n.childForFieldName("value");
2927
+ const parent = n.parent;
2928
+ const kindNode = parent?.childForFieldName?.("kind");
2929
+ const isVar = parent?.type === "variable_declaration" || kindNode !== void 0 && kindNode !== null && kindNode.text === "var";
2930
+ const target = isVar ? hoistTarget(scope) : scope;
2931
+ const moduleBound = isModuleLoad(value);
2932
+ const moduleSpec = moduleBound ? moduleLoadSpecifier(value) : "";
2933
+ if (nameNode) {
2934
+ for (const pair of this.collectPatternPairs(nameNode)) {
2935
+ addName(target, pair.name, pair.node);
2936
+ if (moduleBound) {
2937
+ moduleBindings.add(pair.name);
2938
+ if (moduleSpec) moduleBindingSpecs.set(pair.name, moduleSpec);
2939
+ }
2940
+ }
2941
+ }
2942
+ for (let i = 0; i < n.namedChildCount; i++) {
2943
+ const c = n.namedChild(i);
2944
+ if (c) walk(c, scope);
2945
+ }
2946
+ return;
2947
+ }
2948
+ if (t === "catch_clause") {
2949
+ const catchScope = newScope(n, "catch", scope, false);
2950
+ const param = n.childForFieldName("parameter");
2951
+ if (param) for (const pair of this.collectPatternPairs(param)) addName(catchScope, pair.name, pair.node);
2952
+ for (let i = 0; i < n.namedChildCount; i++) {
2953
+ const c = n.namedChild(i);
2954
+ if (c) walk(c, catchScope);
2955
+ }
2956
+ return;
2957
+ }
2958
+ if (t === "class_declaration") {
2959
+ const nm = n.childForFieldName("name");
2960
+ if (nm) {
2961
+ addName(scope, nm.text, nm);
2962
+ if (scope.published) publishedClasses.add(nm.text);
2963
+ }
2964
+ for (let i = 0; i < n.namedChildCount; i++) {
2965
+ const c = n.namedChild(i);
2966
+ if (c) walk(c, scope);
2967
+ }
2968
+ return;
2969
+ }
2970
+ if (t === "statement_block" || t === "switch_body") {
2971
+ const blockScope = newScope(n, "block", scope, false);
2972
+ for (let i = 0; i < n.namedChildCount; i++) {
2973
+ const c = n.namedChild(i);
2974
+ if (c) walk(c, blockScope);
2975
+ }
2976
+ return;
2977
+ }
2978
+ if (t === "for_statement" || t === "for_in_statement") {
2979
+ const loopScope = newScope(n, "block", scope, false);
2980
+ for (let i = 0; i < n.namedChildCount; i++) {
2981
+ const c = n.namedChild(i);
2982
+ if (c) walk(c, loopScope);
2983
+ }
2984
+ return;
2985
+ }
2986
+ for (let i = 0; i < n.namedChildCount; i++) {
2987
+ const c = n.namedChild(i);
2988
+ if (c) walk(c, scope);
2989
+ }
2990
+ };
2991
+ walk(rootNode, program);
2992
+ const declScopeCounts = /* @__PURE__ */ new Map();
2993
+ for (const s of scopes) {
2994
+ for (const n of s.names) declScopeCounts.set(n, (declScopeCounts.get(n) ?? 0) + 1);
2995
+ }
2996
+ const multiScopeNames = /* @__PURE__ */ new Set();
2997
+ for (const [n, count] of declScopeCounts) if (count > 1) multiScopeNames.add(n);
2998
+ return { scopes, allNames, moduleBindings, moduleBindingSpecs, publishedClasses, multiScopeNames };
2999
+ }
3000
+ /**
3001
+ * THE LOCAL-BINDING SPELLING for the site, or `null` when the name at this node is not a
3002
+ * claimable local binding. Returns `${source}${BINDING_SEP}${name}` exactly as the family
3003
+ * contract specifies (php/dart/java/fsharp/haskell): the arbiter verifies the SPELLING (a
3004
+ * declaration name can never contain the separator), the row lands in the gate's `bindOK`
3005
+ * column, and it is never merged into `resolved`.
3006
+ *
3007
+ * REFUSALS, each one a false statement the claim must not make:
3008
+ * · the file loads the name from a module (`import` / `require` / dynamic `import()`) — that is
3009
+ * the imported-binding seam class, not a local binding;
3010
+ * · the name resolves to the PROGRAM scope — module-scope declarations are PUBLISHED SYMBOLS,
3011
+ * so the edge can resolve normally and a binding claim would deny a real declaration;
3012
+ * · no lexical binding answers the name at this node's position (the read is of something the
3013
+ * file does not bind — host global, import through another route, or a genuine gap);
3014
+ * · the name would be the empty string or is one of the keywords the language never binds.
3015
+ */
3016
+ localBindingTarget(node, name, source) {
3017
+ const model = this.bindingModel;
3018
+ if (!model || !node || !name || !source) return null;
3019
+ if (!this.isLocalBindingName(node, name)) return null;
3020
+ return `${source}${BINDING_SEP}${name}`;
3021
+ }
3022
+ /**
3023
+ * STATIC CLASS RECEIVER — `Service.create()` where the receiver identifier is a PROGRAM-scope
3024
+ * class DECLARATION in this file. That class's members ARE published symbols, so the owner channel
3025
+ * can resolve the member inside it; the proof is positional (the identifier must still resolve to
3026
+ * the program scope at THIS site — a parameter or local shadowing the class name is not the class,
3027
+ * and gets nothing). Evidence class `class-static`.
3028
+ */
3029
+ staticClassReceiver(node, name) {
3030
+ const model = this.bindingModel;
3031
+ if (!model || !node || !name) return null;
3032
+ if (!model.publishedClasses.has(name)) return null;
3033
+ return this.resolveBindingKind(model, node.startIndex, name) === "published" ? { type: name, evidence: "class-static" } : null;
3034
+ }
3035
+ /**
3036
+ * THIS-ALIAS RECEIVER — `const self = this` in the SAME function body as the site, with `this`
3037
+ * provably the enclosing class DECLARATION's instance. Resolved POSITIONALLY, never through the
3038
+ * file map: a file map leaked an alias across sibling functions in the fixture (a plain
3039
+ * function's `const self = this` is dynamic `this`, yet it inherited the class recorded by
3040
+ * another function's alias of the same name) — the leak produced a wrong owner, which is worse
3041
+ * than none. Refusals: a plain function/generator is dynamic `this` (nothing); an arrow is
3042
+ * lexical `this` but the alias would have to be declared in an enclosing body, so this helper
3043
+ * under-claims rather than reach across (nothing); a class expression is not a published symbol.
3044
+ */
3045
+ thisAliasOwnerAt(node, name) {
3046
+ if (!node || !name) return null;
3047
+ const isFunctionForm = (t) => t === "function_declaration" || t === "function_expression" || t === "generator_function_declaration" || t === "generator_function" || t === "arrow_function" || t === "method_definition" || t === "class_static_block";
3048
+ let fn = null;
3049
+ for (let p = node; p; p = p.parent) {
3050
+ if (isFunctionForm(p.type)) {
3051
+ fn = p;
3052
+ break;
3053
+ }
3054
+ if (p.type === "class_declaration" || p.type === "class") return null;
3055
+ }
3056
+ if (!fn) return null;
3057
+ if (fn.type !== "method_definition" && fn.type !== "class_static_block") return null;
3058
+ let cls = null;
3059
+ for (let p = fn.parent; p; p = p.parent) {
3060
+ if (p.type === "class_declaration") {
3061
+ cls = p.childForFieldName("name")?.text ?? null;
3062
+ break;
3063
+ }
3064
+ if (p.type === "class") return null;
3065
+ if (p.type === "function_declaration" || p.type === "function_expression" || p.type === "generator_function_declaration" || p.type === "generator_function") return null;
3066
+ }
3067
+ if (!cls) return null;
3068
+ const stack = [fn];
3069
+ while (stack.length > 0) {
3070
+ const n = stack.pop();
3071
+ if (n !== fn && isFunctionForm(n.type)) continue;
3072
+ if (n.type === "variable_declarator") {
3073
+ const id = n.childForFieldName("name");
3074
+ const value = n.childForFieldName("value");
3075
+ if (id && id.type === "identifier" && id.text === name && value && value.type === "this") {
3076
+ return { type: cls, evidence: "this-alias" };
3077
+ }
3078
+ }
3079
+ for (let i = 0; i < n.namedChildCount; i++) {
3080
+ const c = n.namedChild(i);
3081
+ if (c) stack.push(c);
3082
+ }
3083
+ }
3084
+ return null;
3085
+ }
3086
+ /** innermost scope containing `index`, then the scope chain — "published" | "lexical" | null. */
3087
+ resolveBindingKind(model, index, name) {
3088
+ const scope = this.resolveBindingScope(model, index, name);
3089
+ if (!scope) return null;
3090
+ return scope.published ? "published" : "lexical";
3091
+ }
3092
+ /**
3093
+ * R2 (charter §21.41) — THE SCOPE THAT DECLARES `name` AT `index`: the innermost scope that
3094
+ * contains the position, then the scope chain, exactly the resolution `resolveBindingKind` uses
3095
+ * — but it returns the SCOPE, not the kind, because the scope is what carries the declaration's
3096
+ * identity.
3097
+ */
3098
+ resolveBindingScope(model, index, name) {
3099
+ let best = null;
3100
+ for (const scope of model.scopes) {
3101
+ if (index < scope.startIndex || index > scope.endIndex) continue;
3102
+ if (best === null || scope.endIndex - scope.startIndex < best.endIndex - best.startIndex) best = scope;
3103
+ }
3104
+ let cur = best;
3105
+ while (cur) {
3106
+ if (cur.names.has(name)) return cur;
3107
+ cur = cur.parent;
3108
+ }
3109
+ return null;
3110
+ }
3111
+ /**
3112
+ * R2 (charter §21.41) — THE DECLARATION'S SCOPE IDENTITY for a read of `name` at `node`, or null.
3113
+ *
3114
+ * The identity is the innermost scope that DECLARES the name at this position, plus that
3115
+ * declaration's own offset — the dart lane's shape. Two reads of ONE declaration share it; two
3116
+ * reads of TWO declarations do not. It joins the DEDUP KEY only, and only for a row whose
3117
+ * identity is the occurrence (a local-binding row), so the payload-pollution dedup stays byte for
3118
+ * byte for every other row.
3119
+ *
3120
+ * `null` in three cases, and every one of them keeps the old key:
3121
+ * · the name is declared in only ONE scope in the file — the base key already separates it;
3122
+ * · the read resolves to the PROGRAM scope — a published symbol, not a local-binding row, and
3123
+ * a claim there would deny a real declaration;
3124
+ * · the name is not bound at this site at all.
3125
+ */
3126
+ localBindingScopeId(node, name) {
3127
+ const model = this.bindingModel;
3128
+ if (!model || !node || !name) return null;
3129
+ if (!model.multiScopeNames.has(name)) return null;
3130
+ if (!this.isLocalBindingName(node, name)) return null;
3131
+ const scope = this.resolveBindingScope(model, node.startIndex, name);
3132
+ if (!scope || scope.published) return null;
3133
+ return `${scope.startIndex}:${scope.decls.get(name) ?? scope.startIndex}`;
3134
+ }
3135
+ /**
3136
+ * IS THIS READ A LOCAL-BINDING ROW? — the ONE test both the TARGET's spelling and the DEDUP KEY's
3137
+ * scope identity must agree on.
3138
+ *
3139
+ * The refusals, each a false statement the row must not make:
3140
+ * · `this` / `super` / `arguments` / `undefined` are not names a declaration can bind;
3141
+ * · a name the file binds FROM A MODULE is the imported-binding class, never a local binding
3142
+ * (its claim is evidence-verified and its target stays bare);
3143
+ * · a name that resolves to the PROGRAM scope is a published symbol, not a local binding — a
3144
+ * claim there would deny a real declaration.
3145
+ */
3146
+ isLocalBindingName(node, name) {
3147
+ const model = this.bindingModel;
3148
+ if (!model || !node || !name) return false;
3149
+ if (name === "this" || name === "super" || name === "arguments" || name === "undefined") return false;
3150
+ if (model.moduleBindings.has(name)) return false;
3151
+ return this.resolveBindingKind(model, node.startIndex, name) === "lexical";
3152
+ }
3153
+ /**
3154
+ * IMPORTED-BINDING CLAIM — the EVIDENCE-VERIFIED claim kind (Lead contract, 2026-10-18). Returns
3155
+ * `{ specifier, binding }` when the name IS the imported binding at THIS site, so the caller can
3156
+ * publish `metadata.importedBinding` and keep the target spelling BARE (no BINDING_SEP — the
3157
+ * arbiter verifies this claim against the run's own import rows, not a spelling).
3158
+ *
3159
+ * REFUSALS, each a false statement the claim must not make:
3160
+ * · the name has no import evidence in this file (no literal specifier: a name the PROJECT
3161
+ * declares, a name with no import at all, or a DYNAMIC `require(expr)` — never-claim (a)/(c));
3162
+ * · a LOCAL binding shadows the import at this site (the positional scope check): the target is
3163
+ * the shadow, not the import;
3164
+ * · the site is a namespace MEMBER access — the caller only asks this for a target that IS the
3165
+ * binding (bare callee / bare read / chain root), never a member segment (never-claim (b));
3166
+ * · re-export rows never reach here (never-claim (d): different surface).
3167
+ */
3168
+ importedBindingFor(node, name) {
3169
+ const model = this.bindingModel;
3170
+ if (!model || !node || !name) return null;
3171
+ const specifier = model.moduleBindingSpecs.get(name);
3172
+ if (specifier === void 0) return null;
3173
+ if (this.resolveBindingKind(model, node.startIndex, name) !== "published") return null;
3174
+ return { specifier, binding: name };
3175
+ }
3176
+ /**
3177
+ * JS EXTERNAL CLAIMS — mark the edges whose target is a platform name the FILE cannot declare,
3178
+ * using the CLOSED CITED vocabulary in `js-builtin-prototypes.ts` and nothing else.
3179
+ *
3180
+ * WHY THIS PASS EXISTS. The lane's unresolved `calls`/`uses` mass is dominated by names no
3181
+ * repository can declare: `document.getElementById`, `window.addEventListener`,
3182
+ * `location.hash.substring`, `JSON.stringify`, `setTimeout`. Before this pass the analyzer
3183
+ * published those rows with NO claim at all, so they read exactly like a name the project
3184
+ * failed to bind. The claim fixes the READ, not the binding — it never picks a declaration,
3185
+ * so it cannot raise `arbitraryTwin`, `bindFAIL` or `extFAIL` for a DOTLESS target (the
3186
+ * arbiter looks a dotless target up by its own spelling and can only reach a refutation on a
3187
+ * target that names a declaration — which is why this pass NEVER rewrites a target, and why
3188
+ * it refuses every non-identifier target; a dotted target is looked up by its OWNER, which a
3189
+ * project may well publish).
3190
+ *
3191
+ * THE TWO PROOFS, both readable from the syntax with NO type inference:
3192
+ *
3193
+ * bare name is a cited host global `setTimeout(...)` -> claim, cites ECMA-262 §19.2
3194
+ * receiver ROOT is a cited host global `document.getElementById` -> claim, cites WHATWG HTML §7.3
3195
+ * receiver root is a LOCAL `x.substring(...)` -> NO CLAIM (x may be a project object)
3196
+ * name/receiver root the FILE DECLARES `class Map {}; Map.x()` -> NO CLAIM (the declaration outranks the table)
3197
+ *
3198
+ * The vocabulary is `HOST_GLOBALS` (57 cited entries) + `memberChainClaim` in
3199
+ * `js-builtin-prototypes.ts` — the same module the differential's oracle imports, so the two
3200
+ * sides cannot drift into two copies of one rule. A name NOT in that list gets NO claim: a
3201
+ * heuristic ("looks like a DOM name") would be the closed-list lie this fleet keeps catching.
3202
+ *
3203
+ * THE IMPORT CLASS is claimed too, and it is a DIFFERENT claim kind on purpose: an `imports`
3204
+ * row spells `<module>:<item>`, so the claim is about the MODULE (a namespace, not a
3205
+ * declaration). The arbiter's `moduleNamespace` claim verifies the module ROOT against the
3206
+ * project's own file/directory names and FAILS CLOSED when the root is project-owned, so a
3207
+ * workspace package that names a real project directory cannot be laundered. A relative or
3208
+ * absolute specifier is NEVER claimed: it names a FILE in this project, and a file claim that
3209
+ * misses is REFUTED against the lane — this per-file analyzer is not given the project's file
3210
+ * list, so those rows stay unclaimed rather than risk a false external statement.
3211
+ *
3212
+ * WHAT IT DELIBERATELY DOES NOT CLAIM, and the counts are the next measurement: Node runtime
3213
+ * globals (`process`, `Buffer`, `module`, `require`), test-runner globals (`describe`, `it`,
3214
+ * `test`, `expect`, `assert`) and imported bindings from external packages are OUTSIDE the
3215
+ * cited list. They stay unresolved until each gets its own cited vocabulary; they are named in
3216
+ * `validation/m14/verification/JAVASCRIPT-RESOLUTION-CLASSIFICATION.md` with their measured
3217
+ * counts rather than smuggled into this table.
3218
+ *
3219
+ * The claim's own words are published twice: `externalReason` (the machine bucket the arbiter
3220
+ * reads) and `externalClaimReason` (the full citation a human audits). `externalReceiver`
3221
+ * names the receiver root when the proof is the MEMBER rule, so a reader can re-run the rule
3222
+ * from the edge itself.
3223
+ */
3224
+ markExternalClaims(rootNode, relationships, symbols, context) {
3225
+ const declaredHere = /* @__PURE__ */ new Set();
3226
+ for (const s of symbols) if (s.name) declaredHere.add(String(s.name));
3227
+ const bound = this.bindingModel ? this.bindingModel.allNames : this.collectBoundNames(rootNode);
3228
+ let marked = 0;
3229
+ for (const r of relationships) {
3230
+ const label = String(r.type ?? "");
3231
+ const target = String(r.target ?? "");
3232
+ if (target.length === 0) continue;
3233
+ const md = r.metadata ?? (r.metadata = {});
3234
+ if (md.isExternalTarget === true) continue;
3235
+ if (label !== "imports" && !isPlainJsIdentifier(target)) continue;
3236
+ if (declaredHere.has(target)) continue;
3237
+ let reason = null;
3238
+ let citation = null;
3239
+ let receiver = null;
3240
+ if (label === "calls") {
3241
+ const callee = String(md.calleeExpression ?? "");
3242
+ if (callee.length > 0 && callee === target) {
3243
+ if (!bound.has(target)) {
3244
+ citation = hostGlobalCitation(target);
3245
+ if (citation !== null) reason = "host-global-bare";
3246
+ else {
3247
+ citation = runtimeGlobalCitation(target);
3248
+ if (citation !== null) reason = runtimeGlobalReason(target);
3249
+ }
3250
+ }
3251
+ } else if (callee.length > 0 && callee.endsWith(`.${target}`)) {
3252
+ const recvChain = callee.slice(0, callee.length - target.length - 1);
3253
+ const rootEnd = jsIdentifierPrefixEnd(recvChain);
3254
+ const root = recvChain.slice(0, rootEnd);
3255
+ if (root.length > 0 && isPlainJsIdentifier(root) && !bound.has(root) && !declaredHere.has(root)) {
3256
+ citation = memberChainClaim(root, target);
3257
+ if (citation !== null) {
3258
+ reason = "host-global-member";
3259
+ receiver = root;
3260
+ }
3261
+ }
3262
+ }
3263
+ } else if (label === "uses") {
3264
+ const isBareRead = md.bareRead === true || md.chainRoot === true || md.callReceiver === true || md.isTemplateInterpolation === true;
3265
+ if (isBareRead) {
3266
+ if (!bound.has(target)) {
3267
+ citation = hostGlobalCitation(target);
3268
+ if (citation !== null) reason = "host-global-bare";
3269
+ else {
3270
+ citation = runtimeGlobalCitation(target);
3271
+ if (citation !== null) reason = runtimeGlobalReason(target);
3272
+ }
3273
+ }
3274
+ } else if (md.isFieldAccess === true) {
3275
+ const root = String(md.receiverRoot ?? "");
3276
+ if (root.length > 0 && isPlainJsIdentifier(root) && !bound.has(root) && !declaredHere.has(root)) {
3277
+ citation = memberChainClaim(root, target);
3278
+ if (citation !== null) {
3279
+ reason = "host-global-member";
3280
+ receiver = root;
3281
+ }
3282
+ }
3283
+ }
3284
+ } else if (label === "imports") {
3285
+ const at = target.lastIndexOf(":");
3286
+ if (at > 0) {
3287
+ const spec = target.slice(0, at);
3288
+ const item = target.slice(at + 1);
3289
+ const isRelative = spec.startsWith(".") || spec.startsWith("/");
3290
+ const isScheme = spec.includes("://");
3291
+ const isPathish = spec.includes("\\") || spec.length === 0 || item.length === 0;
3292
+ const index = context?.projectIndex;
3293
+ if (!isRelative && !isScheme && !isPathish && index !== void 0 && index.complete === true) {
3294
+ const verdict = jsProjectModuleLocalityOf(index)(spec);
3295
+ if (verdict === "third-party") {
3296
+ reason = "module-namespace";
3297
+ citation = `import specifier \`${spec}\` names no module of this project (complete project index; ${item === "*" ? "namespace import" : `item ${item}`})`;
3298
+ receiver = spec;
3299
+ }
3300
+ }
3301
+ }
3302
+ } else if (label === "extends" || label === "references" || label === "instantiates") {
3303
+ if (!bound.has(target) && label !== "instantiates") {
3304
+ citation = hostGlobalCitation(target);
3305
+ if (citation !== null) reason = label === "extends" ? "host-global-base" : "host-global-type";
3306
+ }
3307
+ if (citation === null && !bound.has(target)) {
3308
+ citation = runtimeGlobalCitation(target);
3309
+ if (citation !== null) reason = runtimeGlobalReason(target);
3310
+ }
3311
+ }
3312
+ if (reason === null || citation === null) continue;
3313
+ md.isExternalTarget = true;
3314
+ md.externalReason = reason;
3315
+ md.externalClaimReason = citation;
3316
+ if (receiver !== null) md.externalReceiver = receiver;
3317
+ if (reason === "module-namespace" && receiver !== null) md.externalSpecifier = receiver;
3318
+ marked++;
3319
+ }
3320
+ return marked;
3321
+ }
3322
+ /**
3323
+ * EVERY NAME THIS FILE BINDS — the negative control of the claim pass, and deliberately
3324
+ * over-collecting: a name collected here that is not really a binding can only REFUSE a
3325
+ * claim, never make one, so the conservative direction is the safe direction.
3326
+ *
3327
+ * The positions are the grammar's own definition positions, matched by the same node-type
3328
+ * tests the bare-read walk applies (`isImportBinding` / `isParamBinding` / `isPatternBinding`
3329
+ * / declaration names), read from the IDENTIFIER's parent rather than re-walking each
3330
+ * construct — one pass, no shape re-derived.
3331
+ */
3332
+ collectBoundNames(rootNode) {
3333
+ const out = /* @__PURE__ */ new Set();
3334
+ const stack = [rootNode];
3335
+ while (stack.length > 0) {
3336
+ const n = stack.pop();
3337
+ if (n.type === "identifier") {
3338
+ const p = n.parent;
3339
+ if (p) {
3340
+ const isDeclName = p.type === "variable_declarator" && p.childForFieldName("name")?.id === n.id || p.type === "function_declaration" && p.childForFieldName("name")?.id === n.id || p.type === "generator_function_declaration" && p.childForFieldName("name")?.id === n.id || p.type === "class_declaration" && p.childForFieldName("name")?.id === n.id || p.type === "function_expression" && p.childForFieldName("name")?.id === n.id || p.type === "method_definition" && p.childForFieldName("name")?.id === n.id;
3341
+ const isBindingPosition = p.type === "formal_parameters" || p.type === "import_clause" || p.type === "import_specifier" || p.type === "namespace_import" || p.type === "catch_clause" || p.type === "object_pattern" || p.type === "array_pattern" || p.type === "rest_pattern" || p.type === "shorthand_property_identifier_pattern" || p.type === "pair_pattern" || p.type === "assignment_pattern" && (p.childForFieldName("left") ?? p.namedChild(0))?.id === n.id || p.type === "arrow_function" && p.childForFieldName("parameter")?.id === n.id;
3342
+ if (isDeclName || isBindingPosition) out.add(n.text);
3343
+ }
3344
+ }
3345
+ for (let i = 0; i < n.namedChildCount; i++) {
3346
+ const c = n.namedChild(i);
3347
+ if (c) stack.push(c);
3348
+ }
3349
+ }
3350
+ return out;
3351
+ }
3352
+ /**
3353
+ * M14 #299 - evidence-tier classification for EVERY emitted relationship.
3354
+ * Same rules as the JVM/TS family. JS calls are usage_contextual,
3355
+ * field_access uses are usage_contextual, imports are import_based.
3356
+ */
3357
+ classifyEvidenceTier(relationships) {
3358
+ for (const r of relationships) {
3359
+ const md = r.metadata ?? (r.metadata = {});
3360
+ if (md.evidenceClass) continue;
3361
+ const t = String(r.type ?? "");
3362
+ const conf = typeof r.confidence === "number" ? r.confidence : 1;
3363
+ if (t === "contains" || t === "exports") {
3364
+ md.evidenceClass = "structural";
3365
+ continue;
3366
+ }
3367
+ if (t === "imports") {
3368
+ md.evidenceClass = "import_based";
3369
+ continue;
3370
+ }
3371
+ if (t === "references") {
3372
+ md.evidenceClass = "reference_qualified";
3373
+ continue;
3374
+ }
3375
+ if (t === "extends" || t === "implements" || t === "overrides") {
3376
+ md.evidenceClass = conf >= 1 ? "structural" : conf >= 0.85 ? "usage_contextual" : "inferred";
3377
+ continue;
3378
+ }
3379
+ if (t === "instantiates") {
3380
+ md.evidenceClass = conf >= 0.9 ? "structural" : "usage_contextual";
3381
+ continue;
3382
+ }
3383
+ md.evidenceClass = "usage_contextual";
3384
+ }
3385
+ }
3386
+ /**
3387
+ * M59 (#825): the `line_number` for a relationship, taken from the node that
3388
+ * SPELLS the name the edge is about.
3389
+ *
3390
+ * javascript is the largest silent lane in the product: measured by the M59
3391
+ * oracle before this round, **0 of 5,818 relationships on a 40-file sample
3392
+ * carried a line** (vue 0/3,940 and svelte 0/526, both of which delegate their
3393
+ * script block to this analyzer — so one defect blanked three lanes). The
3394
+ * contract is inherited, not re-invented: **the line belongs to the name, never
3395
+ * to the construct that contains it.** A `call_expression` starts at its
3396
+ * RECEIVER (`a.b.c()` opens on `a`, not on `c`), a `new_expression` on its
3397
+ * `new` token, an `import_statement` on `import` even when its named specifiers
3398
+ * sit three lines below, and a class body's children open at their decorators.
3399
+ * Callers therefore pass the NAME node and this returns nothing when there is
3400
+ * none — never `0`, because `0` reads as "unknown" and unknown must be absent.
3401
+ */
3402
+ lineField(nameNode, fallback) {
3403
+ const n = nameNode ?? fallback;
3404
+ return n ? { line_number: n.startPosition.row + 1 } : {};
3405
+ }
3406
+ nodeToRange(node) {
3407
+ let startSource = node;
3408
+ {
3409
+ let i = 0;
3410
+ while (i < node.childCount && node.child(i)?.type === "decorator") i++;
3411
+ const first = i < node.childCount ? node.child(i) : null;
3412
+ if (i > 0 && first) startSource = first;
3413
+ }
3414
+ return {
3415
+ startLine: startSource.startPosition.row + 1,
3416
+ startColumn: startSource.startPosition.column + 1,
3417
+ endLine: node.endPosition.row + 1,
3418
+ endColumn: node.endPosition.column + 1,
3419
+ startIndex: startSource.startIndex,
3420
+ endIndex: node.endIndex
3421
+ };
3422
+ }
3423
+ };
3424
+
3425
+ export {
3426
+ RealJavaScriptAnalyzer
3427
+ };