@backbencher/brain 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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-KY7K552E.js +17 -0
  8. package/dist/brain-relationship-enrichment-QH7OOHUF.js +558 -0
  9. package/dist/brain.sql-EIM4WS4D.js +29 -0
  10. package/dist/chunk-2BSKD7O3.js +40 -0
  11. package/dist/chunk-2DE27DVC.js +1314 -0
  12. package/dist/chunk-2RRBMNBQ.js +322 -0
  13. package/dist/chunk-2RTN2DFU.js +711 -0
  14. package/dist/chunk-2WXYNLBA.js +29 -0
  15. package/dist/chunk-3CTFIJJB.js +17 -0
  16. package/dist/chunk-3LFTSMIV.js +174 -0
  17. package/dist/chunk-47HKX4AJ.js +930 -0
  18. package/dist/chunk-4H22TLOT.js +543 -0
  19. package/dist/chunk-5SD45FLC.js +818 -0
  20. package/dist/chunk-66A5ZFYA.js +1758 -0
  21. package/dist/chunk-6AIRWAO2.js +247 -0
  22. package/dist/chunk-6WNGD3NN.js +196 -0
  23. package/dist/chunk-7AN7U5IZ.js +371 -0
  24. package/dist/chunk-BHB7BYYI.js +626 -0
  25. package/dist/chunk-BQ3OVQ3P.js +125 -0
  26. package/dist/chunk-CW4XXSNN.js +1479 -0
  27. package/dist/chunk-D2Q4Y47A.js +53 -0
  28. package/dist/chunk-DIYAGQJL.js +34 -0
  29. package/dist/chunk-DYM4FQEE.js +90 -0
  30. package/dist/chunk-EWMWK7WN.js +75 -0
  31. package/dist/chunk-FVVQTSIM.js +3427 -0
  32. package/dist/chunk-FWSL4VG6.js +1290 -0
  33. package/dist/chunk-GBI7V7KY.js +69 -0
  34. package/dist/chunk-GEFFO6O2.js +152 -0
  35. package/dist/chunk-HOWAQXJO.js +236 -0
  36. package/dist/chunk-HQSPJDHI.js +613 -0
  37. package/dist/chunk-IFZNR5UJ.js +2863 -0
  38. package/dist/chunk-J4PMBXQQ.js +336 -0
  39. package/dist/chunk-KAET47ZL.js +125 -0
  40. package/dist/chunk-KCDECRJY.js +440 -0
  41. package/dist/chunk-KLK3PBTO.js +1119 -0
  42. package/dist/chunk-L5TDYKYK.js +76 -0
  43. package/dist/chunk-MLKGABMK.js +9 -0
  44. package/dist/chunk-OWISBAOF.js +61 -0
  45. package/dist/chunk-PJTNGD3Z.js +6924 -0
  46. package/dist/chunk-QLK6ED4Q.js +235 -0
  47. package/dist/chunk-R2GW4F6R.js +19 -0
  48. package/dist/chunk-RCMTFSEF.js +87 -0
  49. package/dist/chunk-RH7KC5HU.js +12050 -0
  50. package/dist/chunk-RM3PY6TG.js +376 -0
  51. package/dist/chunk-ROMM6KMC.js +433 -0
  52. package/dist/chunk-SK6YVHFZ.js +211 -0
  53. package/dist/chunk-SM7RFQRO.js +214 -0
  54. package/dist/chunk-UFE77L67.js +782 -0
  55. package/dist/chunk-UGR4QIGS.js +67 -0
  56. package/dist/chunk-UUNHHX4M.js +13 -0
  57. package/dist/chunk-V44PGPA6.js +2104 -0
  58. package/dist/chunk-VL52PL2T.js +71 -0
  59. package/dist/chunk-VURYFIHW.js +201 -0
  60. package/dist/chunk-XJKJYIPP.js +7436 -0
  61. package/dist/chunk-YI6FF2ZR.js +340 -0
  62. package/dist/cli/index.js +37 -0
  63. package/dist/config-HZAAYDP3.js +10 -0
  64. package/dist/content-search-642F7E5V.js +593 -0
  65. package/dist/database-TCTZDLE5.js +9 -0
  66. package/dist/doctor-GXCKSJOX.js +395 -0
  67. package/dist/edit-engine-KM7MPFEC.js +51 -0
  68. package/dist/engine-42TZFNIZ.js +11 -0
  69. package/dist/engine-LO6L4JLQ.js +18 -0
  70. package/dist/fingerprint-XDXFRWO6.js +33 -0
  71. package/dist/framework-intelligence-DWFCKV44.js +860 -0
  72. package/dist/freshness-3K6VU5OT.js +174 -0
  73. package/dist/graph-engine-PL5YA7BA.js +21 -0
  74. package/dist/graph_request-MM4REP6H.js +350 -0
  75. package/dist/impact_request-WDE47IZ5.js +68 -0
  76. package/dist/index-space-IUUN5WYV.js +15 -0
  77. package/dist/index-status-6BD7UBHR.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-4PW3AFXP.js +88 -0
  86. package/dist/pool-worker.js +1153 -0
  87. package/dist/project-health-ZS5TS2Z6.js +94 -0
  88. package/dist/project-index-5SDPHITE.js +57 -0
  89. package/dist/project-registry-DGZBIZS7.js +355 -0
  90. package/dist/real-bash-analyzer-SXSV67E2.js +2201 -0
  91. package/dist/real-c-analyzer-SY7FTZ6W.js +5942 -0
  92. package/dist/real-clean-analyzer-IGDHHLZK.js +3470 -0
  93. package/dist/real-clojure-analyzer-4OH7IWLB.js +2818 -0
  94. package/dist/real-cpp-analyzer-7LTDRJRJ.js +4101 -0
  95. package/dist/real-csharp-analyzer-KICU3TLH.js +3298 -0
  96. package/dist/real-css-analyzer-HPU277Q6.js +15 -0
  97. package/dist/real-dart-analyzer-7LZ5LCMW.js +3944 -0
  98. package/dist/real-elixir-analyzer-BE3MEVZO.js +2192 -0
  99. package/dist/real-erlang-analyzer-BZMAKYBK.js +1731 -0
  100. package/dist/real-fsharp-analyzer-5QLTOV3E.js +2390 -0
  101. package/dist/real-go-analyzer-POD7MZQR.js +4126 -0
  102. package/dist/real-graphql-analyzer-MAQRZ6QW.js +1442 -0
  103. package/dist/real-groovy-analyzer-MEWSUPMJ.js +2285 -0
  104. package/dist/real-haskell-analyzer-I5L2GR3Y.js +2533 -0
  105. package/dist/real-hcl-analyzer-DBOPXSWJ.js +725 -0
  106. package/dist/real-html-analyzer-O63XZLZD.js +1759 -0
  107. package/dist/real-java-analyzer-CHCW3EKA.js +4677 -0
  108. package/dist/real-javascript-analyzer-QAAJQYZE.js +14 -0
  109. package/dist/real-json-analyzer-UGBR7YQN.js +1640 -0
  110. package/dist/real-julia-analyzer-35Q4GHOZ.js +2085 -0
  111. package/dist/real-kotlin-analyzer-GRAYCWW2.js +3956 -0
  112. package/dist/real-lua-analyzer-5BAPDZDF.js +1957 -0
  113. package/dist/real-makefile-analyzer-YJKAHNAS.js +920 -0
  114. package/dist/real-markdown-analyzer-UKUQZO4F.js +1215 -0
  115. package/dist/real-matlab-analyzer-FVIKQZKG.js +480 -0
  116. package/dist/real-nim-analyzer-M7FAIABG.js +3603 -0
  117. package/dist/real-objective-c-analyzer-XMYQIGQI.js +1518 -0
  118. package/dist/real-perl-analyzer-ZLY6M2DZ.js +1865 -0
  119. package/dist/real-php-analyzer-KI2ZAM7X.js +9368 -0
  120. package/dist/real-powershell-analyzer-B3T5ZP6U.js +2915 -0
  121. package/dist/real-protobuf-analyzer-23OZ75XV.js +867 -0
  122. package/dist/real-python-analyzer-I4TIUB6D.js +4880 -0
  123. package/dist/real-r-analyzer-FJZJXXS6.js +2596 -0
  124. package/dist/real-ruby-analyzer-M3UFIVTQ.js +2429 -0
  125. package/dist/real-rust-analyzer-LVSONPAN.js +3359 -0
  126. package/dist/real-scala-analyzer-CWPI33Q2.js +3293 -0
  127. package/dist/real-scss-analyzer-TGRJQ7TH.js +1515 -0
  128. package/dist/real-solidity-analyzer-4P2VBVWA.js +2608 -0
  129. package/dist/real-sql-analyzer-J7DYZOZR.js +3840 -0
  130. package/dist/real-svelte-analyzer-NHEN2SX5.js +2532 -0
  131. package/dist/real-swift-analyzer-2NSYULWI.js +2311 -0
  132. package/dist/real-toml-analyzer-L3C66L7Q.js +929 -0
  133. package/dist/real-typescript-analyzer-FTC6ETRQ.js +14 -0
  134. package/dist/real-vb-analyzer-ORY7LAQS.js +3145 -0
  135. package/dist/real-vba-analyzer-JH2TTUFI.js +466 -0
  136. package/dist/real-vue-analyzer-OO4FUGBV.js +2680 -0
  137. package/dist/real-xml-analyzer-6CQVR477.js +2405 -0
  138. package/dist/real-yaml-analyzer-J6MQG3YX.js +3528 -0
  139. package/dist/real-zig-analyzer-EFKCPJNU.js +2430 -0
  140. package/dist/regex-search-OPTLKUN5.js +239 -0
  141. package/dist/rules-I453JTRC.js +152 -0
  142. package/dist/scoped-path-MJYWJZQX.js +11 -0
  143. package/dist/setup-show-Y5GB5KXA.js +15 -0
  144. package/dist/setup-wizard-G5YYR5T6.js +24 -0
  145. package/dist/symbol-search-KB4MU4SI.js +16 -0
  146. package/dist/taint-YOTN5X55.js +202 -0
  147. package/dist/taint-analyzer-N7YLZU3N.js +163 -0
  148. package/dist/tool-handlers-G4NAGJIH.js +72 -0
  149. package/dist/tool-registry-BPEJNSZG.js +17 -0
  150. package/dist/tool-validation-RF675A3C.js +32 -0
  151. package/dist/type-inference-4Y7WTMFE.js +13 -0
  152. package/dist/vector-binary-LMY4QGAN.js +19 -0
  153. package/dist/version-LXGNFO37.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,2680 @@
1
+ import {
2
+ RealTypeScriptAnalyzer
3
+ } from "./chunk-XJKJYIPP.js";
4
+ import {
5
+ RealJavaScriptAnalyzer
6
+ } from "./chunk-FVVQTSIM.js";
7
+ import "./chunk-R2GW4F6R.js";
8
+ import {
9
+ ASTEngine
10
+ } from "./chunk-V44PGPA6.js";
11
+ import "./chunk-BQ3OVQ3P.js";
12
+ import "./chunk-SK6YVHFZ.js";
13
+ import "./chunk-2BSKD7O3.js";
14
+ import "./chunk-KAET47ZL.js";
15
+ import "./chunk-KCDECRJY.js";
16
+ import "./chunk-MLKGABMK.js";
17
+
18
+ // src/ast/real-vue-analyzer.ts
19
+ import * as path from "path";
20
+ function shiftMetadataLineMirrors(md, lineOffset) {
21
+ if (!md || lineOffset === 0) return;
22
+ for (const [k, v] of Object.entries(md)) {
23
+ if (!k.toLowerCase().includes("line")) continue;
24
+ if (typeof v === "number") md[k] = v + lineOffset;
25
+ else if (Array.isArray(v) && v.some((x) => typeof x === "number")) {
26
+ md[k] = v.map((x) => typeof x === "number" ? x + lineOffset : x);
27
+ }
28
+ }
29
+ }
30
+ var HTML_BUILTIN_TAGS = /* @__PURE__ */ new Set([
31
+ "a",
32
+ "abbr",
33
+ "address",
34
+ "area",
35
+ "article",
36
+ "aside",
37
+ "audio",
38
+ "b",
39
+ "base",
40
+ "bdi",
41
+ "bdo",
42
+ "blockquote",
43
+ "body",
44
+ "br",
45
+ "button",
46
+ "canvas",
47
+ "caption",
48
+ "cite",
49
+ "code",
50
+ "col",
51
+ "colgroup",
52
+ "data",
53
+ "datalist",
54
+ "dd",
55
+ "del",
56
+ "details",
57
+ "dfn",
58
+ "dialog",
59
+ "div",
60
+ "dl",
61
+ "dt",
62
+ "em",
63
+ "embed",
64
+ "fieldset",
65
+ "figcaption",
66
+ "figure",
67
+ "footer",
68
+ "form",
69
+ "h1",
70
+ "h2",
71
+ "h3",
72
+ "h4",
73
+ "h5",
74
+ "h6",
75
+ "head",
76
+ "header",
77
+ "hgroup",
78
+ "hr",
79
+ "html",
80
+ "i",
81
+ "iframe",
82
+ "img",
83
+ "input",
84
+ "ins",
85
+ "kbd",
86
+ "label",
87
+ "legend",
88
+ "li",
89
+ "link",
90
+ "main",
91
+ "map",
92
+ "mark",
93
+ "menu",
94
+ "meta",
95
+ "meter",
96
+ "nav",
97
+ "noscript",
98
+ "object",
99
+ "ol",
100
+ "optgroup",
101
+ "option",
102
+ "output",
103
+ "p",
104
+ "picture",
105
+ "pre",
106
+ "progress",
107
+ "q",
108
+ "rp",
109
+ "rt",
110
+ "ruby",
111
+ "s",
112
+ "samp",
113
+ "script",
114
+ "search",
115
+ "section",
116
+ "select",
117
+ "slot",
118
+ "small",
119
+ "source",
120
+ "span",
121
+ "strong",
122
+ "style",
123
+ "sub",
124
+ "summary",
125
+ "sup",
126
+ "table",
127
+ "tbody",
128
+ "td",
129
+ "template",
130
+ "textarea",
131
+ "tfoot",
132
+ "th",
133
+ "thead",
134
+ "time",
135
+ "title",
136
+ "tr",
137
+ "track",
138
+ "u",
139
+ "ul",
140
+ "var",
141
+ "video",
142
+ "wbr",
143
+ // SVG
144
+ "svg",
145
+ "path",
146
+ "g",
147
+ "rect",
148
+ "circle",
149
+ "ellipse",
150
+ "line",
151
+ "polyline",
152
+ "polygon",
153
+ "text",
154
+ "tspan",
155
+ "use",
156
+ "defs",
157
+ "linearGradient",
158
+ "radialGradient",
159
+ "stop",
160
+ "mask",
161
+ "pattern",
162
+ "marker",
163
+ "view",
164
+ "symbol",
165
+ "clipPath",
166
+ // SVG — the FILTER module and the rest of the element vocabulary (F30, 2026-10-12).
167
+ //
168
+ // The list above covered the SVG elements a chart OR a decorative icon uses and
169
+ // stopped there, so `<filter>` and every filter PRIMITIVE fell through the builtin
170
+ // filter and were published as component usages: `<feGaussianBlur/>` emitted
171
+ // `instantiates -> feGaussianBlur`, a target no file in any corpus declares. Measured
172
+ // through production `parseFile` over the lane's 1,162-file draw (element-plus,
173
+ // vuejs, vue-element-admin, vue-core): 377 such edges from just six primitives —
174
+ // feGaussianBlur 64, feFlood 63, feColorMatrix 63, feOffset 63, feBlend 63,
175
+ // feComposite 61 — every one of them a dangling endpoint of exactly the class F23/F26
176
+ // removed for reads. An unknown namespace still fails toward VISIBILITY (see
177
+ // BUILTIN_NAMESPACES below); an element the SVG spec DEFINES is markup, and the rule
178
+ // this list encodes is that a real element is not a component — not that a lowercase
179
+ // name is not a component.
180
+ //
181
+ // NO DOUBLE QUOTES AND NO CLOSING-BRACKET-PAREN PAIR IN THIS COMMENT OR THE TWO
182
+ // BELOW, and that is a constraint, not a style preference:
183
+ // verify-cross-language-consistency.ts extracts this literal by slicing from the
184
+ // `new Set` open anchor to the FIRST closing-bracket-paren pair after it, then matching
185
+ // every quoted string in that slice. So a quoted phrase in a comment here becomes a
186
+ // PHANTOM TAG, and that pair ends the slice early — silently dropping the very tags the
187
+ // fix adds. Measured twice, not assumed: the first draft of this comment quoted the
188
+ // rule and the gate reported extra tags, and the second draft wrote the pair out and
189
+ // the gate reported every new SVG tag as missing from this list only.
190
+ "filter",
191
+ "feBlend",
192
+ "feColorMatrix",
193
+ "feComponentTransfer",
194
+ "feComposite",
195
+ "feConvolveMatrix",
196
+ "feDiffuseLighting",
197
+ "feDisplacementMap",
198
+ "feDistantLight",
199
+ "feDropShadow",
200
+ "feFlood",
201
+ "feFuncA",
202
+ "feFuncB",
203
+ "feFuncG",
204
+ "feFuncR",
205
+ "feGaussianBlur",
206
+ "feImage",
207
+ "feMerge",
208
+ "feMergeNode",
209
+ "feMorphology",
210
+ "feOffset",
211
+ "fePointLight",
212
+ "feSpecularLighting",
213
+ "feSpotLight",
214
+ "feTile",
215
+ "feTurbulence",
216
+ // the rest of SVG 2's element list, plus the SVG 1.1 elements browsers still parse.
217
+ // Counted separately from the block above on purpose: the block above is measured on
218
+ // the corpus, this one is the SPEC's vocabulary, and the difference is recorded rather
219
+ // than blurred (same honesty rule the svelte lane's read map uses for its site lists).
220
+ "switch",
221
+ "foreignObject",
222
+ "desc",
223
+ "metadata",
224
+ "textPath",
225
+ "tref",
226
+ "mpath",
227
+ "animate",
228
+ "animateMotion",
229
+ "animateTransform",
230
+ "set",
231
+ "discard",
232
+ "solidColor",
233
+ "mesh",
234
+ "meshgradient",
235
+ "meshpatch",
236
+ "meshrow",
237
+ "hatch",
238
+ "hatchpath",
239
+ "altGlyph",
240
+ "altGlyphDef",
241
+ "altGlyphItem",
242
+ "glyphRef",
243
+ "animateColor",
244
+ "cursor",
245
+ "color-profile",
246
+ "font",
247
+ "font-face",
248
+ "font-face-format",
249
+ "font-face-name",
250
+ "font-face-src",
251
+ "font-face-uri",
252
+ "glyph",
253
+ "hkern",
254
+ "missing-glyph",
255
+ "vkern"
256
+ ]);
257
+ var VUE_BUILTIN_TAGS = /* @__PURE__ */ new Set([
258
+ "component",
259
+ "transition",
260
+ "transition-group",
261
+ "keep-alive",
262
+ "teleport",
263
+ "suspense",
264
+ "fragment"
265
+ ]);
266
+ var BUILTIN_NAMESPACES = /* @__PURE__ */ new Set([
267
+ "svg",
268
+ "math",
269
+ "mathml",
270
+ "xlink",
271
+ "html",
272
+ "xhtml"
273
+ ]);
274
+ var VUE_COMPILER_MACROS = /* @__PURE__ */ new Set([
275
+ "defineProps",
276
+ "defineEmits",
277
+ "defineExpose",
278
+ "withDefaults",
279
+ "defineModel",
280
+ "defineSlots",
281
+ "defineOptions"
282
+ ]);
283
+ function vueRegistryName(tag) {
284
+ const candidates = [tag];
285
+ const kebab = tag.includes("-");
286
+ let camel = false;
287
+ if (!kebab) {
288
+ for (let ci = 1; ci < tag.length; ci++) {
289
+ const cc = tag.charCodeAt(ci);
290
+ if (cc >= 65 && cc <= 90) {
291
+ camel = true;
292
+ break;
293
+ }
294
+ }
295
+ }
296
+ if (!kebab && !camel) return { target: tag, candidates, normalized: false };
297
+ const pascal = kebab ? tag.split("-").filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("") : tag.charAt(0).toUpperCase() + tag.slice(1);
298
+ if (pascal.length === 0 || pascal === tag) return { target: tag, candidates, normalized: false };
299
+ const camelized = pascal.charAt(0).toLowerCase() + pascal.slice(1);
300
+ if (camelized !== tag) candidates.push(camelized);
301
+ candidates.push(pascal);
302
+ return { target: pascal, candidates, normalized: true };
303
+ }
304
+ function peelTypeWrappers(node) {
305
+ let cur = node;
306
+ for (let i = 0; i < 8; i++) {
307
+ const t = cur.type;
308
+ if (t !== "as_expression" && t !== "satisfies_expression" && t !== "parenthesized_expression" && t !== "non_null_expression") return cur;
309
+ const kids = cur.namedChildren;
310
+ const first = kids[0];
311
+ if (!first) return cur;
312
+ cur = first;
313
+ }
314
+ return cur;
315
+ }
316
+ function isVueModuleSpecifier(specifier) {
317
+ return specifier === "vue" || specifier.startsWith("vue/") || specifier.startsWith("@vue/");
318
+ }
319
+ function unquote(text) {
320
+ if (text.length >= 2) {
321
+ const first = text[0];
322
+ if ((first === '"' || first === "'") && text[text.length - 1] === first) {
323
+ return text.slice(1, -1);
324
+ }
325
+ }
326
+ return text;
327
+ }
328
+ var DELEGATE_FILE_ACTOR = "(file)";
329
+ var RealVueAnalyzer = class _RealVueAnalyzer {
330
+ analyze(rootNode, content, filePath, context) {
331
+ const symbols = [];
332
+ const relationships = [];
333
+ const imports = [];
334
+ const exports = [];
335
+ const errors = [];
336
+ const patterns = {};
337
+ const moduleName = path.basename(filePath, path.extname(filePath));
338
+ patterns["Vue SFC"] = true;
339
+ if (rootNode.hasError) {
340
+ const errStack = [rootNode];
341
+ while (errStack.length > 0) {
342
+ const n = errStack.pop();
343
+ if (n.type === "ERROR" || n.isMissing) {
344
+ errors.push({
345
+ message: `Syntax ${n.type} detected. The Brain Engine is bypassing this broken node.`,
346
+ range: this.nodeToRange(n),
347
+ severity: "warning"
348
+ });
349
+ }
350
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
351
+ const child = n.namedChild(i);
352
+ if (child) errStack.push(child);
353
+ }
354
+ }
355
+ }
356
+ symbols.push({
357
+ name: moduleName,
358
+ kind: ASTEngine.SymbolKind.Class,
359
+ range: this.nodeToRange(rootNode),
360
+ scope: "",
361
+ visibility: "public",
362
+ metadata: { isVueComponent: true, isSfc: true }
363
+ });
364
+ const templateStack = [rootNode];
365
+ const usageRows = /* @__PURE__ */ new Map();
366
+ const usageSpellings = /* @__PURE__ */ new Map();
367
+ let templateDecisions = 0;
368
+ const templateExprNames = /* @__PURE__ */ new Map();
369
+ const shadowStack = [{ node: rootNode, shadowed: /* @__PURE__ */ new Set() }];
370
+ while (shadowStack.length > 0) {
371
+ const { node: n, shadowed } = shadowStack.pop();
372
+ const t = n.type;
373
+ if (t === "script_element" || t === "style_element") continue;
374
+ const childShadowed = new Set(shadowed);
375
+ if (t === "element" || t === "template_element") {
376
+ for (let i = 0; i < n.namedChildCount; i++) {
377
+ const c = n.namedChild(i);
378
+ if (!c || c.type !== "start_tag") continue;
379
+ this.collectNodeShadowBindings(c, childShadowed);
380
+ }
381
+ const rawTag = this.elementTagSpelling(n);
382
+ const dotIdx = rawTag?.indexOf(".") ?? -1;
383
+ const colonIdx = rawTag?.indexOf(":") ?? -1;
384
+ const tag = dotIdx > 0 ? rawTag.slice(0, dotIdx) : rawTag;
385
+ const member = dotIdx > 0 ? rawTag.slice(dotIdx + 1) : void 0;
386
+ const nsPrefix = colonIdx > 0 ? rawTag.slice(0, colonIdx) : void 0;
387
+ const isBuiltinNamespace = nsPrefix !== void 0 && BUILTIN_NAMESPACES.has(nsPrefix);
388
+ if (tag && !isBuiltinNamespace && !HTML_BUILTIN_TAGS.has(tag) && !VUE_BUILTIN_TAGS.has(tag)) {
389
+ const registry = vueRegistryName(tag);
390
+ const spellings = usageSpellings.get(registry.target);
391
+ if (spellings) {
392
+ if (!spellings.includes(tag)) {
393
+ spellings.push(tag);
394
+ const row = usageRows.get(registry.target);
395
+ row.metadata = { ...row.metadata, tagSpellingsWritten: [...spellings].sort() };
396
+ }
397
+ } else {
398
+ usageSpellings.set(registry.target, [tag]);
399
+ const tagNode = this.elementTagNameNode(n);
400
+ const row = {
401
+ source: moduleName,
402
+ target: registry.target,
403
+ // RENDERS, NOT INSTANTIATES (2026-10-24, extended vocabulary; the svelte
404
+ // precedent, Lead-ruled). A template component tag mounts an element the
405
+ // HOST RENDERER creates — the source contains no `new`, so an
406
+ // `instantiates` edge claimed an allocation site the file does not have
407
+ // (types.ts' Renders contract: "NOT `instantiates`: nothing is `new`ed —
408
+ // the host renderer constructs the element"). `instantiates` keeps its
409
+ // OWN rows: the JS/TS delegate's script `new` rows, copied by the seam
410
+ // below — the two relations stay distinct, exactly as svelte's migration
411
+ // kept 191 delegate `new` rows while moving 11,512 template rows.
412
+ // Measured before the relabel over the whole 1,155-file admitted
413
+ // population: 2,235 template rows (isComponentUsage:true) and 16
414
+ // delegate `new` rows = the 2,251 `instantiates` the last whole-
415
+ // population run published — so the relabel moves 2,235 rows and no
416
+ // edge is gained or lost.
417
+ type: ASTEngine.RelationshipType.Renders,
418
+ confidence: 0.95,
419
+ // (#825): the `<Child/>` tag as written in the template.
420
+ ...tagNode ? { line_number: tagNode.startPosition.row + 1 } : {},
421
+ metadata: {
422
+ // F9(b): the leaf of a dotted tag — see the comment at the filter.
423
+ // Merged HERE rather than spread above, because this literal's own
424
+ // `metadata` key comes later and a spread placed before it is
425
+ // silently overwritten (which is exactly what the first attempt did).
426
+ ...member ? { member, componentPath: rawTag } : {},
427
+ // F31: the target is the registry name (see `vueRegistryName`). When it
428
+ // is not the spelling the template wrote, the edge says so and carries
429
+ // the written spelling plus Vue's lookup order, so nothing is lost and a
430
+ // reader can tell a normalized target from a literal one.
431
+ ...registry.normalized ? {
432
+ // The family's reason code for "why this target string is what it
433
+ // is" (`svelte` writes `resolution: namespace-member` for its own
434
+ // non-literal target), so a reader has one key to look for.
435
+ resolution: "registry-name",
436
+ tagSpellingsWritten: [tag],
437
+ tagAsWritten: tag,
438
+ componentNameCandidates: registry.candidates,
439
+ componentNameNormalized: true
440
+ } : {},
441
+ isComponentUsage: true
442
+ }
443
+ };
444
+ usageRows.set(registry.target, row);
445
+ relationships.push(row);
446
+ }
447
+ }
448
+ }
449
+ if (t === "directive_attribute") {
450
+ const dname = this.findChildByType(n, "directive_name")?.text;
451
+ if (dname === "v-if" || dname === "v-else-if" || dname === "v-for" || dname === "v-show") templateDecisions++;
452
+ const value = this.findChildByType(n, "quoted_attribute_value");
453
+ const attrVal = value ? this.findChildByType(value, "attribute_value") : null;
454
+ if (attrVal) {
455
+ if (dname === "v-for") {
456
+ this.collectVForUsage(attrVal.text, templateExprNames, childShadowed, attrVal.startPosition.row + 1);
457
+ } else if (dname === "#" || dname === "v-slot") {
458
+ } else {
459
+ this.collectIdentifiers(attrVal.text, templateExprNames, childShadowed, attrVal.startPosition.row + 1);
460
+ }
461
+ }
462
+ }
463
+ if (t === "interpolation") {
464
+ const raw = this.findChildByType(n, "raw_text");
465
+ if (raw) {
466
+ this.collectIdentifiers(raw.text, templateExprNames, childShadowed, raw.startPosition.row + 1);
467
+ }
468
+ }
469
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
470
+ const child = n.namedChild(i);
471
+ if (child) shadowStack.push({ node: child, shadowed: childShadowed });
472
+ }
473
+ }
474
+ const scriptTreeCount = context?.sfcScriptTrees?.length ?? 0;
475
+ const pushedComponentNames = /* @__PURE__ */ new Set([moduleName]);
476
+ const pendingContracts = [];
477
+ const pendingInjections = [];
478
+ const pendingProvided = [];
479
+ let scriptCyclomaticSum = 0;
480
+ let dispatchedToTypeScript = false;
481
+ const MEMBER_TARGET = /* @__PURE__ */ new Set([
482
+ ASTEngine.RelationshipType.Contains,
483
+ ASTEngine.RelationshipType.Provides
484
+ ]);
485
+ let componentIdentity = moduleName;
486
+ let declaredNameUnread = false;
487
+ let declaredNameFromLocalConst;
488
+ let declaredNameUnreadSetup = false;
489
+ let declaredNameFromOptions;
490
+ let optionsScriptIndex = 0;
491
+ for (let bi = 0; bi < scriptTreeCount; bi++) {
492
+ const scriptTree = context.sfcScriptTrees[bi];
493
+ const scriptStartLine = context.sfcScriptLines[bi];
494
+ const scriptStartColumn = context.sfcScriptCols[bi] ?? 0;
495
+ const scriptStartIndex = context?.sfcScriptStartIndex?.[bi] ?? 0;
496
+ const isTs = context?.sfcScriptLangs?.[bi] === "ts" || context?.sfcScriptLangs?.[bi] === "tsx";
497
+ if (isTs) dispatchedToTypeScript = true;
498
+ {
499
+ const scriptContent = scriptTree.rootNode?.text ?? "";
500
+ const lineOffset = scriptStartLine - 1;
501
+ const colOffset = scriptStartColumn;
502
+ const defaultExport = this.findExportDefault(scriptTree.rootNode);
503
+ const defaultObject = defaultExport ? this.findDescendantObject(defaultExport) : void 0;
504
+ if (defaultObject) patterns["Options API"] = true;
505
+ else patterns["Composition API"] = true;
506
+ let sub;
507
+ try {
508
+ sub = isTs ? new RealTypeScriptAnalyzer().analyze(scriptTree.rootNode, scriptContent, filePath) : new RealJavaScriptAnalyzer().analyze(scriptTree.rootNode, scriptContent, filePath, context);
509
+ } catch (e) {
510
+ sub = { symbols: [], relationships: [], imports: [], exports: [], errors: [], metadata: {} };
511
+ errors.push({
512
+ message: `script sub-parse failed: ${e?.message?.slice(0, 120)}`,
513
+ range: { startLine: scriptStartLine, startColumn: 0, endLine: scriptStartLine, endColumn: 0 },
514
+ severity: "warning"
515
+ });
516
+ }
517
+ scriptCyclomaticSum += Math.max(0, (sub.metadata?.cyclomaticComplexity ?? 1) - 1);
518
+ const defaultPayloads = /* @__PURE__ */ new Map();
519
+ if (defaultObject) {
520
+ for (const s of sub.symbols ?? []) {
521
+ if ((s.range.startIndex ?? 0) >= defaultObject.startIndex && (s.range.endIndex ?? 0) <= defaultObject.endIndex && (s.kind === ASTEngine.SymbolKind.Method || s.kind === ASTEngine.SymbolKind.Function) && !defaultPayloads.has(s.name)) {
522
+ defaultPayloads.set(s.name, s);
523
+ }
524
+ }
525
+ }
526
+ if (defaultObject) {
527
+ let componentName = moduleName;
528
+ const namePair = defaultObject.namedChildren.find(
529
+ (c) => c.type === "pair" && c.childForFieldName("key")?.text === "name"
530
+ );
531
+ let declaredNameUnreadable = false;
532
+ if (namePair) {
533
+ const v = namePair.childForFieldName("value");
534
+ const declared = this.declaredNameValue(v, scriptTree.rootNode);
535
+ if (declared) {
536
+ componentName = declared.name;
537
+ declaredNameFromLocalConst = declared.constName;
538
+ } else if (v) declaredNameUnreadable = true;
539
+ }
540
+ declaredNameUnread = declaredNameUnread || declaredNameUnreadable;
541
+ if (componentName !== moduleName) {
542
+ const sfcSymbol = symbols.find((sy) => sy.kind === ASTEngine.SymbolKind.Class && sy.name === moduleName);
543
+ if (sfcSymbol) {
544
+ sfcSymbol.name = componentName;
545
+ sfcSymbol.metadata = {
546
+ ...sfcSymbol.metadata ?? {},
547
+ moduleName,
548
+ isOptionsComponent: true,
549
+ ...declaredNameUnreadable ? { declaredNameUnreadable: true } : {}
550
+ };
551
+ componentIdentity = componentName;
552
+ }
553
+ }
554
+ pushedComponentNames.add(componentName);
555
+ this.extractOptionsApi(defaultObject, componentName, symbols, patterns, lineOffset, colOffset, defaultPayloads, scriptStartIndex, scriptTree.rootNode, relationships);
556
+ }
557
+ const defaultStart = defaultObject ? defaultObject.startIndex : -1;
558
+ const defaultEnd = defaultObject ? defaultObject.endIndex : -1;
559
+ for (const s of sub.symbols ?? []) {
560
+ if (defaultStart !== -1 && (s.range.startIndex ?? 0) >= defaultStart && (s.range.endIndex ?? 0) <= defaultEnd) continue;
561
+ s.range.startLine += lineOffset;
562
+ s.range.endLine += lineOffset;
563
+ if (s.range.startLine === 1 + lineOffset) s.range.startColumn += colOffset;
564
+ if (scriptStartIndex && s.range.startIndex !== void 0) s.range.startIndex += scriptStartIndex;
565
+ if (scriptStartIndex && s.range.endIndex !== void 0) s.range.endIndex += scriptStartIndex;
566
+ shiftMetadataLineMirrors(s.metadata, lineOffset);
567
+ symbols.push(s);
568
+ }
569
+ const delegateBases = /* @__PURE__ */ new Map();
570
+ for (const rb of sub.relationships ?? []) {
571
+ if (rb?.type !== ASTEngine.RelationshipType.Extends) continue;
572
+ if (typeof rb.source === "string" && rb.source !== "" && typeof rb.target === "string" && rb.target !== "") {
573
+ delegateBases.set(rb.source, rb.target);
574
+ }
575
+ }
576
+ for (const r of sub.relationships ?? []) {
577
+ const tgt = r.target ?? "";
578
+ if (VUE_COMPILER_MACROS.has(tgt)) continue;
579
+ if (String(r.type ?? r.relationshipType) === ASTEngine.RelationshipType.Exports && tgt === "default") {
580
+ if (r.metadata?.isDefault !== true) continue;
581
+ }
582
+ if (typeof r.line_number === "number") r.line_number += lineOffset;
583
+ shiftMetadataLineMirrors(r.metadata, lineOffset);
584
+ if (r.source === DELEGATE_FILE_ACTOR) r.source = moduleName;
585
+ if (r.type === ASTEngine.RelationshipType.Imports) {
586
+ const mod = r.metadata?.module;
587
+ if (typeof mod === "string" && mod !== "" && typeof r.target === "string" && r.target !== "" && !r.target.includes(":") && !r.target.includes("/")) {
588
+ r.target = `${mod}:${r.target}`;
589
+ }
590
+ }
591
+ if (r.type === ASTEngine.RelationshipType.Overrides) {
592
+ if (typeof r.source === "string" && typeof r.target === "string" && r.target !== "" && !r.target.includes(".")) {
593
+ const owner = r.source.slice(0, r.source.lastIndexOf("."));
594
+ const base = owner === "" ? void 0 : delegateBases.get(owner);
595
+ if (base) r.target = `${base}.${r.target}`;
596
+ }
597
+ }
598
+ r.metadata = { ...r.metadata ?? {}, sfcDelegated: true, sfcLineOffset: lineOffset };
599
+ relationships.push(r);
600
+ }
601
+ const namedDefaultDeclaration = defaultExport ? (defaultExport.namedChildren ?? []).some(
602
+ (c) => c.type === "class_declaration" || c.type === "abstract_class_declaration" || c.type === "function_declaration" || c.type === "generator_function_declaration"
603
+ ) : false;
604
+ if (isTs && defaultExport && !namedDefaultDeclaration) {
605
+ const alreadyPublished = relationships.some(
606
+ (x) => String(x.type ?? x.relationshipType) === ASTEngine.RelationshipType.Exports && String(x.target ?? "") === "default" && x.metadata?.isDefault === true
607
+ );
608
+ if (!alreadyPublished) {
609
+ relationships.push({
610
+ source: moduleName,
611
+ target: "default",
612
+ type: ASTEngine.RelationshipType.Exports,
613
+ confidence: 1,
614
+ line_number: defaultExport.startPosition.row + 1 + lineOffset,
615
+ metadata: { isDefault: true, sfcDefaultExport: true }
616
+ });
617
+ }
618
+ }
619
+ for (const i of sub.imports ?? []) {
620
+ if (i.range) {
621
+ const shifted = this.shiftScriptRange(i.range, lineOffset, colOffset, scriptStartIndex);
622
+ i.range.startLine = shifted.startLine;
623
+ i.range.endLine = shifted.endLine;
624
+ i.range.startColumn = shifted.startColumn;
625
+ i.range.endColumn = shifted.endColumn;
626
+ if (shifted.startIndex !== void 0) i.range.startIndex = shifted.startIndex;
627
+ if (shifted.endIndex !== void 0) i.range.endIndex = shifted.endIndex;
628
+ }
629
+ imports.push(i);
630
+ }
631
+ for (const e of sub.exports ?? []) {
632
+ if (e.range) {
633
+ const shifted = this.shiftScriptRange(e.range, lineOffset, colOffset, scriptStartIndex);
634
+ e.range.startLine = shifted.startLine;
635
+ e.range.endLine = shifted.endLine;
636
+ e.range.startColumn = shifted.startColumn;
637
+ e.range.endColumn = shifted.endColumn;
638
+ if (shifted.startIndex !== void 0) e.range.startIndex = shifted.startIndex;
639
+ if (shifted.endIndex !== void 0) e.range.endIndex = shifted.endIndex;
640
+ }
641
+ exports.push(e);
642
+ }
643
+ for (const er of sub.errors ?? []) {
644
+ if (er.range) {
645
+ const shifted = this.shiftScriptRange(er.range, lineOffset, colOffset, scriptStartIndex);
646
+ er.range.startLine = shifted.startLine;
647
+ er.range.endLine = shifted.endLine;
648
+ er.range.startColumn = shifted.startColumn;
649
+ er.range.endColumn = shifted.endColumn;
650
+ if (shifted.startIndex !== void 0) er.range.startIndex = shifted.startIndex;
651
+ if (shifted.endIndex !== void 0) er.range.endIndex = shifted.endIndex;
652
+ }
653
+ errors.push(er);
654
+ }
655
+ {
656
+ const macroStack = [scriptTree.rootNode];
657
+ const seenMacroNames = /* @__PURE__ */ new Set();
658
+ while (macroStack.length > 0) {
659
+ const n = macroStack.pop();
660
+ if (n.type === "call_expression") {
661
+ const fn = n.childForFieldName("function");
662
+ if (fn && (fn.text === "defineProps" || fn.text === "defineEmits")) {
663
+ const args = n.childForFieldName("arguments");
664
+ const firstArg = args?.namedChild(0);
665
+ const which = fn.text === "defineProps" ? "props" : "emits";
666
+ const defaultsArg = which === "props" ? this.withDefaultsObject(n) : null;
667
+ const propDefaults = this.defaultsEntries(defaultsArg);
668
+ const defaultsExpression = defaultsArg && defaultsArg.type !== "object" ? defaultsArg.text : void 0;
669
+ const typeArgsNode = this.findChildByType(n, "type_arguments");
670
+ for (const part of this.contractTypeShape(typeArgsNode).participants) {
671
+ const partKey = `${which}:${part.name}`;
672
+ if (seenMacroNames.has(partKey)) continue;
673
+ seenMacroNames.add(partKey);
674
+ pendingContracts.push({
675
+ name: part.name,
676
+ marker: which === "props" ? "isPropContract" : "isEmitContract",
677
+ resolved: this.hasTopLevelDeclaration(part.name, n),
678
+ line: this.offsetRange(this.nodeToRange(part.node), lineOffset, colOffset, scriptStartIndex).startLine,
679
+ lineOffset,
680
+ // F3, and a consequence of moving the participant push UP here:
681
+ // this row is now the FIRST to claim `seenMacroNames`, so it is
682
+ // also the row the F12 `macroContractName` branch skips. Without
683
+ // the defaults carried along, adding F14 silently stripped every
684
+ // withDefaults default off every NAMED contract — the fixture
685
+ // caught it, which is the only reason it is worth recording.
686
+ ...propDefaults.size > 0 ? { defaults: propDefaults } : {},
687
+ ...defaultsExpression ? { defaultsExpression } : {}
688
+ });
689
+ }
690
+ const declRefs = this.propDeclarationEntries(firstArg ?? void 0);
691
+ for (const d of declRefs) {
692
+ const contractKey = `${which}:${d.name}`;
693
+ if (seenMacroNames.has(contractKey)) continue;
694
+ seenMacroNames.add(contractKey);
695
+ pendingContracts.push({
696
+ name: d.name,
697
+ marker: which === "props" ? "isPropContract" : "isEmitContract",
698
+ // Same certainty as the macro hoist: the name is written here,
699
+ // but this lane has not read the declaration, so the row is the
700
+ // weaker of the two — matching `defineProps(binding)`'s shape
701
+ // rather than claiming more resolution than it did.
702
+ resolved: false,
703
+ line: this.offsetRange(this.nodeToRange(d.node), lineOffset, colOffset, scriptStartIndex).startLine,
704
+ lineOffset
705
+ });
706
+ }
707
+ let fromBinding = false;
708
+ let inlineGeneric = false;
709
+ let entries = this.propEntries(firstArg ?? void 0);
710
+ if (entries.length === 0) {
711
+ entries = this.genericMacroEntries(n, which);
712
+ if (entries.length > 0) inlineGeneric = true;
713
+ }
714
+ if (entries.length === 0) {
715
+ entries = this.boundMacroEntries(firstArg ?? void 0, scriptTree.rootNode, which);
716
+ if (entries.length > 0) fromBinding = true;
717
+ }
718
+ if (entries.length === 0) {
719
+ const declared = this.macroContractName(n, firstArg ?? void 0, which);
720
+ if (declared) {
721
+ const key = `${which}:${declared.name}`;
722
+ if (!seenMacroNames.has(key)) {
723
+ seenMacroNames.add(key);
724
+ const macroKey = which === "props" ? "isPropContract" : "isEmitContract";
725
+ pendingContracts.push({
726
+ name: declared.name,
727
+ marker: macroKey,
728
+ module: declared.module,
729
+ resolved: declared.resolved,
730
+ line: this.offsetRange(this.nodeToRange(declared.node), lineOffset, colOffset, scriptStartIndex).startLine,
731
+ lineOffset,
732
+ ...propDefaults.size > 0 ? { defaults: propDefaults } : {},
733
+ ...defaultsExpression ? { defaultsExpression } : {}
734
+ });
735
+ }
736
+ continue;
737
+ }
738
+ }
739
+ const kind = fn.text === "defineProps" ? ASTEngine.SymbolKind.Property : ASTEngine.SymbolKind.Event;
740
+ const meta = fn.text === "defineProps" ? "isProp" : "isEmit";
741
+ for (const { name: pn, node: pnNode } of entries) {
742
+ const dedupe = `${meta}:${pn}`;
743
+ if (seenMacroNames.has(dedupe)) continue;
744
+ seenMacroNames.add(dedupe);
745
+ symbols.push({
746
+ name: pn,
747
+ kind,
748
+ range: this.offsetRange(this.nodeToRange(pnNode), lineOffset, colOffset, scriptStartIndex),
749
+ // F8: a contract symbol belongs to the ONE component. `name:`
750
+ // is read from a `name:` option in the FIRST block, so by the
751
+ // time a `<script setup>` block's macros run, `componentIdentity`
752
+ // already holds the declared name — and a script-setup prop
753
+ // owned by the FILE STEM while an Options prop is owned by the
754
+ // component is exactly the split identity F8 removed.
755
+ scope: componentIdentity,
756
+ ownerName: componentIdentity,
757
+ ownerKind: ASTEngine.SymbolKind.Class,
758
+ visibility: "public",
759
+ metadata: {
760
+ [meta]: true,
761
+ isScriptSetup: true,
762
+ // Which shape the names came from: "inline" (a literal
763
+ // argument), "generic" (an inline type literal) or
764
+ // "binding" (a same-file declaration resolved by name).
765
+ contractSource: fromBinding ? "binding" : inlineGeneric ? "generic" : "inline",
766
+ // F3: the `withDefaults` default for this prop, as WRITTEN
767
+ // SOURCE. A same-file binding is included, which is why this
768
+ // is keyed off the map rather than off the shape: a binding's
769
+ // members have no type annotation, and the default is the only
770
+ // thing that says the prop is genuinely optional-by-default.
771
+ ...propDefaults.has(pn) ? { defaultValue: propDefaults.get(pn), hasDefault: true } : {}
772
+ }
773
+ });
774
+ }
775
+ }
776
+ }
777
+ for (let i = 0; i < n.namedChildCount; i++) {
778
+ const c = n.namedChild(i);
779
+ if (c) macroStack.push(c);
780
+ }
781
+ }
782
+ {
783
+ const pjStack = [scriptTree.rootNode];
784
+ while (pjStack.length > 0) {
785
+ const n = pjStack.pop();
786
+ if (n.type === "call_expression") {
787
+ const callee = n.childForFieldName("function")?.text;
788
+ if (callee === "provide" || callee === "inject") {
789
+ const vueContract = this.contractNameIsVueBinding(callee, n);
790
+ const argsNode = n.childForFieldName("arguments");
791
+ const keyNode = argsNode?.namedChild(0);
792
+ if (vueContract && keyNode && (keyNode.type === "identifier" || keyNode.type === "string")) {
793
+ const keyName = keyNode.type === "string" ? unquote(keyNode.text) : keyNode.text;
794
+ if (keyName) {
795
+ pendingInjections.push({
796
+ key: keyName,
797
+ marker: callee === "provide" ? "isProvideContract" : "isInjectContract",
798
+ line: this.offsetRange(this.nodeToRange(keyNode), lineOffset, colOffset, scriptStartIndex).startLine,
799
+ lineOffset
800
+ });
801
+ if (callee === "provide") {
802
+ const value = this.providedValueObject(argsNode?.namedChild(1) ?? null);
803
+ if (value) {
804
+ for (let i = 0; i < value.namedChildCount; i++) {
805
+ const c = value.namedChild(i);
806
+ if (!c || c.type !== "pair" && c.type !== "method_definition" && c.type !== "shorthand_property_identifier") continue;
807
+ const shorthand = c.type === "shorthand_property_identifier";
808
+ const k = shorthand ? c.text : c.type === "pair" ? c.childForFieldName("key")?.text : c.childForFieldName("name")?.text;
809
+ if (k && !k.startsWith("[")) {
810
+ pendingProvided.push({
811
+ name: unquote(k),
812
+ owner: moduleName,
813
+ line: n.startPosition.row + 1 + scriptStartLine,
814
+ column: n.startPosition.column + 1 + (context.sfcScriptCols[bi] ?? 0)
815
+ });
816
+ }
817
+ }
818
+ }
819
+ }
820
+ }
821
+ }
822
+ }
823
+ }
824
+ for (let i = 0; i < n.namedChildCount; i++) {
825
+ const c = n.namedChild(i);
826
+ if (c) pjStack.push(c);
827
+ }
828
+ }
829
+ }
830
+ }
831
+ }
832
+ }
833
+ for (const [bi, scriptTree] of (context?.sfcScriptTrees ?? []).entries()) {
834
+ const optsCall = this.findCallNamed(scriptTree.rootNode, "defineOptions");
835
+ if (!optsCall) continue;
836
+ const optsObj = this.firstObjectArgument(optsCall);
837
+ if (!optsObj) continue;
838
+ const stack = [optsObj];
839
+ while (stack.length > 0 && declaredNameFromOptions === void 0) {
840
+ const n = stack.pop();
841
+ const keyNode = this.findChildByType(n, "property_identifier");
842
+ if (n.type === "pair" && keyNode && keyNode.text === "name") {
843
+ const value = n.childForFieldName("value") ?? this.findChildByType(n, "string");
844
+ const declared = this.declaredNameValue(value, scriptTree.rootNode);
845
+ if (declared) {
846
+ declaredNameFromOptions = declared.name;
847
+ declaredNameFromLocalConst = declared.constName;
848
+ } else if (value) declaredNameUnreadSetup = true;
849
+ }
850
+ for (let i = 0; i < n.namedChildCount; i++) {
851
+ const c = n.namedChild(i);
852
+ if (c) stack.push(c);
853
+ }
854
+ }
855
+ if (declaredNameFromOptions) {
856
+ optionsScriptIndex = bi;
857
+ break;
858
+ }
859
+ }
860
+ if (declaredNameFromOptions && declaredNameFromOptions !== componentIdentity) {
861
+ const cls = symbols.find(
862
+ (sy) => sy.kind === ASTEngine.SymbolKind.Class && sy.name === componentIdentity && sy.metadata?.isVueComponent === true
863
+ );
864
+ if (cls) {
865
+ cls.metadata = {
866
+ ...cls.metadata ?? {},
867
+ moduleName: componentIdentity,
868
+ isScriptSetupComponent: true
869
+ };
870
+ const col = context.sfcScriptCols?.[optionsScriptIndex] ?? 0;
871
+ cls.range = { ...cls.range, startColumn: cls.range.startColumn + col };
872
+ const oldIdentity = cls.name;
873
+ for (const sym of symbols) {
874
+ if (sym.ownerName === oldIdentity || sym.ownerName === moduleName) sym.ownerName = declaredNameFromOptions;
875
+ if (sym.scope === oldIdentity || sym.scope === moduleName) sym.scope = declaredNameFromOptions;
876
+ }
877
+ for (const e of relationships) {
878
+ if (e.source === oldIdentity || e.source === moduleName) e.source = declaredNameFromOptions;
879
+ if (e.target === oldIdentity || e.target === moduleName) e.target = declaredNameFromOptions;
880
+ }
881
+ cls.name = declaredNameFromOptions;
882
+ componentIdentity = declaredNameFromOptions;
883
+ }
884
+ }
885
+ {
886
+ const cls = symbols.find(
887
+ (sy) => sy.kind === ASTEngine.SymbolKind.Class && sy.metadata?.isVueComponent === true
888
+ );
889
+ if (cls) {
890
+ if (cls.name === moduleName) cls.metadata = { ...cls.metadata ?? {}, synthesized: true, identityFromFileStem: true };
891
+ if (declaredNameUnread || declaredNameUnreadSetup) cls.metadata = { ...cls.metadata ?? {}, declaredNameUnreadable: true };
892
+ if (declaredNameFromLocalConst) {
893
+ cls.metadata = { ...cls.metadata ?? {}, nameFromLocalConst: declaredNameFromLocalConst };
894
+ }
895
+ }
896
+ }
897
+ for (const c of pendingInjections) {
898
+ const meta = { [c.marker]: true, sfcDelegated: true, sfcLineOffset: c.lineOffset };
899
+ const existing = relationships.find(
900
+ (x) => x.source === moduleName && x.target === c.key && String(x.type ?? x.relationshipType) === String(ASTEngine.RelationshipType.References)
901
+ );
902
+ if (existing) {
903
+ existing.metadata = { ...existing.metadata ?? {}, ...meta };
904
+ } else {
905
+ relationships.push({
906
+ source: moduleName,
907
+ target: c.key,
908
+ type: ASTEngine.RelationshipType.References,
909
+ confidence: 0.9,
910
+ line_number: c.line,
911
+ metadata: meta
912
+ });
913
+ }
914
+ }
915
+ for (const c of pendingContracts) {
916
+ const contractMeta = {
917
+ [c.marker]: true,
918
+ ...c.module ? { contractModule: c.module } : {},
919
+ ...c.resolved ? {} : { unresolved: true },
920
+ // F3: the defaults map, plus its size so a consumer can answer "does this
921
+ // contract declare defaults" without walking the keys.
922
+ ...c.defaults && c.defaults.size > 0 ? { defaults: Object.fromEntries(c.defaults), defaultKeys: c.defaults.size } : {},
923
+ // F3: an indirect defaults expression, kept as text. Deliberately NOT
924
+ // `defaultKeys: 0` — zero keys would claim the component declares no
925
+ // defaults, which is the opposite of the truth.
926
+ ...c.defaultsExpression ? { defaultsExpression: c.defaultsExpression } : {}
927
+ };
928
+ const sameEdge = (x) => x.target === c.name && String(x.type ?? x.relationshipType) === String(ASTEngine.RelationshipType.References) && x.metadata?.sfcDelegated === true && (x.line_number ?? x.line) === c.line;
929
+ const existing = relationships.find(sameEdge);
930
+ if (existing) {
931
+ existing.metadata = { ...existing.metadata ?? {}, ...contractMeta };
932
+ if (existing.metadata?.sfcDelegated === true) existing.source = componentIdentity;
933
+ } else {
934
+ relationships.push({
935
+ source: componentIdentity,
936
+ target: c.name,
937
+ type: ASTEngine.RelationshipType.References,
938
+ confidence: c.resolved ? 1 : 0.8,
939
+ line_number: c.line,
940
+ metadata: { ...contractMeta, sfcDelegated: true, sfcLineOffset: c.lineOffset }
941
+ });
942
+ }
943
+ }
944
+ if (templateExprNames.size > 0) {
945
+ const memberNames = new Set(symbols.map((s) => s.name));
946
+ const seenUses = /* @__PURE__ */ new Set();
947
+ for (const [name, line] of templateExprNames) {
948
+ if (!memberNames.has(name)) continue;
949
+ const key = `${moduleName}:${name}`;
950
+ if (seenUses.has(key)) continue;
951
+ seenUses.add(key);
952
+ relationships.push({
953
+ source: moduleName,
954
+ target: name,
955
+ type: ASTEngine.RelationshipType.Uses,
956
+ confidence: 0.95,
957
+ // (#825): the line the identifier is WRITTEN on (the fragment's
958
+ // base plus its offset inside it).
959
+ line_number: line,
960
+ metadata: { isTemplateExpression: true }
961
+ });
962
+ }
963
+ }
964
+ if (componentIdentity !== moduleName) {
965
+ for (const rel of relationships) {
966
+ if (rel.source === moduleName) rel.source = componentIdentity;
967
+ }
968
+ }
969
+ for (const p of pendingProvided) {
970
+ const sym = symbols.find((sy) => sy.name === p.name && sy.ownerName === void 0);
971
+ if (sym) {
972
+ sym.ownerName = p.owner;
973
+ sym.ownerKind = ASTEngine.SymbolKind.Class;
974
+ sym.scope = p.owner;
975
+ sym.metadata = { ...sym.metadata ?? {}, isProvided: true };
976
+ continue;
977
+ }
978
+ if (symbols.some((sy) => sy.name === p.name && sy.ownerName === p.owner)) continue;
979
+ symbols.push({
980
+ name: p.name,
981
+ kind: ASTEngine.SymbolKind.Variable,
982
+ ownerName: p.owner,
983
+ ownerKind: ASTEngine.SymbolKind.Class,
984
+ scope: p.owner,
985
+ line_number: p.line,
986
+ range: { startLine: p.line, startColumn: p.column, endLine: p.line, endColumn: p.column + p.name.length },
987
+ metadata: { isProvided: true, synthesized: true }
988
+ });
989
+ }
990
+ const existingContains = /* @__PURE__ */ new Set();
991
+ for (const r of relationships) {
992
+ if (r.type === ASTEngine.RelationshipType.Contains && r.source && r.target) {
993
+ existingContains.add(`${r.source}::${r.target}`);
994
+ }
995
+ }
996
+ for (const s of symbols) {
997
+ if (s.ownerName) {
998
+ const key = `${s.ownerName}::${s.name}`;
999
+ if (existingContains.has(key)) continue;
1000
+ existingContains.add(key);
1001
+ relationships.push({
1002
+ source: s.ownerName,
1003
+ target: s.name,
1004
+ type: ASTEngine.RelationshipType.Contains,
1005
+ confidence: 1,
1006
+ // (#825): the MEMBER's declaration line (its symbol range, already
1007
+ // shifted to SFC coordinates) — the edge names the member.
1008
+ ...s.range?.startLine ? { line_number: s.range.startLine } : {},
1009
+ metadata: {}
1010
+ });
1011
+ }
1012
+ }
1013
+ const architecturalScore = Math.min(100, Math.max(0, 100 - errors.length * 5));
1014
+ const detectedPatterns = Object.keys(patterns);
1015
+ const frameworkPatterns = detectedPatterns.map((p) => ({
1016
+ framework: "Vue",
1017
+ patterns: [{ type: "usage", detected: true, description: p }]
1018
+ }));
1019
+ this.classifyEvidenceTier(relationships);
1020
+ if (componentIdentity !== moduleName) {
1021
+ for (const sym of symbols) {
1022
+ if (sym.ownerName === moduleName) sym.ownerName = componentIdentity;
1023
+ if (sym.scope === moduleName) sym.scope = componentIdentity;
1024
+ }
1025
+ for (const e of relationships) {
1026
+ if (e.source === moduleName) e.source = componentIdentity;
1027
+ }
1028
+ }
1029
+ return {
1030
+ language: "vue",
1031
+ symbols,
1032
+ relationships,
1033
+ imports,
1034
+ exports,
1035
+ errors,
1036
+ metadata: {
1037
+ // Real cyclomatic complexity: script-block decisions (per JS/TS
1038
+ // contract) plus template decision directives (v-if/v-for/v-show).
1039
+ // Was hardcoded 1 / the synthetic symbols+relationships heuristic.
1040
+ complexity: 1 + scriptCyclomaticSum + templateDecisions,
1041
+ linesOfCode: content.split("\n").length,
1042
+ cyclomaticComplexity: 1 + scriptCyclomaticSum + templateDecisions,
1043
+ framework: "Vue",
1044
+ patterns: detectedPatterns,
1045
+ detectedFrameworks: ["Vue"],
1046
+ frameworkPatterns,
1047
+ architecturalInsights: [],
1048
+ architecturalScore,
1049
+ codeQuality: errors.length === 0 ? "Excellent" : "Fair",
1050
+ // F7: true iff a <script> block was parsed with a TypeScript grammar
1051
+ // (lang="ts" or lang="tsx", #903's three-way dispatch). A plain block,
1052
+ // or an SFC with no script, is the JavaScript analyzer and stays false.
1053
+ hasTypeInference: dispatchedToTypeScript,
1054
+ hasFrameworkIntelligence: true,
1055
+ analysisVersion: "5.0.0-m12.12-family-contract"
1056
+ }
1057
+ };
1058
+ }
1059
+ // ============================================================================
1060
+ // OPTIONS API EXTRACTION
1061
+ // ============================================================================
1062
+ /**
1063
+ * Which shape the LAST contract resolution came from. A field rather than a
1064
+ * return value because the two call sites (the Options options and the
1065
+ * script-setup macros) each consume the entries in their own loop; the marker
1066
+ * is what lets a fixture pin the shape and a mutant revert one of them
1067
+ * without touching the others.
1068
+ */
1069
+ lastContractSource = "none";
1070
+ extractOptionsApi(objectNode, componentName, symbols, patterns, lineOffset, colOffset, payloads, indexOffset, scriptRoot, relationships) {
1071
+ const memberName = (n) => {
1072
+ const key = n.childForFieldName("key");
1073
+ if (key) return key.text;
1074
+ const name = n.childForFieldName("name");
1075
+ if (name) return name.text;
1076
+ return this.findChildByType(n, "property_identifier")?.text;
1077
+ };
1078
+ const mergeBases = [];
1079
+ for (let i = 0; i < objectNode.namedChildCount; i++) {
1080
+ const child = objectNode.namedChild(i);
1081
+ if (!child) continue;
1082
+ if (child.type !== "pair" && child.type !== "method_definition") continue;
1083
+ const key = memberName(child);
1084
+ if (!key) continue;
1085
+ const value = child.type === "pair" ? child.childForFieldName("value") : void 0;
1086
+ if (key === "methods" && value) {
1087
+ patterns["Options API"] = true;
1088
+ this.emitObjectMembers(value, componentName, symbols, ASTEngine.SymbolKind.Method, "method", lineOffset, colOffset, payloads, indexOffset);
1089
+ } else if (key === "computed" && value) {
1090
+ patterns["Options API"] = true;
1091
+ this.emitObjectMembers(value, componentName, symbols, ASTEngine.SymbolKind.Property, "computed", lineOffset, colOffset, payloads, indexOffset);
1092
+ } else if (key === "data") {
1093
+ symbols.push({
1094
+ name: "data",
1095
+ kind: ASTEngine.SymbolKind.Method,
1096
+ range: this.offsetRange(this.nodeToRange(child), lineOffset, colOffset, indexOffset),
1097
+ scope: componentName,
1098
+ ownerName: componentName,
1099
+ ownerKind: ASTEngine.SymbolKind.Class,
1100
+ visibility: "public",
1101
+ metadata: { isOptionsData: true }
1102
+ });
1103
+ const dataValue = child.type === "pair" ? child.childForFieldName("value") : void 0;
1104
+ if (dataValue && dataValue.type === "object") {
1105
+ this.emitDataKeys(dataValue, componentName, symbols, lineOffset, colOffset, indexOffset);
1106
+ } else if (child.type === "method_definition") {
1107
+ const returned = this.findReturnedObject(child);
1108
+ if (returned) this.emitDataKeys(returned, componentName, symbols, lineOffset, colOffset, indexOffset);
1109
+ } else if (dataValue && (dataValue.type === "arrow_function" || dataValue.type === "function_expression" || dataValue.type === "function")) {
1110
+ const returned = this.findReturnedObject(dataValue);
1111
+ if (returned) this.emitDataKeys(returned, componentName, symbols, lineOffset, colOffset, indexOffset);
1112
+ }
1113
+ } else if (key === "props") {
1114
+ for (const { name: pn, node: pnode } of this.resolveContractEntries(value ?? child, scriptRoot, "props", relationships, componentName)) {
1115
+ symbols.push({
1116
+ name: pn,
1117
+ kind: ASTEngine.SymbolKind.Property,
1118
+ // (#825): the prop KEY's own node — the line and column a
1119
+ // navigation to this prop must land on.
1120
+ range: this.offsetRange(this.nodeToRange(pnode), lineOffset, colOffset, indexOffset),
1121
+ scope: componentName,
1122
+ ownerName: componentName,
1123
+ ownerKind: ASTEngine.SymbolKind.Class,
1124
+ visibility: "public",
1125
+ metadata: { isProp: true, contractSource: this.lastContractSource }
1126
+ });
1127
+ }
1128
+ } else if (key === "emits") {
1129
+ for (const { name: en, node: enode } of this.resolveContractEntries(value ?? child, scriptRoot, "emits", relationships, componentName)) {
1130
+ symbols.push({
1131
+ name: en,
1132
+ kind: ASTEngine.SymbolKind.Event,
1133
+ range: this.offsetRange(this.nodeToRange(enode), lineOffset, colOffset, indexOffset),
1134
+ scope: componentName,
1135
+ ownerName: componentName,
1136
+ ownerKind: ASTEngine.SymbolKind.Class,
1137
+ visibility: "public",
1138
+ metadata: { isEmit: true, contractSource: this.lastContractSource }
1139
+ });
1140
+ }
1141
+ } else if (key === "extends" || key === "mixins") {
1142
+ if (!value || !relationships) continue;
1143
+ const isMixin = key === "mixins";
1144
+ for (const base of this.identifiersOf(value)) {
1145
+ relationships.push({
1146
+ source: componentName,
1147
+ target: base.name,
1148
+ type: ASTEngine.RelationshipType.Extends,
1149
+ // A named component the analyzer cannot see is the Vue
1150
+ // documentation's own guarantee — the option is resolved by Vue at
1151
+ // runtime — so this is a declaration of the relationship, not a
1152
+ // guess. It is not `structural` in the closure sense (nothing in
1153
+ // this file states the base's members), which is why the
1154
+ // confidence is below 1.
1155
+ confidence: 0.9,
1156
+ line_number: this.offsetRange(this.nodeToRange(base.node), lineOffset, colOffset, indexOffset).startLine,
1157
+ metadata: { isMixin, isOptionsApi: true }
1158
+ });
1159
+ }
1160
+ for (const base of this.identifiersOf(value)) {
1161
+ mergeBases.push({
1162
+ name: base.name,
1163
+ isMixin,
1164
+ node: base.node,
1165
+ optionsObject: this.sameTreeOptionsObject(base.node, value, scriptRoot)
1166
+ });
1167
+ }
1168
+ if (value.type === "object") {
1169
+ for (let i2 = 0; i2 < value.namedChildCount; i2++) {
1170
+ const sub = value.namedChild(i2);
1171
+ if (!sub || sub.type !== "pair" && sub.type !== "method_definition") continue;
1172
+ const subKey = sub.type === "pair" ? sub.childForFieldName("key")?.text : sub.childForFieldName("name")?.text;
1173
+ const subValue = sub.type === "pair" ? sub.childForFieldName("value") : void 0;
1174
+ if (!subKey) continue;
1175
+ if ((subKey === "methods" || subKey === "computed") && subValue) {
1176
+ this.emitObjectMembers(
1177
+ subValue,
1178
+ componentName,
1179
+ symbols,
1180
+ subKey === "methods" ? ASTEngine.SymbolKind.Method : ASTEngine.SymbolKind.Property,
1181
+ subKey === "methods" ? "method" : "computed",
1182
+ lineOffset,
1183
+ colOffset,
1184
+ payloads,
1185
+ indexOffset
1186
+ );
1187
+ } else if (subKey === "props" && subValue) {
1188
+ for (const { name: pn, node: pnode } of this.propEntries(subValue)) {
1189
+ symbols.push({
1190
+ name: pn,
1191
+ kind: ASTEngine.SymbolKind.Property,
1192
+ range: this.offsetRange(this.nodeToRange(pnode), lineOffset, colOffset, indexOffset),
1193
+ scope: componentName,
1194
+ ownerName: componentName,
1195
+ ownerKind: ASTEngine.SymbolKind.Class,
1196
+ visibility: "public",
1197
+ metadata: { isProp: true, inheritedFrom: "extends" }
1198
+ });
1199
+ }
1200
+ }
1201
+ }
1202
+ }
1203
+ } else if (key === "watch") {
1204
+ if (value) {
1205
+ patterns["Options API"] = true;
1206
+ this.emitObjectMembers(value, componentName, symbols, ASTEngine.SymbolKind.Method, "isWatcher", lineOffset, colOffset, payloads, indexOffset);
1207
+ }
1208
+ } else if (key === "setup") {
1209
+ patterns["Options API"] = true;
1210
+ symbols.push({
1211
+ name: key,
1212
+ kind: ASTEngine.SymbolKind.Method,
1213
+ range: this.offsetRange(this.nodeToRange(child), lineOffset, colOffset, indexOffset),
1214
+ scope: componentName,
1215
+ ownerName: componentName,
1216
+ ownerKind: ASTEngine.SymbolKind.Class,
1217
+ visibility: "public",
1218
+ metadata: { isSetup: true }
1219
+ });
1220
+ } else if (this.isLifecycleHook(key)) {
1221
+ symbols.push({
1222
+ name: key,
1223
+ kind: ASTEngine.SymbolKind.Method,
1224
+ range: this.offsetRange(this.nodeToRange(child), lineOffset, colOffset, indexOffset),
1225
+ scope: componentName,
1226
+ ownerName: componentName,
1227
+ ownerKind: ASTEngine.SymbolKind.Class,
1228
+ visibility: "public",
1229
+ metadata: { isLifecycleHook: true }
1230
+ });
1231
+ }
1232
+ }
1233
+ if (mergeBases.length > 0 && relationships) {
1234
+ const MEMBER_KINDS = /* @__PURE__ */ new Set([
1235
+ ASTEngine.SymbolKind.Method,
1236
+ ASTEngine.SymbolKind.Property,
1237
+ ASTEngine.SymbolKind.Event
1238
+ ]);
1239
+ const own = /* @__PURE__ */ new Map();
1240
+ for (const s of symbols) {
1241
+ if (s.ownerName !== componentName) continue;
1242
+ if (!MEMBER_KINDS.has(s.kind)) continue;
1243
+ own.set(s.name, s);
1244
+ }
1245
+ const emitted = /* @__PURE__ */ new Set();
1246
+ for (const base of mergeBases) {
1247
+ const baseMembers = base.optionsObject ? this.optionsMemberNames(base.optionsObject) : void 0;
1248
+ if (!baseMembers || baseMembers.size === 0) continue;
1249
+ for (const [name, sym] of own) {
1250
+ if (!baseMembers.has(name)) continue;
1251
+ const edgeKey = `${componentName}.${name}->${base.name}.${name}`;
1252
+ if (emitted.has(edgeKey)) continue;
1253
+ emitted.add(edgeKey);
1254
+ relationships.push({
1255
+ source: `${componentName}.${name}`,
1256
+ target: `${base.name}.${name}`,
1257
+ type: ASTEngine.RelationshipType.Overrides,
1258
+ confidence: 0.95,
1259
+ ...sym.range?.startLine ? { line_number: this.offsetRange(sym.range, lineOffset, colOffset, indexOffset).startLine } : {},
1260
+ metadata: {
1261
+ baseOption: base.name,
1262
+ isMixin: base.isMixin,
1263
+ isOptionsApi: true,
1264
+ // Honesty contract : a sub-1.0 confidence carries a
1265
+ // machine-readable reason, not just a number.
1266
+ reasonCode: "verified-in-file"
1267
+ }
1268
+ });
1269
+ }
1270
+ }
1271
+ }
1272
+ }
1273
+ /**
1274
+ * ONTOLOGY : the options OBJECT a `mixins`/`extends` base names,
1275
+ * when this parse tree can read one, and `undefined` when it cannot.
1276
+ *
1277
+ * Two accepted shapes, both resolved WITHOUT leaving the tree:
1278
+ *
1279
+ * INLINE `mixins: [{ methods: { … } }]` / `extends: { … }` — the object
1280
+ * is the element (or the value) itself.
1281
+ * SAME-FILE `const shared = { … }` in the same `<script>` block, named by
1282
+ * the mixins array. Resolved by walking `variable_declarator`s
1283
+ * for the wanted name, which is exactly what `boundMacroEntries`
1284
+ * does for `defineProps(binding)` — the same rule, for the same
1285
+ * reason: a same-tree declaration is the only form whose keys are
1286
+ * published by nothing else, so reading them here cannot double
1287
+ * count another producer's symbols.
1288
+ *
1289
+ * Anything else returns `undefined` and the caller emits nothing. That is the
1290
+ * honest answer for an imported base: the analyzer has not read the base, so it
1291
+ * does not know what the base declares, and an `overrides` row needs that.
1292
+ */
1293
+ sameTreeOptionsObject(baseNode, value, scriptRoot) {
1294
+ if (baseNode.type === "object") return baseNode;
1295
+ if (value.type === "object" && baseNode.startIndex === value.startIndex) return value;
1296
+ if (baseNode.type !== "identifier" || !scriptRoot) return void 0;
1297
+ const wanted = baseNode.text;
1298
+ const stack = [scriptRoot];
1299
+ while (stack.length > 0) {
1300
+ const n = stack.pop();
1301
+ if (n.type === "variable_declarator") {
1302
+ const nameField = n.childForFieldName("name");
1303
+ const valueField = n.childForFieldName("value");
1304
+ if (nameField?.type === "identifier" && nameField.text === wanted && valueField) {
1305
+ const peeled = peelTypeWrappers(valueField);
1306
+ if (peeled.type === "object") return peeled;
1307
+ }
1308
+ }
1309
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
1310
+ const c = n.namedChild(i);
1311
+ if (c) stack.push(c);
1312
+ }
1313
+ }
1314
+ return void 0;
1315
+ }
1316
+ /**
1317
+ * ONTOLOGY : the member names an options
1318
+ * object declares — restricted to the options Vue's OWN MERGE TABLE REPLACES,
1319
+ * and to nothing else.
1320
+ *
1321
+ * WHY THE RESTRICTION IS THE WHOLE FIX. The first version of this read every
1322
+ * `pair` child of `MEMBER_OPTIONS` plus every `method_definition` child, on
1323
+ * the stated premise that "Vue's own merge rule is that a component's own
1324
+ * value for an option WINS over the base's, so a member declared here that the
1325
+ * base also declares is an override of that member". That premise is FALSE, and
1326
+ * Vue's own source says so in one table.
1327
+ *
1328
+ * vuejs@4ab865a, packages/runtime-core/src/componentOptions.ts:1005-1035,
1329
+ * `internalOptionMergeStrats`:
1330
+ *
1331
+ * methods, computed, components, directives -> mergeObjectOptions
1332
+ * `extend(Object.create(null), to, from)` — the CHILD'S value for a
1333
+ * same-named key wins outright. OVERRIDE
1334
+ * props, emits -> mergeEmitsOrPropsOptions
1335
+ * `extend({}, normalize(to), normalize(from ?? {}))` — same. OVERRIDE
1336
+ * inject -> mergeInject -> mergeObjectOptions OVERRIDE
1337
+ * data -> mergeDataFn
1338
+ * BOTH factories run and the results are `extend`ed, so the child's
1339
+ * wins PER RETURNED KEY while the `data` option itself survives as
1340
+ * two. Not a member override — but the colliding DATA KEYS are real.
1341
+ * beforeCreate … serverPrefetch (14 hooks) -> mergeAsArray
1342
+ * `[...new Set([].concat(to, from))]` — BOTH HOOKS RUN. NOT one.
1343
+ * Confirmed independently at apiLifecycle.ts:26-27:
1344
+ * `const hooks = target[type] || (target[type] = []); hooks.push(...)`.
1345
+ * watch -> mergeWatchOptions
1346
+ * per key the two are CONCATENATED into an array — both run. NOT one.
1347
+ * provide -> mergeDataFn, same as data. NOT one.
1348
+ *
1349
+ * So of the option kinds a real merge can collide on, four classes are a
1350
+ * replacement and four are a merge. The first version could not tell them
1351
+ * apart, and it emitted a row for all of them.
1352
+ *
1353
+ * WHAT THAT COST, MEASURED. Over the 1,151 admitted `.vue` files of
1354
+ * element-plus + vue-element-admin + vuejs there are 9 `extends`/`mixins`
1355
+ * heritage edges and 21 name collisions across them — and every one of the 21
1356
+ * is in the merge half: `mounted`, `beforeDestroy`, `activated`, `deactivated`
1357
+ * and the `data` factory. A cross-file resolution built on the old rule would
1358
+ * have emitted 21 fabricated `overrides` rows and 0 true ones
1359
+ * (`script/m14/vue-overrides-crossfile-probe.mts`; evidence in
1360
+ * `validation/m14/results/archive/vue-verify8-overrides-crossfile.json`). Under this
1361
+ * rule the same corpus supports 0, which is the correct answer and not a gap.
1362
+ *
1363
+ * A fabricated edge is the most expensive mistake available in this programme
1364
+ * (ONTOLOGY-WORKSTREAM, "The fabrication rule"): it counts as `unexplained`
1365
+ * and it poisons bb_graph, bb_impact and bb_migrate. Removing 21 false rows is
1366
+ * worth more than adding them.
1367
+ *
1368
+ * WHAT IS DELIBERATELY NOT IN THE SET, each exclusion a decision:
1369
+ * - lifecycle hooks, `data`, `setup`, `watch`: merged, per the table above.
1370
+ * - `components` / `directives`: merged per key like `methods`, so they COULD
1371
+ * collide — but this analyzer publishes no symbol for an asset
1372
+ * registration, so a collision could never be joined to an own member and
1373
+ * the rule could never fire. Adding it would be a branch that cannot fire,
1374
+ * which is the dead-code class this workstream exists to remove.
1375
+ */
1376
+ optionsMemberNames(optionsObject) {
1377
+ const out = /* @__PURE__ */ new Set();
1378
+ const MEMBER_OPTIONS = /* @__PURE__ */ new Set(["methods", "computed", "props", "emits", "inject"]);
1379
+ for (let i = 0; i < optionsObject.namedChildCount; i++) {
1380
+ const child = optionsObject.namedChild(i);
1381
+ if (!child) continue;
1382
+ if (child.type === "method_definition") continue;
1383
+ if (child.type !== "pair") continue;
1384
+ const key = child.childForFieldName("key")?.text;
1385
+ if (!key || key.startsWith("[")) continue;
1386
+ const k = child.childForFieldName("key").type === "string" ? unquote(key) : key;
1387
+ if (!MEMBER_OPTIONS.has(key)) continue;
1388
+ const v = child.childForFieldName("value");
1389
+ if (!v) {
1390
+ out.add(k);
1391
+ continue;
1392
+ }
1393
+ if (v.type === "object") {
1394
+ for (let j = 0; j < v.namedChildCount; j++) {
1395
+ const m = v.namedChild(j);
1396
+ if (!m || m.type !== "pair" && m.type !== "method_definition" && m.type !== "shorthand_property_identifier") continue;
1397
+ const mk = m.type === "pair" ? m.childForFieldName("key")?.text : m.type === "method_definition" ? m.childForFieldName("name")?.text : m.text;
1398
+ if (mk && !mk.startsWith("[")) out.add(unquote(mk));
1399
+ }
1400
+ } else if (v.type === "array") {
1401
+ for (let j = 0; j < v.namedChildCount; j++) {
1402
+ const m = v.namedChild(j);
1403
+ if (m?.type === "string") out.add(unquote(m.text));
1404
+ }
1405
+ } else {
1406
+ out.add(k);
1407
+ }
1408
+ }
1409
+ for (const dk of this.optionsDataKeys(optionsObject)) out.add(dk);
1410
+ return out;
1411
+ }
1412
+ /**
1413
+ * The keys a `data` option RETURNS — the one collision class `data` really does
1414
+ * replace, because `mergeDataFn` runs both factories and `extend`s the two
1415
+ * results with the child's last.
1416
+ *
1417
+ * Two shapes, both read off the options object and neither leaving the tree:
1418
+ * the object form (`data: { n: 0 }`) and the factory form
1419
+ * (`data() { return { n: 0 } }`), which is `findReturnedObject`'s job. An arrow
1420
+ * factory written as a `pair` value (`data: () => ({ … })`) goes through the
1421
+ * same call, because `findReturnedObject` handles an expression body.
1422
+ */
1423
+ optionsDataKeys(optionsObject) {
1424
+ const out = /* @__PURE__ */ new Set();
1425
+ const addFrom = (o) => {
1426
+ if (!o || o.type !== "object") return;
1427
+ for (let i = 0; i < o.namedChildCount; i++) {
1428
+ const p = o.namedChild(i);
1429
+ if (p?.type !== "pair") continue;
1430
+ const raw = p.childForFieldName("key")?.text;
1431
+ if (!raw || raw.startsWith("[")) continue;
1432
+ out.add(unquote(raw));
1433
+ }
1434
+ };
1435
+ for (let i = 0; i < optionsObject.namedChildCount; i++) {
1436
+ const child = optionsObject.namedChild(i);
1437
+ if (!child) continue;
1438
+ if (child.type === "method_definition") {
1439
+ if (child.childForFieldName("name")?.text !== "data") continue;
1440
+ addFrom(this.findReturnedObject(child));
1441
+ continue;
1442
+ }
1443
+ if (child.type !== "pair" || child.childForFieldName("key")?.text !== "data") continue;
1444
+ const v = child.childForFieldName("value");
1445
+ if (!v) continue;
1446
+ if (v.type === "object") {
1447
+ addFrom(v);
1448
+ continue;
1449
+ }
1450
+ if (v.type === "arrow_function" || v.type === "function_expression" || v.type === "function") {
1451
+ addFrom(this.findReturnedObject(v));
1452
+ }
1453
+ }
1454
+ return out;
1455
+ }
1456
+ /**
1457
+ * H1 (review wave): emit one Property symbol per key of a data-state
1458
+ * object (pair ranges, so hover lands on the key). String keys unquoted.
1459
+ */
1460
+ emitDataKeys(objectNode, componentName, symbols, lineOffset, colOffset, indexOffset) {
1461
+ const seen = /* @__PURE__ */ new Map();
1462
+ for (let i = 0; i < objectNode.namedChildCount; i++) {
1463
+ const child = objectNode.namedChild(i);
1464
+ if (!child || child.type !== "pair") continue;
1465
+ const keyNode = child.childForFieldName("key");
1466
+ if (!keyNode) continue;
1467
+ if (keyNode.text.startsWith("[")) continue;
1468
+ const name = keyNode.type === "string" ? unquote(keyNode.text) : keyNode.text;
1469
+ if (!name) continue;
1470
+ const prevIdx = seen.get(name);
1471
+ const sym = {
1472
+ name,
1473
+ kind: ASTEngine.SymbolKind.Property,
1474
+ range: this.offsetRange(this.nodeToRange(child), lineOffset, colOffset, indexOffset),
1475
+ scope: componentName,
1476
+ ownerName: componentName,
1477
+ ownerKind: ASTEngine.SymbolKind.Class,
1478
+ visibility: "public",
1479
+ metadata: { isDataKey: true }
1480
+ };
1481
+ if (prevIdx !== void 0) {
1482
+ symbols[prevIdx] = sym;
1483
+ } else {
1484
+ seen.set(name, symbols.length);
1485
+ symbols.push(sym);
1486
+ }
1487
+ }
1488
+ }
1489
+ /**
1490
+ * H1 (review wave): first `return {...}` object literal under a data()
1491
+ * factory body. Conditional/multiple returns: first wins (documented
1492
+ * approximation — branches are not evaluated). D2 (deeper review):
1493
+ * expression-bodied arrows (`() => ({...})`) carry the object as the
1494
+ * direct body — an object reached with only parenthesized wrappers
1495
+ * between it and an arrow/function start is the body's value, not a
1496
+ * nested construction.
1497
+ */
1498
+ findReturnedObject(methodNode) {
1499
+ const isStart = (p) => !!p && p.startIndex === methodNode.startIndex && p.endIndex === methodNode.endIndex;
1500
+ const stack = [methodNode];
1501
+ while (stack.length > 0) {
1502
+ const n = stack.pop();
1503
+ if (n.type === "return_statement") {
1504
+ for (let i = 0; i < n.namedChildCount; i++) {
1505
+ const c = n.namedChild(i);
1506
+ if (c && c.type === "object") return c;
1507
+ }
1508
+ }
1509
+ if (n.type === "object" && !isStart(n)) {
1510
+ let ok = true;
1511
+ let p = n.parent;
1512
+ while (p && !isStart(p)) {
1513
+ if (p.type !== "parenthesized_expression" && p.type !== "arrow_function" && p.type !== "function_expression" && p.type !== "function" && p.type !== "method_definition") {
1514
+ ok = false;
1515
+ break;
1516
+ }
1517
+ p = p.parent;
1518
+ }
1519
+ if (ok && p) return n;
1520
+ }
1521
+ if (!isStart(n) && (n.type === "function_expression" || n.type === "arrow_function" || n.type === "function_declaration" || n.type === "method_definition")) continue;
1522
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
1523
+ const c = n.namedChild(i);
1524
+ if (c) stack.push(c);
1525
+ }
1526
+ }
1527
+ return void 0;
1528
+ }
1529
+ emitObjectMembers(valueNode, componentName, symbols, kind, meta, lineOffset, colOffset, payloads, indexOffset) {
1530
+ const seen = /* @__PURE__ */ new Map();
1531
+ for (let i = 0; i < valueNode.namedChildCount; i++) {
1532
+ const child = valueNode.namedChild(i);
1533
+ if (!child) continue;
1534
+ let name;
1535
+ if (child.type === "method_definition" || child.type === "pair") {
1536
+ const raw = child.childForFieldName("name")?.text || child.childForFieldName("key")?.text;
1537
+ if (!raw) continue;
1538
+ if (raw.startsWith("[")) continue;
1539
+ name = unquote(raw);
1540
+ } else if (child.type === "property_identifier") {
1541
+ name = child.text;
1542
+ }
1543
+ if (!name) continue;
1544
+ const prevIdx = seen.get(name);
1545
+ const payload = payloads?.get(name);
1546
+ const sym = {
1547
+ name,
1548
+ kind,
1549
+ range: this.offsetRange(this.nodeToRange(child), lineOffset, colOffset, indexOffset),
1550
+ scope: componentName,
1551
+ ownerName: componentName,
1552
+ ownerKind: ASTEngine.SymbolKind.Class,
1553
+ visibility: "public",
1554
+ parameters: payload?.parameters,
1555
+ returnType: payload?.returnType,
1556
+ complexity: payload?.complexity,
1557
+ qualified_calls: payload?.qualified_calls,
1558
+ metadata: { [meta]: true }
1559
+ };
1560
+ if (prevIdx !== void 0) {
1561
+ symbols[prevIdx] = sym;
1562
+ } else {
1563
+ seen.set(name, symbols.length);
1564
+ symbols.push(sym);
1565
+ }
1566
+ }
1567
+ }
1568
+ /**
1569
+ * F12: a contract OPTION's entries, in the shape order —
1570
+ * inline literal, then a same-file BINDING, then the declaration EDGE.
1571
+ *
1572
+ * Shared by the Options `props:`/`emits:` options and (for the binding half)
1573
+ * the script-setup macros, because the two differ only in spelling: Vue
1574
+ * hoists the declaration to a `const` in both styles, and both styles lost
1575
+ * the whole contract when the name was not followed.
1576
+ */
1577
+ resolveContractEntries(value, scriptRoot, which, relationships, componentName) {
1578
+ const inline = this.propEntries(value ?? void 0);
1579
+ if (inline.length > 0) {
1580
+ this.lastContractSource = "inline";
1581
+ return inline;
1582
+ }
1583
+ if (!value || !scriptRoot) {
1584
+ this.lastContractSource = "none";
1585
+ return [];
1586
+ }
1587
+ const bound = this.boundMacroEntries(value, scriptRoot, which);
1588
+ if (bound.length > 0) {
1589
+ this.lastContractSource = "binding";
1590
+ return bound;
1591
+ }
1592
+ this.lastContractSource = "none";
1593
+ if (!relationships) return [];
1594
+ const declared = this.macroContractNameFromNode(value, which);
1595
+ if (!declared) return [];
1596
+ const marker = which === "props" ? "isPropContract" : "isEmitContract";
1597
+ relationships.push({
1598
+ source: componentName,
1599
+ target: declared.name,
1600
+ type: ASTEngine.RelationshipType.References,
1601
+ confidence: declared.resolved ? 1 : 0.8,
1602
+ ...declared.node.startPosition ? { line_number: declared.node.startPosition.row + 1 } : {},
1603
+ metadata: {
1604
+ [marker]: true,
1605
+ isOptionsApi: true,
1606
+ ...declared.module ? { contractModule: declared.module } : {},
1607
+ ...declared.resolved ? {} : { unresolved: true }
1608
+ }
1609
+ });
1610
+ return [];
1611
+ }
1612
+ /** The declaration a bare identifier argument names, without a call node. */
1613
+ macroContractNameFromNode(arg, _which) {
1614
+ if (arg.type !== "identifier") return void 0;
1615
+ const root = this.scriptRootOf(arg);
1616
+ return {
1617
+ name: arg.text,
1618
+ node: arg,
1619
+ module: root ? this.importModuleFor(arg.text, arg) : void 0,
1620
+ resolved: root ? this.hasTopLevelDeclaration(arg.text, arg) : false
1621
+ };
1622
+ }
1623
+ /**
1624
+ * The identifiers a heritage option names, in the three shapes Vue accepts:
1625
+ * a bare name (`extends: Base`), an array of them (`extends: [A, B]`), and a
1626
+ * string name — which Vue also honours for a globally-registered component.
1627
+ *
1628
+ * Only names are taken. An inline OBJECT (`extends: { methods: … }`) is a
1629
+ * merge rather than a reference, and its members are extracted by the caller;
1630
+ * naming the literal's own keys as base classes would fabricate an inheritance
1631
+ * to a property.
1632
+ */
1633
+ identifiersOf(node) {
1634
+ const out = [];
1635
+ if (node.type === "identifier") {
1636
+ out.push({ name: node.text, node });
1637
+ return out;
1638
+ }
1639
+ if (node.type === "string") {
1640
+ const v = unquote(node.text);
1641
+ if (v) out.push({ name: v, node });
1642
+ return out;
1643
+ }
1644
+ if (node.type === "array") {
1645
+ for (let i = 0; i < node.namedChildCount; i++) {
1646
+ const c = node.namedChild(i);
1647
+ if (!c) continue;
1648
+ if (c.type === "identifier") out.push({ name: c.text, node: c });
1649
+ else if (c.type === "string") {
1650
+ const v = unquote(c.text);
1651
+ if (v) out.push({ name: v, node: c });
1652
+ }
1653
+ }
1654
+ return out;
1655
+ }
1656
+ return out;
1657
+ }
1658
+ /**
1659
+ * F12: a same-file BINDING as the macro's argument —
1660
+ * `defineProps(boundProps)` / `defineEmits(boundEmits)`.
1661
+ *
1662
+ * The name is an `identifier`; this resolves it to its `variable_declarator`
1663
+ * in the SAME script tree and reads the literal it holds, which is the only
1664
+ * form whose keys are published by nothing else — so component-owned symbols
1665
+ * are safe here and match what the inline runtime form already emits. A
1666
+ * binding that does not resolve (imported from a sibling module) returns
1667
+ * nothing and the caller emits the contract edge instead; this walk never
1668
+ * leaves the tree, so it costs no I/O and cannot depend on the filesystem.
1669
+ */
1670
+ boundMacroEntries(arg, scriptRoot, which) {
1671
+ if (!arg) return [];
1672
+ const nameNode = arg.type === "identifier" ? arg : this.findChildByType(arg, "identifier");
1673
+ if (!nameNode) return [];
1674
+ const wanted = nameNode.text;
1675
+ const stack = [scriptRoot];
1676
+ while (stack.length > 0) {
1677
+ const n = stack.pop();
1678
+ if (n.type === "variable_declarator") {
1679
+ const nameField = n.childForFieldName("name");
1680
+ const valueField = n.childForFieldName("value");
1681
+ if (nameField?.type === "identifier" && nameField.text === wanted && valueField) {
1682
+ return this.propEntries(peelTypeWrappers(valueField));
1683
+ }
1684
+ }
1685
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
1686
+ const c = n.namedChild(i);
1687
+ if (c) stack.push(c);
1688
+ }
1689
+ }
1690
+ void which;
1691
+ return [];
1692
+ }
1693
+ /**
1694
+ * F12: the DECLARATION a macro names, when it is not an inline literal —
1695
+ * `defineProps<AffixProps>()` (a `type_identifier` in `type_arguments`) or
1696
+ * `defineProps(affixProps)` (a `binding` identifier). Returns the name plus
1697
+ * the node that writes it, and whether the declaration is reachable in THIS
1698
+ * tree (`resolved`) — which selects the edge's confidence, since a
1699
+ * same-file declaration is a certainty and an imported one is the import's
1700
+ * word for it.
1701
+ */
1702
+ macroContractName(callNode, arg, _which) {
1703
+ if (arg) {
1704
+ let inner = arg;
1705
+ for (let i = 0; i < 8 && inner; i++) {
1706
+ if (inner.type === "identifier") break;
1707
+ if (inner.type === "as_expression" || inner.type === "satisfies_expression" || inner.type === "parenthesized_expression" || inner.type === "non_null_expression") {
1708
+ const kids = inner.namedChildren;
1709
+ inner = kids.length > 0 ? kids[0] ?? null : null;
1710
+ continue;
1711
+ }
1712
+ break;
1713
+ }
1714
+ if (inner?.type === "identifier") {
1715
+ const resolved = this.hasTopLevelDeclaration(inner.text, callNode);
1716
+ return { name: inner.text, node: inner, module: this.importModuleFor(inner.text, callNode), resolved };
1717
+ }
1718
+ }
1719
+ return void 0;
1720
+ }
1721
+ /** Is `name` declared at this tree's top level (an interface or a type alias)? */
1722
+ hasTopLevelDeclaration(name, callNode) {
1723
+ const root = callNode.parent ? this.scriptRootOf(callNode) : null;
1724
+ const scope = root ?? callNode;
1725
+ const stack = [];
1726
+ for (let i = 0; i < scope.namedChildCount; i++) {
1727
+ const c = scope.namedChild(i);
1728
+ if (c) stack.push(c);
1729
+ }
1730
+ while (stack.length > 0) {
1731
+ const n = stack.pop();
1732
+ if ((n.type === "interface_declaration" || n.type === "type_alias_declaration") && n.childForFieldName("name")?.text === name) {
1733
+ return true;
1734
+ }
1735
+ }
1736
+ return false;
1737
+ }
1738
+ /** The `program` node this call sits in, by climbing parents (web-tree-sitter
1739
+ * hands back fresh wrappers, so identity is never compared by reference). */
1740
+ /** The first call expression in `root` whose callee is `name` (F16). */
1741
+ findCallNamed(root, name) {
1742
+ const stack = [root];
1743
+ while (stack.length > 0) {
1744
+ const n = stack.pop();
1745
+ if (n.type === "call_expression") {
1746
+ const fn = n.childForFieldName("function");
1747
+ if (fn?.text === name) return n;
1748
+ }
1749
+ for (let i = 0; i < n.namedChildCount; i++) {
1750
+ const c = n.namedChild(i);
1751
+ if (c) stack.push(c);
1752
+ }
1753
+ }
1754
+ return null;
1755
+ }
1756
+ /**
1757
+ * F32 . The component name a `name:` option declares, resolved one
1758
+ * step further than a string literal.
1759
+ *
1760
+ * WHY. F17 ruled that a non-literal `name:` keeps the file stem and
1761
+ * records `declaredNameUnreadable`, because the value is "an IMPORTED IDENTIFIER,
1762
+ * whose value lives in another module". That ruling is right about an import and
1763
+ * wrong about the shape this corpus is full of — element-plus declares almost
1764
+ * every component's name through a LOCAL constant:
1765
+ *
1766
+ * const COMPONENT_NAME = 'ElInput' // input.vue
1767
+ * defineOptions({ name: COMPONENT_NAME })
1768
+ *
1769
+ * Measured on the 1,162-file draw before this: 724 unresolved component-usage
1770
+ * `instantiates` edges, of which **253 (34.9%) named a component whose own name is
1771
+ * assigned as a string literal in the DECLARING file** — `ElInput` 59 edges,
1772
+ * `ElSelect` 42, `ElSwitch` 33, `ElForm` 20, 25 distinct names — while the lane
1773
+ * published the stem (`input`, `select`) and no reader could bind the tag.
1774
+ *
1775
+ * WHAT IT READS, and the ORDER is the contract: an IMPORTED identifier is refused
1776
+ * FIRST (F17's case — the value really is in another module, and this lane does not
1777
+ * do cross-file resolution), then a TOP-LEVEL `const`/`let`/`var NAME = '<literal>'`
1778
+ * in the SAME program is read. Top-level only, for the same reason
1779
+ * `importModuleFor` and `hasTopLevelDeclaration` are: a declarator nested inside a
1780
+ * function body is not a binding the options object can see, so `program`'s direct
1781
+ * children are the complete search space.
1782
+ *
1783
+ * A `name:` that is neither a literal nor a readable local const still returns
1784
+ * undefined, so the caller keeps F17's stem fallback and its marker.
1785
+ */
1786
+ declaredNameValue(valueNode, scriptRoot) {
1787
+ if (!valueNode) return void 0;
1788
+ if (valueNode.type === "string") return { name: unquote(valueNode.text) };
1789
+ if (valueNode.type !== "identifier") return void 0;
1790
+ const name = valueNode.text;
1791
+ if (this.importModuleFor(name, valueNode) !== void 0) return void 0;
1792
+ for (let i = 0; i < scriptRoot.namedChildCount; i++) {
1793
+ const stmt = scriptRoot.namedChild(i);
1794
+ if (!stmt) continue;
1795
+ if (stmt.type !== "lexical_declaration" && stmt.type !== "variable_declaration") continue;
1796
+ for (let j = 0; j < stmt.namedChildCount; j++) {
1797
+ const d = stmt.namedChild(j);
1798
+ if (!d || d.type !== "variable_declarator") continue;
1799
+ if (d.childForFieldName("name")?.text !== name) continue;
1800
+ const value = d.childForFieldName("value");
1801
+ if (value?.type === "string") return { name: unquote(value.text), constName: name };
1802
+ }
1803
+ }
1804
+ return void 0;
1805
+ }
1806
+ /** The first object-typed argument of a call, if any (F16). */
1807
+ firstObjectArgument(call) {
1808
+ const args = call.childForFieldName("arguments");
1809
+ if (!args) return null;
1810
+ for (let i = 0; i < args.namedChildCount; i++) {
1811
+ const c = args.namedChild(i);
1812
+ if (c?.type === "object") return c;
1813
+ }
1814
+ return null;
1815
+ }
1816
+ scriptRootOf(node) {
1817
+ let cur = node.parent;
1818
+ let last = node;
1819
+ while (cur) {
1820
+ if (cur.type === "program") return cur;
1821
+ last = cur;
1822
+ cur = cur.parent;
1823
+ }
1824
+ return last.parent ? null : last;
1825
+ }
1826
+ /** The module specifier an identifier was imported from, if any. */
1827
+ importModuleFor(name, callNode) {
1828
+ const root = this.scriptRootOf(callNode);
1829
+ if (!root) return void 0;
1830
+ const stack = [];
1831
+ for (let i = 0; i < root.namedChildCount; i++) {
1832
+ const c = root.namedChild(i);
1833
+ if (c) stack.push(c);
1834
+ }
1835
+ while (stack.length > 0) {
1836
+ const n = stack.pop();
1837
+ if (n.type === "import_statement") {
1838
+ const src = this.findChildByType(n, "string");
1839
+ const clause = n.namedChildren.find((c) => c.type === "import_clause");
1840
+ for (const c of clause?.namedChildren ?? []) {
1841
+ if (!c) continue;
1842
+ if (c.type === "named_imports") {
1843
+ for (const sp of c.namedChildren) {
1844
+ if (!sp) continue;
1845
+ const alias = sp.childForFieldName("alias")?.text;
1846
+ const imp = sp.childForFieldName("name")?.text;
1847
+ if ((alias ?? imp) === name) return src ? unquote(src.text) : void 0;
1848
+ }
1849
+ } else if (c.type === "identifier" && c.text === name) {
1850
+ return src ? unquote(src.text) : void 0;
1851
+ }
1852
+ }
1853
+ }
1854
+ }
1855
+ return void 0;
1856
+ }
1857
+ /**
1858
+ * F21 . Does `name` at this call site still denote Vue's
1859
+ * `provide`/`inject`? Ordered narrowest-first, and the ORDER is the contract:
1860
+ * a decorator is a syntactic position, so it decides before any name lookup; a
1861
+ * bound name is decided by its module; only an UNBOUND name reaches the local
1862
+ * declaration test, where a top-level declaration of the same name is the
1863
+ * shadow. A nested declaration deliberately does NOT count: it does not shadow
1864
+ * a call in a sibling scope, and treating it as one would suppress genuine
1865
+ * contract edges in real components — the failure mode a blanket name filter
1866
+ * has anyway.
1867
+ */
1868
+ contractNameIsVueBinding(name, callNode) {
1869
+ if (this.isInsideDecorator(callNode)) return false;
1870
+ const module = this.importModuleFor(name, callNode);
1871
+ if (module !== void 0) return isVueModuleSpecifier(module);
1872
+ return !this.hasProgramBinding(name, callNode);
1873
+ }
1874
+ /** Is this call written inside a decorator (`@inject("db")`)? */
1875
+ isInsideDecorator(node) {
1876
+ let cur = node.parent;
1877
+ while (cur) {
1878
+ if (cur.type === "decorator") return true;
1879
+ cur = cur.parent;
1880
+ }
1881
+ return false;
1882
+ }
1883
+ /**
1884
+ * Is `name` declared as a VALUE binding among this program's top-level
1885
+ * statements (`function inject`, `class inject`, `const/let/var inject`, and a
1886
+ * destructuring declaration that binds it)? Short-circuits on the same
1887
+ * reasoning as `importModuleFor`: only top-level statements can shadow a
1888
+ * program-wide global, and `program`'s direct children are their whole search
1889
+ * space.
1890
+ */
1891
+ hasProgramBinding(name, callNode) {
1892
+ const root = this.scriptRootOf(callNode);
1893
+ if (!root) return false;
1894
+ for (let i = 0; i < root.namedChildCount; i++) {
1895
+ const n = root.namedChild(i);
1896
+ if (!n) continue;
1897
+ if (n.type === "function_declaration" || n.type === "generator_function_declaration" || n.type === "class_declaration") {
1898
+ if (n.childForFieldName("name")?.text === name) return true;
1899
+ } else if (n.type === "lexical_declaration" || n.type === "variable_declaration") {
1900
+ for (let j = 0; j < n.namedChildCount; j++) {
1901
+ const d = n.namedChild(j);
1902
+ if (d?.type !== "variable_declarator") continue;
1903
+ const bound = d.childForFieldName("name");
1904
+ if (!bound) continue;
1905
+ if (bound.type === "identifier" ? bound.text === name : this.patternBindsName(bound, name)) return true;
1906
+ }
1907
+ }
1908
+ }
1909
+ return false;
1910
+ }
1911
+ /** Does a destructuring PATTERN bind `name`? (an object/array pattern's own
1912
+ * identifier leaves, including a shorthand `{ inject }`.) */
1913
+ patternBindsName(pattern, name) {
1914
+ const stack = [pattern];
1915
+ while (stack.length > 0) {
1916
+ const n = stack.pop();
1917
+ if ((n.type === "identifier" || n.type === "shorthand_property_identifier_pattern") && n.text === name) {
1918
+ return true;
1919
+ }
1920
+ for (let i = 0; i < n.namedChildCount; i++) {
1921
+ const c = n.namedChild(i);
1922
+ if (c) stack.push(c);
1923
+ }
1924
+ }
1925
+ return false;
1926
+ }
1927
+ propNames(node) {
1928
+ return this.propEntries(node).map((e) => e.name);
1929
+ }
1930
+ /**
1931
+ * The prop/emit names WITH the node that writes each one (#825).
1932
+ *
1933
+ * The string-only walk was enough while nothing asked WHERE a prop is
1934
+ * declared, and the caller then stamped every prop with the `props: { … }`
1935
+ * PAIR's range — one line for all of them. The oracle made that visible the
1936
+ * moment `contains` edges carried lines: Account.vue claimed `user` at line 17
1937
+ * (`props: {`) while `user` is written at 18 (the same for UserCard.vue:60 and
1938
+ * TabPane.vue:70's `type`). A prop's declaration line is the line its own key
1939
+ * is written on, which only this walk can say.
1940
+ */
1941
+ /**
1942
+ * F3 (vue master review 2026-09-30): the DEFAULTS object of a
1943
+ * `withDefaults(defineProps<T>(), { … })` call.
1944
+ *
1945
+ * The macro handler visits the inner `defineProps` call, so the defaults sit
1946
+ * one frame UP the parent chain — that frame is what was never looked at, and
1947
+ * it is the whole reason 98 corpus files lost their declared defaults. Climbs
1948
+ * from the macro call to the nearest enclosing `withDefaults` and returns its
1949
+ * SECOND argument, or null when there is no wrapper (the plain `defineProps`
1950
+ * form, which legitimately has no defaults). The hop bound guards against a
1951
+ * pathological tree; `scriptRootOf` above climbs the same way without one.
1952
+ */
1953
+ withDefaultsObject(macroCall) {
1954
+ let n = macroCall.parent;
1955
+ let hops = 0;
1956
+ while (n && hops < 8) {
1957
+ if (n.type === "call_expression") {
1958
+ const fn = n.childForFieldName("function");
1959
+ if (fn && fn.text === "withDefaults") return n.childForFieldName("arguments")?.namedChild(1) ?? null;
1960
+ }
1961
+ n = n.parent;
1962
+ hops++;
1963
+ }
1964
+ return null;
1965
+ }
1966
+ /**
1967
+ * F3: key → raw default-value TEXT of a `withDefaults` defaults object.
1968
+ *
1969
+ * The value is kept as WRITTEN SOURCE, not evaluated: a default may be any
1970
+ * expression (`0`, `'hi'`, `() => []`, `defaultSize`) and this lane has no
1971
+ * evaluator, so pretending otherwise would be a worse lie than storing the
1972
+ * text. A BRACKET-LED computed key is skipped under the same F6 ruling — its
1973
+ * "default" is a variable reference, not a static default.
1974
+ */
1975
+ defaultsEntries(objects) {
1976
+ const out = /* @__PURE__ */ new Map();
1977
+ if (!objects || objects.type !== "object") return out;
1978
+ for (let i = 0; i < objects.namedChildCount; i++) {
1979
+ const c = objects.namedChild(i);
1980
+ if (!c || c.type !== "pair") continue;
1981
+ const keyNode = c.childForFieldName("key");
1982
+ const raw = keyNode?.text;
1983
+ if (!raw || raw.startsWith("[")) continue;
1984
+ const value = c.childForFieldName("value");
1985
+ if (!value) continue;
1986
+ const k = keyNode.type === "string" ? unquote(raw) : raw;
1987
+ if (k) out.set(k, value.text);
1988
+ }
1989
+ return out;
1990
+ }
1991
+ propEntries(node) {
1992
+ if (!node) return [];
1993
+ const out = [];
1994
+ if (node.type === "array") {
1995
+ for (let i = 0; i < node.namedChildCount; i++) {
1996
+ const c = node.namedChild(i);
1997
+ if (c && c.type === "string") {
1998
+ const v = unquote(c.text);
1999
+ if (v) out.push({ name: v, node: c });
2000
+ }
2001
+ }
2002
+ } else if (node.type === "object") {
2003
+ for (let i = 0; i < node.namedChildCount; i++) {
2004
+ const c = node.namedChild(i);
2005
+ if (c && c.type === "pair") {
2006
+ const keyNode = c.childForFieldName("key");
2007
+ const key = keyNode?.text;
2008
+ if (key && !key.startsWith("[")) out.push({ name: key, node: keyNode ?? c });
2009
+ }
2010
+ }
2011
+ }
2012
+ return out;
2013
+ }
2014
+ /**
2015
+ * F13 (full 829-file corpus draw, 2026-09-30): the IDENTIFIER elements of a
2016
+ * `defineProps([ … ])` / `defineEmits([ … ])` array.
2017
+ *
2018
+ * `propEntries` accepts string elements only, which is right for its callers —
2019
+ * an identifier is not an event NAME and must not become a symbol (that would
2020
+ * be the F6 phantom class again: `CHANGE_EVENT` is a declaration, and its
2021
+ * value is the event). But dropping it was worse than either: measured, 13
2022
+ * corpus files lost a member of their event set outright, and
2023
+ * time-select.vue's `defineEmits([CHANGE_EVENT, 'blur', 'focus', 'clear',
2024
+ * UPDATE_MODEL_EVENT])` published three events where the component emits five.
2025
+ *
2026
+ * Those constants are imported, so resolving their VALUES needs cross-file
2027
+ * constant folding this lane does not do. The DECLARATION is nameable right
2028
+ * here, and F12 already established the rule for exactly this situation in
2029
+ * `defineProps(binding)`: a named declaration becomes a contract EDGE, never a
2030
+ * copy of its members. This is a separate accessor rather than a flag on
2031
+ * `propEntries` so that the Options-API path, which uses the same helper for
2032
+ * `emits: [ … ]`, keeps ignoring identifiers rather than inheriting a
2033
+ * declaration edge it never asked for.
2034
+ */
2035
+ propDeclarationEntries(node) {
2036
+ if (!node || node.type !== "array") return [];
2037
+ const out = [];
2038
+ for (let i = 0; i < node.namedChildCount; i++) {
2039
+ const c = node.namedChild(i);
2040
+ if (c && c.type === "identifier") out.push({ name: c.text, node: c });
2041
+ }
2042
+ return out;
2043
+ }
2044
+ /**
2045
+ * V1 (#419): names from the TYPE-LITERAL generic form of the script-setup
2046
+ * compiler macros — defineProps<{ id: string }>() and
2047
+ * defineEmits<{ (e: 'save'): void }>() — where the public contract lives in
2048
+ * the type_arguments node, not a runtime argument. Probe-verified against
2049
+ * tree-sitter-typescript: props = property_identifier under object_type;
2050
+ * emits = the first string literal of each call_signature's formal
2051
+ * parameters.
2052
+ */
2053
+ genericMacroNames(callNode, which) {
2054
+ return this.genericMacroEntries(callNode, which).map((e) => e.name);
2055
+ }
2056
+ /**
2057
+ * (#825) follow-up: genericMacroNames carrying the name node, so macro
2058
+ * symbols range at their own key lines (same contract as the runtime form).
2059
+ */
2060
+ /**
2061
+ * F14 (full 829-file corpus draw, 2026-09-30): the shape of a macro's TYPE
2062
+ * ARGUMENT, at the level of the contract, with one rule — descend through
2063
+ * intersection / union constituents and through type wrappers, read a generic's
2064
+ * OWN name, and STOP at an object literal.
2065
+ *
2066
+ * The old code had no shape at all: `findChildByType(typeArgs, "object_type")`
2067
+ * reads DIRECT children only, so `defineProps<{ … } & Pick<X, 'a'>>()` — an
2068
+ * intersection — matched nothing and published NO props (measured:
2069
+ * cascader-panel/src/menu.vue, prop symbols `[]`), while a separate
2070
+ * blind-descendant search for the first `type_identifier` reached INTO the
2071
+ * object literal's property signatures and named a prop's TYPE as the
2072
+ * contract. Both halves were visible only at the 829-file draw, and in
2073
+ * opposite directions: node.vue's plain literal `defineProps<{ node: NodeShape;
2074
+ * menuId?: string }>()` should name nothing, yet the oracle derived
2075
+ * `NodeShape` for it — a MISSING that no sampled run had ever seen.
2076
+ *
2077
+ * `CascaderNode[]` inside a property signature is still inside one, so the
2078
+ * array case that made the old pre-order rule look reasonable is covered by
2079
+ * the same stop.
2080
+ */
2081
+ contractTypeShape(typeArgs) {
2082
+ const objectTypes = [];
2083
+ const participants = [];
2084
+ const seenParticipants = /* @__PURE__ */ new Set();
2085
+ const addParticipant = (n) => {
2086
+ if (!n || !n.text) return;
2087
+ if (seenParticipants.has(n.text)) return;
2088
+ seenParticipants.add(n.text);
2089
+ participants.push({ name: n.text, node: n });
2090
+ };
2091
+ const walk = (node, depth) => {
2092
+ if (!node || depth > 8) return;
2093
+ switch (node.type) {
2094
+ case "intersection_type":
2095
+ case "union_type":
2096
+ for (let i = 0; i < node.namedChildCount; i++) walk(node.namedChild(i), depth + 1);
2097
+ return;
2098
+ case "parenthesized_type":
2099
+ case "as_expression":
2100
+ case "satisfies_expression":
2101
+ case "non_null_expression": {
2102
+ const kids = node.namedChildren;
2103
+ walk(kids.length ? kids[0] : null, depth + 1);
2104
+ return;
2105
+ }
2106
+ case "object_type":
2107
+ objectTypes.push(node);
2108
+ return;
2109
+ case "generic_type": {
2110
+ const kids = node.namedChildren;
2111
+ const nameNode = kids.length > 0 ? kids[0] : null;
2112
+ if (nameNode && (nameNode.type === "type_identifier" || nameNode.type === "nested_type_identifier")) {
2113
+ addParticipant(nameNode);
2114
+ }
2115
+ return;
2116
+ }
2117
+ case "type_identifier":
2118
+ addParticipant(node);
2119
+ return;
2120
+ default:
2121
+ return;
2122
+ }
2123
+ };
2124
+ if (typeArgs) for (let i = 0; i < typeArgs.namedChildCount; i++) walk(typeArgs.namedChild(i), 0);
2125
+ return { objectTypes, participants };
2126
+ }
2127
+ genericMacroEntries(callNode, which) {
2128
+ const out = [];
2129
+ const typeArgs = this.findChildByType(callNode, "type_arguments");
2130
+ if (!typeArgs) return out;
2131
+ const objectTypes = this.contractTypeShape(typeArgs).objectTypes;
2132
+ for (const objectType of objectTypes) for (let i = 0; i < objectType.namedChildCount; i++) {
2133
+ const member = objectType.namedChild(i);
2134
+ if (!member) continue;
2135
+ if (which === "props") {
2136
+ if (member.type === "property_signature") {
2137
+ const pid = this.findChildByType(member, "property_identifier");
2138
+ if (pid?.text) out.push({ name: pid.text, node: pid });
2139
+ } else if (member.type === "index_signature") {
2140
+ }
2141
+ } else {
2142
+ if (member.type === "call_signature") {
2143
+ const fp = this.findChildByType(member, "formal_parameters");
2144
+ if (!fp) continue;
2145
+ const seen = /* @__PURE__ */ new Set();
2146
+ for (let j = 0; j < fp.namedChildCount; j++) {
2147
+ const p = fp.namedChild(j);
2148
+ if (!p) continue;
2149
+ this.collectUnionStringNodes(p, seen, out);
2150
+ }
2151
+ }
2152
+ }
2153
+ }
2154
+ return out;
2155
+ }
2156
+ /** collectUnionStrings carrying the literal node (generic-emits key lines). */
2157
+ collectUnionStringNodes(root, seen, out) {
2158
+ const stack = [root];
2159
+ while (stack.length > 0) {
2160
+ const n = stack.pop();
2161
+ if (n.type === "string") {
2162
+ const v = unquote(n.text);
2163
+ if (v && !seen.has(v)) {
2164
+ seen.add(v);
2165
+ out.push({ name: v, node: n });
2166
+ }
2167
+ continue;
2168
+ }
2169
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
2170
+ const c = n.namedChild(i);
2171
+ if (c) stack.push(c);
2172
+ }
2173
+ }
2174
+ }
2175
+ findDescendantType(root, type) {
2176
+ const stack = [root];
2177
+ while (stack.length > 0) {
2178
+ const n = stack.pop();
2179
+ if (n.type === type) return n;
2180
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
2181
+ const c = n.namedChild(i);
2182
+ if (c) stack.push(c);
2183
+ }
2184
+ }
2185
+ return null;
2186
+ }
2187
+ /**
2188
+ * V1b (#419): collect every string-literal event name under a parameter's
2189
+ * type — the type_annotation may be a literal_type ('save') or a union_type
2190
+ * ('save' | 'close'); both declare event names.
2191
+ */
2192
+ collectUnionStrings(root, out) {
2193
+ const stack = [root];
2194
+ while (stack.length > 0) {
2195
+ const n = stack.pop();
2196
+ if (n.type === "string") {
2197
+ const v = unquote(n.text);
2198
+ if (v) out.add(v);
2199
+ continue;
2200
+ }
2201
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
2202
+ const c = n.namedChild(i);
2203
+ if (c) stack.push(c);
2204
+ }
2205
+ }
2206
+ }
2207
+ /**
2208
+ * V2 (#419) + M1 (review wave) + D1 (deeper review): extract simple
2209
+ * identifier tokens from a template expression's raw text ({{ count }},
2210
+ * :label="label", @click="onSave", v-model="count", v-if="show"). The vue
2211
+ * grammar keeps these as opaque text — no child AST — so tokenization is
2212
+ * by character walk (no regex). Only chain ROOTS are uses: in `item.id`,
2213
+ * `id` is a read of the loop variable, never of a same-named member —
2214
+ * emitting it fabricated member edges (family receiver-only rule). An
2215
+ * identifier preceded (modulo whitespace) by `.`, `?.` or `#` is skipped.
2216
+ * String/template-literal CONTENTS are not identifiers either:
2217
+ * `@click="go('save')"` must not read 'save' as a member. Single/double
2218
+ * quotes skip to their closer (backslash escapes honored); backticks
2219
+ * additionally re-enter code inside ${...} (nesting tracked).
2220
+ *
2221
+ * V4 (#419): identifiers bound by a template-local construct (v-for loop
2222
+ * var, slot prop) in the current scope are skipped — they shadow any
2223
+ * same-named member.
2224
+ */
2225
+ /** Counts the line breaks before `end` — how many lines into the fragment a
2226
+ * match sits. (#825): a template expression's identifier has a REAL line
2227
+ * (the line of the text fragment it is written in, plus its own offset within
2228
+ * that fragment when the fragment wraps), and the collector is the only place
2229
+ * that still knows it — a `Set<string>` throws that away, which is why vue's
2230
+ * template `uses` edges carried no line at all.
2231
+ */
2232
+ static linesBefore(text, end) {
2233
+ let n = 0;
2234
+ for (let i = 0; i < end && i < text.length; i++) if (text[i] === "\n") n++;
2235
+ return n;
2236
+ }
2237
+ collectIdentifiers(text, out, shadowed, baseLine) {
2238
+ const isIdentStart = (ch) => ch >= "A" && ch <= "Z" || ch >= "a" && ch <= "z" || ch === "_" || ch === "$";
2239
+ const isIdentPart = (ch) => isIdentStart(ch) || ch >= "0" && ch <= "9";
2240
+ const isSpace = (ch) => ch === " " || ch === " " || ch === "\n" || ch === "\r";
2241
+ const stack = [];
2242
+ let braceDepth = 0;
2243
+ const pendingTernary = /* @__PURE__ */ new Set();
2244
+ const isKeyPosition = (start) => {
2245
+ let k = start;
2246
+ while (k < text.length && isIdentPart(text[k])) k++;
2247
+ while (k < text.length && isSpace(text[k])) k++;
2248
+ if (text[k] !== ":") return false;
2249
+ if (text[k + 1] === ":") return false;
2250
+ if (pendingTernary.has(braceDepth)) {
2251
+ pendingTernary.delete(braceDepth);
2252
+ return false;
2253
+ }
2254
+ return true;
2255
+ };
2256
+ const emit = (word, start) => {
2257
+ let k = start - 1;
2258
+ while (k >= 0 && isSpace(text[k])) k--;
2259
+ const prev = k >= 0 ? text[k] : "";
2260
+ const prev2 = k - 1 >= 0 ? text[k - 1] : "";
2261
+ const chained = prev === "." || prev2 === "?" && prev === "." || prev === "#";
2262
+ if (!chained && !shadowed?.has(word)) out.set(word, baseLine + _RealVueAnalyzer.linesBefore(text, start));
2263
+ };
2264
+ let i = 0;
2265
+ while (i < text.length) {
2266
+ const ch = text[i];
2267
+ const top = stack.length > 0 ? stack[stack.length - 1] : "";
2268
+ if (top === "'" || top === '"') {
2269
+ if (ch === "\\") {
2270
+ i += 2;
2271
+ continue;
2272
+ }
2273
+ if (ch === top) stack.pop();
2274
+ i++;
2275
+ continue;
2276
+ }
2277
+ if (top === "`") {
2278
+ if (ch === "\\") {
2279
+ i += 2;
2280
+ continue;
2281
+ }
2282
+ if (ch === "$" && text[i + 1] === "{") {
2283
+ stack.push("${");
2284
+ braceDepth++;
2285
+ pendingTernary.delete(braceDepth);
2286
+ i += 2;
2287
+ continue;
2288
+ }
2289
+ if (ch === "`") {
2290
+ stack.pop();
2291
+ i++;
2292
+ continue;
2293
+ }
2294
+ i++;
2295
+ continue;
2296
+ }
2297
+ if (ch === "'" || ch === '"' || ch === "`") {
2298
+ stack.push(ch);
2299
+ i++;
2300
+ continue;
2301
+ }
2302
+ if (ch === "{") {
2303
+ stack.push(ch);
2304
+ braceDepth++;
2305
+ pendingTernary.delete(braceDepth);
2306
+ i++;
2307
+ continue;
2308
+ }
2309
+ if (ch === "}") {
2310
+ if (top === "{" || top === "${") {
2311
+ stack.pop();
2312
+ braceDepth--;
2313
+ pendingTernary.delete(braceDepth);
2314
+ }
2315
+ i++;
2316
+ continue;
2317
+ }
2318
+ if (ch === "?") {
2319
+ if (text[i + 1] !== "." && text[i + 1] !== "?") pendingTernary.add(braceDepth);
2320
+ i++;
2321
+ continue;
2322
+ }
2323
+ if (ch === ":") {
2324
+ if (pendingTernary.has(braceDepth)) pendingTernary.delete(braceDepth);
2325
+ i++;
2326
+ continue;
2327
+ }
2328
+ if (!isIdentStart(ch)) {
2329
+ i++;
2330
+ continue;
2331
+ }
2332
+ let j = i + 1;
2333
+ while (j < text.length && isIdentPart(text[j])) j++;
2334
+ if (!isKeyPosition(i)) emit(text.slice(i, j), i);
2335
+ i = j;
2336
+ }
2337
+ }
2338
+ /**
2339
+ * V4 (#419): template-local bindings declared on an element's start tag.
2340
+ * (a) v-for="item in items" / v-for="(item, i) in items" — the loop
2341
+ * variable(s) are bound locally;
2342
+ * (b) slot props #default="{ row }" / v-slot="{ row, col }" — destructured
2343
+ * names are bound locally.
2344
+ */
2345
+ collectNodeShadowBindings(startTag, shadowed) {
2346
+ for (let i = 0; i < startTag.namedChildCount; i++) {
2347
+ const c = startTag.namedChild(i);
2348
+ if (!c || c.type !== "directive_attribute") continue;
2349
+ const dname = this.findChildByType(c, "directive_name")?.text;
2350
+ const value = this.findChildByType(c, "quoted_attribute_value");
2351
+ const attrVal = value ? this.findChildByType(value, "attribute_value") : null;
2352
+ if (!attrVal) continue;
2353
+ if (dname === "v-for") {
2354
+ this.collectVForVars(attrVal.text, shadowed);
2355
+ } else if (dname === "#" || dname === "v-slot") {
2356
+ this.collectIdentifierNames(attrVal.text, shadowed, attrVal.startPosition.row);
2357
+ }
2358
+ }
2359
+ }
2360
+ /**
2361
+ * V4 (#419): the loop-variable part of v-for="item in items" (before
2362
+ * in/of) is bound locally; only the collection part is a member use.
2363
+ */
2364
+ collectVForUsage(text, out, shadowed, baseLine) {
2365
+ const m = this.parseVForDirective(text);
2366
+ const collection = m && m.collectionStart >= 0 ? m.collection : text;
2367
+ const start = m && m.collectionStart >= 0 ? m.collectionStart : 0;
2368
+ this.collectIdentifiers(collection, out, shadowed, baseLine + _RealVueAnalyzer.linesBefore(text, start));
2369
+ }
2370
+ /**
2371
+ * V4 (#419): the loop variables themselves (item, or item + i in the
2372
+ * parenthesized form) are bound locally.
2373
+ */
2374
+ collectVForVars(text, shadowed) {
2375
+ const m = this.parseVForDirective(text);
2376
+ const vars = m ? m.aliases : text;
2377
+ this.collectIdentifierNames(vars, shadowed, 0);
2378
+ }
2379
+ /**
2380
+ * The `v-for` directive grammar, with NO regular expression.
2381
+ *
2382
+ * v-for="item in items" v-for="(row, col) in grid"
2383
+ * v-for="item of items" v-for="(value, key) of map"
2384
+ *
2385
+ * This replaces `/^\s*\(?([^)]*?)\)?\s+(?:in|of)\s+(.+)$/` and its tail-less
2386
+ * sibling, and reproduces their behaviour rather than an idealised version of
2387
+ * it, because three of the old behaviours are load-bearing:
2388
+ *
2389
+ * 1. `([^)]*?)` is LAZY, so the alias head is the SHORTEST run without a `)`
2390
+ * for which the rest of the pattern matches. A greedy scan would make
2391
+ * `item in items` bind `item in items`.
2392
+ * 2. `\)?` is optional, so the `)` belongs to the KEYWORD MATCH, not the head.
2393
+ * `(row, col) in grid` must bind `row` and `col` — and must stop at the
2394
+ * first `)`, because the head cannot contain one.
2395
+ * 3. `(.+)$` has no `s` flag, so `.` does NOT match a newline: a collection that
2396
+ * spans lines fails the pattern, and the caller falls back to treating the
2397
+ * WHOLE directive as the collection. That fallback is preserved, including
2398
+ * its consequence that such a file loses its shadowing (the tail-less
2399
+ * sibling still matches, so `collectVForVars` is unaffected).
2400
+ *
2401
+ * `collectionStart` is the byte offset of the collection within `text`, which
2402
+ * is what `collectVForUsage` needs to count the line breaks before it. It is
2403
+ * `-1` when the tail-less form matched but the collection form did not.
2404
+ */
2405
+ parseVForDirective(text) {
2406
+ const n = text.length;
2407
+ const isWs = (ch) => ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f" || ch === "\v";
2408
+ let i = 0;
2409
+ while (i < n && isWs(text[i])) i++;
2410
+ if (i < n && text[i] === "(") i++;
2411
+ const aliasStart = i;
2412
+ const keywordAt = (e) => {
2413
+ let j = e;
2414
+ if (j < n && text[j] === ")") j++;
2415
+ const ws1 = j;
2416
+ while (j < n && isWs(text[j])) j++;
2417
+ if (j === ws1) return null;
2418
+ const two = text.slice(j, j + 2);
2419
+ if (two !== "in" && two !== "of") return null;
2420
+ j += 2;
2421
+ const ws2 = j;
2422
+ while (j < n && isWs(text[j])) j++;
2423
+ if (j === ws2) return null;
2424
+ return { wsStart: ws2, wsEnd: j };
2425
+ };
2426
+ const limit = (() => {
2427
+ const close = text.indexOf(")", aliasStart);
2428
+ return close < 0 ? n : close;
2429
+ })();
2430
+ let aliases = "";
2431
+ let collectionStart = -1;
2432
+ for (let e = aliasStart; e <= limit; e++) {
2433
+ const kw = keywordAt(e);
2434
+ if (!kw) continue;
2435
+ let chosen = -1;
2436
+ for (let q = kw.wsEnd; q > kw.wsStart; q--) {
2437
+ const tail = text.slice(q);
2438
+ if (tail.length === 0) continue;
2439
+ if (tail.includes("\n")) continue;
2440
+ chosen = q;
2441
+ break;
2442
+ }
2443
+ if (chosen < 0) continue;
2444
+ aliases = text.slice(aliasStart, e);
2445
+ collectionStart = chosen;
2446
+ break;
2447
+ }
2448
+ if (collectionStart < 0) {
2449
+ for (let e = aliasStart; e <= limit; e++) {
2450
+ if (!keywordAt(e)) continue;
2451
+ aliases = text.slice(aliasStart, e);
2452
+ break;
2453
+ }
2454
+ }
2455
+ if (aliases === "") return null;
2456
+ return {
2457
+ aliases,
2458
+ collection: collectionStart >= 0 ? text.slice(collectionStart) : text,
2459
+ collectionStart
2460
+ };
2461
+ }
2462
+ /**
2463
+ * Tokenize identifiers out of template expression TEXT and SHADOW them, using
2464
+ * the same stack machine `collectIdentifiers` uses for member reads. The lane
2465
+ * previously ran a regex over this input in two places while the stack machine
2466
+ * sat one method away; both now share one tokenizer.
2467
+ */
2468
+ collectIdentifierNames(text, into, baseLine) {
2469
+ const scratch = /* @__PURE__ */ new Map();
2470
+ this.collectIdentifiers(text, scratch, void 0, baseLine);
2471
+ for (const k of scratch.keys()) into.add(k);
2472
+ }
2473
+ isLifecycleHook(key) {
2474
+ return key === "beforeCreate" || key === "created" || key === "beforeMount" || key === "mounted" || key === "beforeUpdate" || key === "updated" || key === "beforeUnmount" || key === "unmounted" || key === "activated" || key === "deactivated" || key === "errorCaptured" || key === "renderTracked" || key === "renderTriggered" || key === "beforeDestroy" || key === "destroyed" || // M3 (review wave): the Vue 3 SSR hook was missing, so
2475
+ // serverPrefetch() methods were invisible.
2476
+ key === "serverPrefetch";
2477
+ }
2478
+ // ============================================================================
2479
+ // HELPERS
2480
+ // ============================================================================
2481
+ /**
2482
+ * M14 #299 - evidence-tier classification for EVERY emitted relationship.
2483
+ * Same rules as the JVM family. Vue template component tags are
2484
+ * usage_contextual (component usage by tag name, not construction).
2485
+ *
2486
+ * THE 0.7 FLOOR IS APPLIED HERE, AND THIS IS THE ONLY LAYER THAT CAN APPLY IT
2487
+ * (F20, re-measured 2026-10-25 on the way to closing the fixture). The contract
2488
+ * in src/lsp/evidence_tier.ts is two rules that interact: rule 1 says a STORED
2489
+ * legend-tier evidenceClass WINS and is "never re-derived, never re-upgraded",
2490
+ * and rule 2 says an edge below the 0.7 floor is a guess and MUST read
2491
+ * `inferred`. Because rule 1 short-circuits the derivation, a confident class
2492
+ * STORED on a heuristic row is final — nobody downstream can correct it. The
2493
+ * lane measured the consequence over 1,151 corpus files: 1,628 rows sit at
2494
+ * confidence 0.6 (1,616 `uses` field-read/bare-read rows and 12 unresolved
2495
+ * dynamic-import/`require` rows) and every one was stored as `usage_contextual`
2496
+ * or `import_based`. So the floor is applied at the TOP of this pass, before
2497
+ * the stored-class skip and before any per-label arm: a sub-floor row stores
2498
+ * `inferred` whoever produced it (a delegate's `import_based` at 0.6 is the
2499
+ * same claim about a row the analyzer cannot see inside), and a row at or
2500
+ * above the floor keeps the arm's own classification. The confident template
2501
+ * read at 0.95 stays `usage_contextual` — the fixture pins it beside the
2502
+ * downgraded rows so a mutant that downgraded EVERYTHING would be caught.
2503
+ */
2504
+ classifyEvidenceTier(relationships) {
2505
+ for (const r of relationships) {
2506
+ const md = r.metadata ?? (r.metadata = {});
2507
+ const conf = typeof r.confidence === "number" ? r.confidence : 1;
2508
+ if (conf < 0.7) {
2509
+ md.evidenceClass = "inferred";
2510
+ continue;
2511
+ }
2512
+ if (md.evidenceClass) continue;
2513
+ const t = String(r.type ?? r.relationshipType ?? "");
2514
+ if (t === "contains" || t === "exports") {
2515
+ md.evidenceClass = "structural";
2516
+ continue;
2517
+ }
2518
+ if (t === "references") {
2519
+ md.evidenceClass = md.isImport ? "import_based" : "reference_qualified";
2520
+ continue;
2521
+ }
2522
+ if (t === "extends" || t === "implements" || t === "overrides") {
2523
+ md.evidenceClass = conf >= 1 ? "structural" : conf >= 0.85 ? "usage_contextual" : "inferred";
2524
+ continue;
2525
+ }
2526
+ if (t === "instantiates") {
2527
+ md.evidenceClass = md.isComponentUsage ? "usage_contextual" : "structural";
2528
+ continue;
2529
+ }
2530
+ if (t === "renders") {
2531
+ md.evidenceClass = "usage_contextual";
2532
+ continue;
2533
+ }
2534
+ md.evidenceClass = "usage_contextual";
2535
+ }
2536
+ }
2537
+ findDescendantObject(root) {
2538
+ const stack = [root];
2539
+ while (stack.length > 0) {
2540
+ const n = stack.pop();
2541
+ if (n.type === "object") return n;
2542
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
2543
+ const c = n.namedChild(i);
2544
+ if (c) stack.push(c);
2545
+ }
2546
+ }
2547
+ return void 0;
2548
+ }
2549
+ findExportDefault(root) {
2550
+ const stack = [root];
2551
+ while (stack.length > 0) {
2552
+ const n = stack.pop();
2553
+ if (n.type === "export_statement" && this.isDefaultExportStatement(n)) return n;
2554
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
2555
+ const c = n.namedChild(i);
2556
+ if (c) stack.push(c);
2557
+ }
2558
+ }
2559
+ return null;
2560
+ }
2561
+ isDefaultExportStatement(n) {
2562
+ let seenExport = false;
2563
+ for (let i = 0; i < n.childCount; i++) {
2564
+ const c = n.child(i);
2565
+ if (!c || c.type === "comment" || c.type === "decorator") continue;
2566
+ if (!seenExport) {
2567
+ if (!c.isNamed && c.text === "export") seenExport = true;
2568
+ else if (c.isNamed) return false;
2569
+ continue;
2570
+ }
2571
+ if (!c.isNamed && c.text === "default") return true;
2572
+ if (c.isNamed) return false;
2573
+ }
2574
+ return false;
2575
+ }
2576
+ elementTagName(element) {
2577
+ return this.elementTagNameNode(element)?.text;
2578
+ }
2579
+ /**
2580
+ * F9(b): the tag as WRITTEN, dotted segments and namespace included.
2581
+ *
2582
+ * `tag_name` spells only the FIRST segment: for `<Form.Input />` it is `Form`,
2583
+ * so reading it alone is what silently dropped the sub-component. The spelling
2584
+ * lives in the enclosing `start_tag` / `self_closing_tag` text, up to the first
2585
+ * delimiter. Written with indexOf rather than a regex on purpose — this file's
2586
+ * regex census is a claim the docs now publish, and adding an 7th literal to
2587
+ * recover a string the grammar already hands us in two pieces is the wrong
2588
+ * trade.
2589
+ */
2590
+ elementTagSpelling(element) {
2591
+ const tagNode = this.elementTagNameNode(element);
2592
+ const holder = this.findChildByType(element, "start_tag") ?? this.findChildByType(element, "self_closing_tag");
2593
+ let text = holder?.text ?? tagNode?.text;
2594
+ if (!text) return void 0;
2595
+ if (text.charCodeAt(0) === 60) text = text.slice(1);
2596
+ let end = text.length;
2597
+ for (const delim of [" ", " ", "\n", "\r", ">", "/"]) {
2598
+ const i = text.indexOf(delim);
2599
+ if (i >= 0 && i < end) end = i;
2600
+ }
2601
+ return end > 0 ? text.slice(0, end) : void 0;
2602
+ }
2603
+ /** The `tag_name` node itself — the node that SPELLS the tag, for its line. */
2604
+ elementTagNameNode(element) {
2605
+ const tagNode = this.findChildByType(element, "start_tag") || this.findChildByType(element, "self_closing_tag");
2606
+ if (!tagNode) return null;
2607
+ return this.findChildByType(tagNode, "tag_name");
2608
+ }
2609
+ offsetRange(range, lineOffset, colOffset, indexOffset = 0) {
2610
+ const out = { ...range };
2611
+ out.startLine += lineOffset;
2612
+ out.endLine += lineOffset;
2613
+ if (range.startLine === 1) out.startColumn += colOffset;
2614
+ if (indexOffset && out.startIndex !== void 0) out.startIndex += indexOffset;
2615
+ if (indexOffset && out.endIndex !== void 0) out.endIndex += indexOffset;
2616
+ return out;
2617
+ }
2618
+ /**
2619
+ * H2 (review wave): full script→SFC shift — lines always, columns when on
2620
+ * the script's first line (endColumn only when the range never leaves
2621
+ * it). Errors, exports, and imports were pushed raw, pointing at script
2622
+ * coordinates while symbols pointed at SFC ones.
2623
+ */
2624
+ shiftScriptRange(range, lineOffset, colOffset, indexOffset = 0) {
2625
+ const out = { ...range };
2626
+ const firstLine = out.startLine === 1;
2627
+ out.startLine += lineOffset;
2628
+ out.endLine += lineOffset;
2629
+ if (firstLine && colOffset) {
2630
+ out.startColumn += colOffset;
2631
+ if (out.endLine === out.startLine) out.endColumn += colOffset;
2632
+ }
2633
+ if (indexOffset && out.startIndex !== void 0) out.startIndex += indexOffset;
2634
+ if (indexOffset && out.endIndex !== void 0) out.endIndex += indexOffset;
2635
+ return out;
2636
+ }
2637
+ /**
2638
+ * The object literal a `provide(key, …)` publishes, unwrapping ONE call layer.
2639
+ *
2640
+ * `provide(k, reactive({ a, b }))` publishes `a` and `b`, but the direct
2641
+ * argument is a call, not an object, so reading only `args[1]` as an object
2642
+ * found nothing. `reactive` / `readonly` / `markRaw` / `computed` all take the
2643
+ * object as their first argument, which is why one level is enough;
2644
+ * `form.vue`'s `provide(formContextKey, reactive({ ...toRefs(props), emit,
2645
+ * resetFields, clearValidate, validateField }))` lost every one of those keys
2646
+ * this way. One layer only — deeper is a shape this lane should not guess at.
2647
+ */
2648
+ providedValueObject(value) {
2649
+ if (!value) return null;
2650
+ if (value.type === "object") return value;
2651
+ if (value.type !== "call_expression") return null;
2652
+ const args = this.findChildByType(value, "arguments");
2653
+ if (!args) return null;
2654
+ for (let i = 0; i < args.namedChildCount; i++) {
2655
+ const c = args.namedChild(i);
2656
+ if (c && c.type === "object") return c;
2657
+ }
2658
+ return null;
2659
+ }
2660
+ findChildByType(node, type) {
2661
+ for (let i = 0; i < node.namedChildCount; i++) {
2662
+ const child = node.namedChild(i);
2663
+ if (child && child.type === type) return child;
2664
+ }
2665
+ return null;
2666
+ }
2667
+ nodeToRange(node) {
2668
+ return {
2669
+ startLine: node.startPosition.row + 1,
2670
+ startColumn: node.startPosition.column + 1,
2671
+ endLine: node.endPosition.row + 1,
2672
+ endColumn: node.endPosition.column + 1,
2673
+ startIndex: node.startIndex,
2674
+ endIndex: node.endIndex
2675
+ };
2676
+ }
2677
+ };
2678
+ export {
2679
+ RealVueAnalyzer
2680
+ };