gitnexus 1.5.3 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/README.md +10 -0
  2. package/dist/_shared/graph/types.d.ts +1 -1
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/index.d.ts +1 -0
  5. package/dist/_shared/index.d.ts.map +1 -1
  6. package/dist/_shared/language-detection.d.ts.map +1 -1
  7. package/dist/_shared/language-detection.js +2 -0
  8. package/dist/_shared/language-detection.js.map +1 -1
  9. package/dist/_shared/languages.d.ts +1 -0
  10. package/dist/_shared/languages.d.ts.map +1 -1
  11. package/dist/_shared/languages.js +1 -0
  12. package/dist/_shared/languages.js.map +1 -1
  13. package/dist/_shared/lbug/schema-constants.d.ts +1 -1
  14. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  15. package/dist/_shared/lbug/schema-constants.js +3 -1
  16. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  17. package/dist/_shared/mro-strategy.d.ts +19 -0
  18. package/dist/_shared/mro-strategy.d.ts.map +1 -0
  19. package/dist/_shared/mro-strategy.js +2 -0
  20. package/dist/_shared/mro-strategy.js.map +1 -0
  21. package/dist/cli/ai-context.d.ts +1 -0
  22. package/dist/cli/ai-context.js +28 -4
  23. package/dist/cli/analyze.d.ts +2 -0
  24. package/dist/cli/analyze.js +30 -4
  25. package/dist/cli/group.d.ts +2 -0
  26. package/dist/cli/group.js +233 -0
  27. package/dist/cli/index.js +3 -0
  28. package/dist/cli/serve.js +4 -1
  29. package/dist/cli/setup.js +34 -3
  30. package/dist/config/ignore-service.js +8 -3
  31. package/dist/core/augmentation/engine.js +1 -1
  32. package/dist/core/git-staleness.d.ts +13 -0
  33. package/dist/core/git-staleness.js +29 -0
  34. package/dist/core/group/bridge-db.d.ts +82 -0
  35. package/dist/core/group/bridge-db.js +460 -0
  36. package/dist/core/group/bridge-schema.d.ts +27 -0
  37. package/dist/core/group/bridge-schema.js +55 -0
  38. package/dist/core/group/config-parser.d.ts +3 -0
  39. package/dist/core/group/config-parser.js +83 -0
  40. package/dist/core/group/contract-extractor.d.ts +7 -0
  41. package/dist/core/group/contract-extractor.js +1 -0
  42. package/dist/core/group/extractors/fs-utils.d.ts +10 -0
  43. package/dist/core/group/extractors/fs-utils.js +24 -0
  44. package/dist/core/group/extractors/grpc-extractor.d.ts +25 -0
  45. package/dist/core/group/extractors/grpc-extractor.js +386 -0
  46. package/dist/core/group/extractors/grpc-patterns/go.d.ts +2 -0
  47. package/dist/core/group/extractors/grpc-patterns/go.js +97 -0
  48. package/dist/core/group/extractors/grpc-patterns/index.d.ts +19 -0
  49. package/dist/core/group/extractors/grpc-patterns/index.js +46 -0
  50. package/dist/core/group/extractors/grpc-patterns/java.d.ts +2 -0
  51. package/dist/core/group/extractors/grpc-patterns/java.js +173 -0
  52. package/dist/core/group/extractors/grpc-patterns/node.d.ts +4 -0
  53. package/dist/core/group/extractors/grpc-patterns/node.js +290 -0
  54. package/dist/core/group/extractors/grpc-patterns/proto.d.ts +9 -0
  55. package/dist/core/group/extractors/grpc-patterns/proto.js +134 -0
  56. package/dist/core/group/extractors/grpc-patterns/python.d.ts +2 -0
  57. package/dist/core/group/extractors/grpc-patterns/python.js +67 -0
  58. package/dist/core/group/extractors/grpc-patterns/types.d.ts +50 -0
  59. package/dist/core/group/extractors/grpc-patterns/types.js +1 -0
  60. package/dist/core/group/extractors/http-patterns/go.d.ts +2 -0
  61. package/dist/core/group/extractors/http-patterns/go.js +215 -0
  62. package/dist/core/group/extractors/http-patterns/index.d.ts +17 -0
  63. package/dist/core/group/extractors/http-patterns/index.js +44 -0
  64. package/dist/core/group/extractors/http-patterns/java.d.ts +2 -0
  65. package/dist/core/group/extractors/http-patterns/java.js +253 -0
  66. package/dist/core/group/extractors/http-patterns/node.d.ts +4 -0
  67. package/dist/core/group/extractors/http-patterns/node.js +354 -0
  68. package/dist/core/group/extractors/http-patterns/php.d.ts +2 -0
  69. package/dist/core/group/extractors/http-patterns/php.js +70 -0
  70. package/dist/core/group/extractors/http-patterns/python.d.ts +2 -0
  71. package/dist/core/group/extractors/http-patterns/python.js +133 -0
  72. package/dist/core/group/extractors/http-patterns/types.d.ts +61 -0
  73. package/dist/core/group/extractors/http-patterns/types.js +1 -0
  74. package/dist/core/group/extractors/http-route-extractor.d.ts +21 -0
  75. package/dist/core/group/extractors/http-route-extractor.js +391 -0
  76. package/dist/core/group/extractors/manifest-extractor.d.ts +54 -0
  77. package/dist/core/group/extractors/manifest-extractor.js +235 -0
  78. package/dist/core/group/extractors/topic-extractor.d.ts +8 -0
  79. package/dist/core/group/extractors/topic-extractor.js +97 -0
  80. package/dist/core/group/extractors/topic-patterns/go.d.ts +2 -0
  81. package/dist/core/group/extractors/topic-patterns/go.js +120 -0
  82. package/dist/core/group/extractors/topic-patterns/index.d.ts +14 -0
  83. package/dist/core/group/extractors/topic-patterns/index.js +38 -0
  84. package/dist/core/group/extractors/topic-patterns/java.d.ts +2 -0
  85. package/dist/core/group/extractors/topic-patterns/java.js +80 -0
  86. package/dist/core/group/extractors/topic-patterns/node.d.ts +4 -0
  87. package/dist/core/group/extractors/topic-patterns/node.js +155 -0
  88. package/dist/core/group/extractors/topic-patterns/python.d.ts +2 -0
  89. package/dist/core/group/extractors/topic-patterns/python.js +116 -0
  90. package/dist/core/group/extractors/topic-patterns/types.d.ts +25 -0
  91. package/dist/core/group/extractors/topic-patterns/types.js +10 -0
  92. package/dist/core/group/extractors/tree-sitter-scanner.d.ts +113 -0
  93. package/dist/core/group/extractors/tree-sitter-scanner.js +94 -0
  94. package/dist/core/group/matching.d.ts +13 -0
  95. package/dist/core/group/matching.js +198 -0
  96. package/dist/core/group/normalization.d.ts +3 -0
  97. package/dist/core/group/normalization.js +115 -0
  98. package/dist/core/group/service-boundary-detector.d.ts +8 -0
  99. package/dist/core/group/service-boundary-detector.js +155 -0
  100. package/dist/core/group/service.d.ts +46 -0
  101. package/dist/core/group/service.js +160 -0
  102. package/dist/core/group/storage.d.ts +9 -0
  103. package/dist/core/group/storage.js +91 -0
  104. package/dist/core/group/sync.d.ts +21 -0
  105. package/dist/core/group/sync.js +148 -0
  106. package/dist/core/group/types.d.ts +130 -0
  107. package/dist/core/group/types.js +1 -0
  108. package/dist/core/ingestion/binding-accumulator.d.ts +212 -0
  109. package/dist/core/ingestion/binding-accumulator.js +336 -0
  110. package/dist/core/ingestion/call-processor.d.ts +155 -24
  111. package/dist/core/ingestion/call-processor.js +1129 -247
  112. package/dist/core/ingestion/class-extractors/generic.d.ts +2 -0
  113. package/dist/core/ingestion/class-extractors/generic.js +135 -0
  114. package/dist/core/ingestion/class-types.d.ts +34 -0
  115. package/dist/core/ingestion/class-types.js +1 -0
  116. package/dist/core/ingestion/cobol-processor.d.ts +1 -1
  117. package/dist/core/ingestion/entry-point-scoring.d.ts +1 -0
  118. package/dist/core/ingestion/entry-point-scoring.js +1 -0
  119. package/dist/core/ingestion/field-types.d.ts +2 -2
  120. package/dist/core/ingestion/filesystem-walker.js +8 -0
  121. package/dist/core/ingestion/framework-detection.d.ts +1 -0
  122. package/dist/core/ingestion/framework-detection.js +1 -0
  123. package/dist/core/ingestion/heritage-processor.d.ts +8 -15
  124. package/dist/core/ingestion/heritage-processor.js +15 -28
  125. package/dist/core/ingestion/import-processor.d.ts +1 -11
  126. package/dist/core/ingestion/import-processor.js +1 -13
  127. package/dist/core/ingestion/import-resolvers/utils.js +1 -0
  128. package/dist/core/ingestion/import-resolvers/vue.d.ts +8 -0
  129. package/dist/core/ingestion/import-resolvers/vue.js +9 -0
  130. package/dist/core/ingestion/language-config.js +1 -1
  131. package/dist/core/ingestion/language-provider.d.ts +14 -3
  132. package/dist/core/ingestion/languages/c-cpp.js +168 -1
  133. package/dist/core/ingestion/languages/csharp.js +20 -0
  134. package/dist/core/ingestion/languages/dart.js +26 -4
  135. package/dist/core/ingestion/languages/go.js +22 -0
  136. package/dist/core/ingestion/languages/index.d.ts +1 -0
  137. package/dist/core/ingestion/languages/index.js +2 -0
  138. package/dist/core/ingestion/languages/java.js +17 -0
  139. package/dist/core/ingestion/languages/kotlin.js +24 -1
  140. package/dist/core/ingestion/languages/php.js +23 -11
  141. package/dist/core/ingestion/languages/python.js +9 -0
  142. package/dist/core/ingestion/languages/ruby.js +43 -0
  143. package/dist/core/ingestion/languages/rust.js +38 -0
  144. package/dist/core/ingestion/languages/swift.js +31 -0
  145. package/dist/core/ingestion/languages/typescript.d.ts +1 -0
  146. package/dist/core/ingestion/languages/typescript.js +52 -3
  147. package/dist/core/ingestion/languages/vue.d.ts +13 -0
  148. package/dist/core/ingestion/languages/vue.js +81 -0
  149. package/dist/core/ingestion/markdown-processor.d.ts +1 -1
  150. package/dist/core/ingestion/method-extractors/configs/c-cpp.d.ts +3 -0
  151. package/dist/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
  152. package/dist/core/ingestion/method-extractors/configs/csharp.js +5 -1
  153. package/dist/core/ingestion/method-extractors/configs/dart.d.ts +2 -0
  154. package/dist/core/ingestion/method-extractors/configs/dart.js +376 -0
  155. package/dist/core/ingestion/method-extractors/configs/go.d.ts +2 -0
  156. package/dist/core/ingestion/method-extractors/configs/go.js +176 -0
  157. package/dist/core/ingestion/method-extractors/configs/jvm.js +14 -4
  158. package/dist/core/ingestion/method-extractors/configs/php.d.ts +2 -0
  159. package/dist/core/ingestion/method-extractors/configs/php.js +304 -0
  160. package/dist/core/ingestion/method-extractors/configs/python.d.ts +2 -0
  161. package/dist/core/ingestion/method-extractors/configs/python.js +309 -0
  162. package/dist/core/ingestion/method-extractors/configs/ruby.d.ts +2 -0
  163. package/dist/core/ingestion/method-extractors/configs/ruby.js +286 -0
  164. package/dist/core/ingestion/method-extractors/configs/rust.d.ts +2 -0
  165. package/dist/core/ingestion/method-extractors/configs/rust.js +195 -0
  166. package/dist/core/ingestion/method-extractors/configs/swift.d.ts +2 -0
  167. package/dist/core/ingestion/method-extractors/configs/swift.js +277 -0
  168. package/dist/core/ingestion/method-extractors/configs/typescript-javascript.js +85 -8
  169. package/dist/core/ingestion/method-extractors/generic.d.ts +6 -0
  170. package/dist/core/ingestion/method-extractors/generic.js +84 -17
  171. package/dist/core/ingestion/method-types.d.ts +29 -0
  172. package/dist/core/ingestion/model/field-registry.d.ts +18 -0
  173. package/dist/core/ingestion/model/field-registry.js +22 -0
  174. package/dist/core/ingestion/model/heritage-map.d.ts +70 -0
  175. package/dist/core/ingestion/model/heritage-map.js +159 -0
  176. package/dist/core/ingestion/model/index.d.ts +20 -0
  177. package/dist/core/ingestion/model/index.js +41 -0
  178. package/dist/core/ingestion/model/method-registry.d.ts +62 -0
  179. package/dist/core/ingestion/model/method-registry.js +130 -0
  180. package/dist/core/ingestion/model/registration-table.d.ts +139 -0
  181. package/dist/core/ingestion/model/registration-table.js +224 -0
  182. package/dist/core/ingestion/model/resolution-context.d.ts +93 -0
  183. package/dist/core/ingestion/model/resolution-context.js +337 -0
  184. package/dist/core/ingestion/model/resolve.d.ts +56 -0
  185. package/dist/core/ingestion/model/resolve.js +297 -0
  186. package/dist/core/ingestion/model/semantic-model.d.ts +86 -0
  187. package/dist/core/ingestion/model/semantic-model.js +120 -0
  188. package/dist/core/ingestion/model/symbol-table.d.ts +222 -0
  189. package/dist/core/ingestion/model/symbol-table.js +206 -0
  190. package/dist/core/ingestion/model/type-registry.d.ts +39 -0
  191. package/dist/core/ingestion/model/type-registry.js +62 -0
  192. package/dist/core/ingestion/mro-processor.d.ts +5 -4
  193. package/dist/core/ingestion/mro-processor.js +311 -107
  194. package/dist/core/ingestion/parsing-processor.d.ts +5 -4
  195. package/dist/core/ingestion/parsing-processor.js +224 -87
  196. package/dist/core/ingestion/pipeline-phases/cobol.d.ts +16 -0
  197. package/dist/core/ingestion/pipeline-phases/cobol.js +45 -0
  198. package/dist/core/ingestion/pipeline-phases/communities.d.ts +16 -0
  199. package/dist/core/ingestion/pipeline-phases/communities.js +62 -0
  200. package/dist/core/ingestion/pipeline-phases/cross-file-impl.d.ts +17 -0
  201. package/dist/core/ingestion/pipeline-phases/cross-file-impl.js +156 -0
  202. package/dist/core/ingestion/pipeline-phases/cross-file.d.ts +37 -0
  203. package/dist/core/ingestion/pipeline-phases/cross-file.js +63 -0
  204. package/dist/core/ingestion/pipeline-phases/index.d.ts +21 -0
  205. package/dist/core/ingestion/pipeline-phases/index.js +22 -0
  206. package/dist/core/ingestion/pipeline-phases/markdown.d.ts +17 -0
  207. package/dist/core/ingestion/pipeline-phases/markdown.js +33 -0
  208. package/dist/core/ingestion/pipeline-phases/mro.d.ts +18 -0
  209. package/dist/core/ingestion/pipeline-phases/mro.js +36 -0
  210. package/dist/core/ingestion/pipeline-phases/orm-extraction.d.ts +22 -0
  211. package/dist/core/ingestion/pipeline-phases/orm-extraction.js +92 -0
  212. package/dist/core/ingestion/pipeline-phases/orm.d.ts +15 -0
  213. package/dist/core/ingestion/pipeline-phases/orm.js +74 -0
  214. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +47 -0
  215. package/dist/core/ingestion/pipeline-phases/parse-impl.js +437 -0
  216. package/dist/core/ingestion/pipeline-phases/parse.d.ts +49 -0
  217. package/dist/core/ingestion/pipeline-phases/parse.js +33 -0
  218. package/dist/core/ingestion/pipeline-phases/processes.d.ts +16 -0
  219. package/dist/core/ingestion/pipeline-phases/processes.js +143 -0
  220. package/dist/core/ingestion/pipeline-phases/routes.d.ts +21 -0
  221. package/dist/core/ingestion/pipeline-phases/routes.js +243 -0
  222. package/dist/core/ingestion/pipeline-phases/runner.d.ts +22 -0
  223. package/dist/core/ingestion/pipeline-phases/runner.js +203 -0
  224. package/dist/core/ingestion/pipeline-phases/scan.d.ts +21 -0
  225. package/dist/core/ingestion/pipeline-phases/scan.js +46 -0
  226. package/dist/core/ingestion/pipeline-phases/structure.d.ts +27 -0
  227. package/dist/core/ingestion/pipeline-phases/structure.js +35 -0
  228. package/dist/core/ingestion/pipeline-phases/tools.d.ts +20 -0
  229. package/dist/core/ingestion/pipeline-phases/tools.js +79 -0
  230. package/dist/core/ingestion/pipeline-phases/types.d.ts +79 -0
  231. package/dist/core/ingestion/pipeline-phases/types.js +37 -0
  232. package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.d.ts +35 -0
  233. package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.js +174 -0
  234. package/dist/core/ingestion/pipeline.d.ts +18 -10
  235. package/dist/core/ingestion/pipeline.js +66 -1410
  236. package/dist/core/ingestion/process-processor.js +1 -1
  237. package/dist/core/ingestion/tree-sitter-queries.d.ts +5 -5
  238. package/dist/core/ingestion/tree-sitter-queries.js +90 -0
  239. package/dist/core/ingestion/type-env.d.ts +15 -2
  240. package/dist/core/ingestion/type-env.js +163 -102
  241. package/dist/core/ingestion/type-extractors/csharp.js +17 -0
  242. package/dist/core/ingestion/type-extractors/jvm.js +11 -0
  243. package/dist/core/ingestion/type-extractors/php.js +0 -55
  244. package/dist/core/ingestion/type-extractors/ruby.js +0 -32
  245. package/dist/core/ingestion/type-extractors/swift.js +13 -0
  246. package/dist/core/ingestion/type-extractors/types.d.ts +8 -8
  247. package/dist/core/ingestion/type-extractors/typescript.js +66 -69
  248. package/dist/core/ingestion/utils/ast-helpers.d.ts +32 -44
  249. package/dist/core/ingestion/utils/ast-helpers.js +157 -573
  250. package/dist/core/ingestion/utils/env.d.ts +10 -0
  251. package/dist/core/ingestion/utils/env.js +10 -0
  252. package/dist/core/ingestion/utils/graph-sort.d.ts +58 -0
  253. package/dist/core/ingestion/utils/graph-sort.js +100 -0
  254. package/dist/core/ingestion/utils/method-props.d.ts +32 -0
  255. package/dist/core/ingestion/utils/method-props.js +147 -0
  256. package/dist/core/ingestion/vue-sfc-extractor.d.ts +44 -0
  257. package/dist/core/ingestion/vue-sfc-extractor.js +94 -0
  258. package/dist/core/ingestion/workers/parse-worker.d.ts +31 -19
  259. package/dist/core/ingestion/workers/parse-worker.js +469 -200
  260. package/dist/core/lbug/lbug-adapter.d.ts +6 -0
  261. package/dist/core/lbug/lbug-adapter.js +134 -27
  262. package/dist/core/lbug/pool-adapter.d.ts +76 -0
  263. package/dist/core/lbug/pool-adapter.js +522 -0
  264. package/dist/core/run-analyze.d.ts +2 -0
  265. package/dist/core/run-analyze.js +1 -1
  266. package/dist/core/search/bm25-index.js +1 -1
  267. package/dist/core/tree-sitter/parser-loader.js +1 -0
  268. package/dist/core/wiki/graph-queries.js +1 -1
  269. package/dist/mcp/core/embedder.js +6 -5
  270. package/dist/mcp/core/lbug-adapter.d.ts +3 -63
  271. package/dist/mcp/core/lbug-adapter.js +3 -484
  272. package/dist/mcp/local/local-backend.d.ts +31 -2
  273. package/dist/mcp/local/local-backend.js +255 -46
  274. package/dist/mcp/resources.js +5 -4
  275. package/dist/mcp/staleness.d.ts +3 -13
  276. package/dist/mcp/staleness.js +2 -31
  277. package/dist/mcp/tools.js +80 -4
  278. package/dist/server/analyze-job.d.ts +2 -0
  279. package/dist/server/analyze-job.js +4 -0
  280. package/dist/server/api.d.ts +20 -1
  281. package/dist/server/api.js +306 -71
  282. package/dist/server/git-clone.d.ts +2 -1
  283. package/dist/server/git-clone.js +98 -5
  284. package/dist/storage/git.d.ts +13 -0
  285. package/dist/storage/git.js +25 -0
  286. package/dist/storage/repo-manager.js +1 -1
  287. package/package.json +9 -3
  288. package/scripts/patch-tree-sitter-swift.cjs +78 -0
  289. package/vendor/tree-sitter-proto/binding.gyp +30 -0
  290. package/vendor/tree-sitter-proto/bindings/node/binding.cc +20 -0
  291. package/vendor/tree-sitter-proto/bindings/node/index.d.ts +28 -0
  292. package/vendor/tree-sitter-proto/bindings/node/index.js +7 -0
  293. package/vendor/tree-sitter-proto/package.json +18 -0
  294. package/vendor/tree-sitter-proto/src/node-types.json +1145 -0
  295. package/vendor/tree-sitter-proto/src/parser.c +10149 -0
  296. package/vendor/tree-sitter-proto/src/tree_sitter/alloc.h +54 -0
  297. package/vendor/tree-sitter-proto/src/tree_sitter/array.h +291 -0
  298. package/vendor/tree-sitter-proto/src/tree_sitter/parser.h +266 -0
  299. package/dist/core/ingestion/named-binding-processor.d.ts +0 -18
  300. package/dist/core/ingestion/named-binding-processor.js +0 -42
  301. package/dist/core/ingestion/resolution-context.d.ts +0 -58
  302. package/dist/core/ingestion/resolution-context.js +0 -135
  303. package/dist/core/ingestion/symbol-table.d.ts +0 -79
  304. package/dist/core/ingestion/symbol-table.js +0 -115
@@ -35,14 +35,15 @@ try {
35
35
  }
36
36
  catch { }
37
37
  import { getLanguageFromFilename } from '../../../_shared/index.js';
38
- import { FUNCTION_NODE_TYPES, extractFunctionName, getDefinitionNodeFromCaptures, findEnclosingClassId, getLabelFromCaptures, extractMethodSignature, findDescendant, extractStringContent, } from '../utils/ast-helpers.js';
38
+ import { FUNCTION_NODE_TYPES, getDefinitionNodeFromCaptures, findEnclosingClassInfo, getLabelFromCaptures, findDescendant, extractStringContent, genericFuncName, inferFunctionLabel, CLASS_CONTAINER_TYPES, } from '../utils/ast-helpers.js';
39
39
  import { countCallArguments, inferCallForm, extractReceiverName, extractReceiverNode, extractMixedChain, extractCallArgTypes, } from '../utils/call-analysis.js';
40
40
  import { extractParsedCallSite } from '../call-sites/extract-language-call-site.js';
41
41
  import { buildTypeEnv } from '../type-env.js';
42
42
  import { detectFrameworkFromAST } from '../framework-detection.js';
43
43
  import { generateId } from '../../../lib/utils.js';
44
44
  import { preprocessImportPath } from '../import-processor.js';
45
- import { CLASS_CONTAINER_TYPES } from '../utils/ast-helpers.js';
45
+ import { extractVueScript, extractTemplateComponents, isVueSetupTopLevel, } from '../vue-sfc-extractor.js';
46
+ import { buildMethodProps, arityForIdFromInfo, typeTagForId, constTagForId, buildCollisionGroups, } from '../utils/method-props.js';
46
47
  // ============================================================================
47
48
  // Worker-local parser + language map
48
49
  // ============================================================================
@@ -61,6 +62,7 @@ const languageMap = {
61
62
  ...(Kotlin ? { [SupportedLanguages.Kotlin]: Kotlin } : {}),
62
63
  [SupportedLanguages.PHP]: PHP.php_only,
63
64
  [SupportedLanguages.Ruby]: Ruby,
65
+ [SupportedLanguages.Vue]: TypeScript.typescript,
64
66
  ...(Dart ? { [SupportedLanguages.Dart]: Dart } : {}),
65
67
  ...(Swift ? { [SupportedLanguages.Swift]: Swift } : {}),
66
68
  };
@@ -114,21 +116,100 @@ function findEnclosingClassNode(node) {
114
116
  let current = node.parent;
115
117
  while (current) {
116
118
  if (CLASS_CONTAINER_TYPES.has(current.type)) {
119
+ // Return singleton_class directly so the method extractor sees it as
120
+ // the owner node and correctly marks methods as static. Name resolution
121
+ // for qualified names is handled separately by findEnclosingClassInfo.
117
122
  return current;
118
123
  }
119
124
  current = current.parent;
120
125
  }
121
126
  return null;
122
127
  }
128
+ /**
129
+ * For C++ out-of-class method definitions (e.g. `void Foo::bar() {}`), extract the
130
+ * class name from the qualified_identifier scope and find the class declaration in the
131
+ * file's AST. Returns the class SyntaxNode or null if not found.
132
+ *
133
+ * Handles pointer/reference return types where function_declarator is nested inside
134
+ * pointer_declarator or reference_declarator.
135
+ */
136
+ function findClassNodeByQualifiedName(node) {
137
+ const declarator = node.childForFieldName('declarator');
138
+ if (!declarator)
139
+ return null;
140
+ // Find the function_declarator, recursively unwrapping pointer_declarator /
141
+ // reference_declarator chains (e.g. int** Foo::bar() has
142
+ // pointer_declarator → pointer_declarator → function_declarator).
143
+ let funcDecl = null;
144
+ if (declarator.type === 'function_declarator') {
145
+ funcDecl = declarator;
146
+ }
147
+ else {
148
+ let current = declarator;
149
+ while (current && !funcDecl) {
150
+ for (let i = 0; i < current.namedChildCount; i++) {
151
+ const child = current.namedChild(i);
152
+ if (child?.type === 'function_declarator') {
153
+ funcDecl = child;
154
+ break;
155
+ }
156
+ }
157
+ if (!funcDecl) {
158
+ const next = current.namedChildren.find((c) => c.type === 'pointer_declarator' || c.type === 'reference_declarator');
159
+ current = next ?? null;
160
+ }
161
+ }
162
+ }
163
+ if (!funcDecl)
164
+ return null;
165
+ // Check if the inner declarator is a qualified_identifier (Foo::bar)
166
+ const innerDecl = funcDecl.childForFieldName('declarator');
167
+ if (!innerDecl || innerDecl.type !== 'qualified_identifier')
168
+ return null;
169
+ const scope = innerDecl.childForFieldName('scope');
170
+ if (!scope)
171
+ return null;
172
+ const className = scope.text;
173
+ // Search the file for a matching class/struct specifier, including inside
174
+ // namespace_definition blocks (the majority of production C++ uses namespaces).
175
+ const root = node.tree.rootNode;
176
+ const classTypes = new Set(['class_specifier', 'struct_specifier']);
177
+ const searchIn = (parent) => {
178
+ for (let i = 0; i < parent.namedChildCount; i++) {
179
+ const child = parent.namedChild(i);
180
+ if (!child)
181
+ continue;
182
+ if (classTypes.has(child.type)) {
183
+ const nameNode = child.childForFieldName('name');
184
+ if (nameNode?.text === className)
185
+ return child;
186
+ }
187
+ // Recurse into namespace blocks
188
+ if (child.type === 'namespace_definition') {
189
+ const found = searchIn(child);
190
+ if (found)
191
+ return found;
192
+ }
193
+ }
194
+ return null;
195
+ };
196
+ return searchIn(root);
197
+ }
123
198
  /**
124
199
  * Minimal no-op SymbolTable stub for FieldExtractorContext in the worker.
125
- * Field extraction only uses symbolTable.lookupExactAll for optional type resolution —
126
- * returning [] causes the extractor to use the raw type string, which is fine for us.
200
+ * Field extraction only uses symbolTable.lookupExactAll for optional type
201
+ * resolution — returning [] causes the extractor to use the raw type
202
+ * string, which is fine for us. Every other method is a no-op so the
203
+ * stub remains safe if a future FieldExtractor consults it through the
204
+ * full {@link SymbolTableReader} surface.
127
205
  */
128
206
  const NOOP_SYMBOL_TABLE = {
129
- lookupExactAll: () => [],
130
207
  lookupExact: () => undefined,
131
208
  lookupExactFull: () => undefined,
209
+ lookupExactAll: () => [],
210
+ lookupCallableByName: () => [],
211
+ getFiles: () => [][Symbol.iterator](),
212
+ getStats: () => ({ fileCount: 0 }),
132
213
  };
133
214
  /**
134
215
  * Get (or extract and cache) field info for a class node.
@@ -180,6 +261,9 @@ function getMethodInfo(classNode, provider, context) {
180
261
  methodInfoCache.set(cacheKey, cached);
181
262
  return cached;
182
263
  }
264
+ // ============================================================================
265
+ // Enclosing function detection (for call extraction) — cached
266
+ // ============================================================================
183
267
  /** Walk up AST to find enclosing function, return its generateId or null for top-level.
184
268
  * Applies provider.labelOverride so the label matches the definition phase (single source of truth). */
185
269
  const findEnclosingFunctionId = (node, filePath, provider) => {
@@ -189,7 +273,9 @@ const findEnclosingFunctionId = (node, filePath, provider) => {
189
273
  let current = node.parent;
190
274
  while (current) {
191
275
  if (FUNCTION_NODE_TYPES.has(current.type)) {
192
- const { funcName, label } = extractFunctionName(current);
276
+ const efnResult = provider.methodExtractor?.extractFunctionName?.(current);
277
+ const funcName = efnResult?.funcName ?? genericFuncName(current);
278
+ const label = efnResult?.label ?? inferFunctionLabel(current.type);
193
279
  if (funcName) {
194
280
  // Apply labelOverride so label matches definition phase (e.g., Kotlin Function→Method).
195
281
  // null means "skip as definition" — keep original label for scope identification.
@@ -199,7 +285,39 @@ const findEnclosingFunctionId = (node, filePath, provider) => {
199
285
  if (override !== null)
200
286
  finalLabel = override;
201
287
  }
202
- const result = generateId(finalLabel, `${filePath}:${funcName}`);
288
+ // Qualify with enclosing class to match definition-phase node IDs
289
+ const classInfo = cachedFindEnclosingClassInfo(current, filePath, provider.resolveEnclosingOwner);
290
+ const qualifiedName = classInfo ? `${classInfo.className}.${funcName}` : funcName;
291
+ // Include #<arity> suffix to match definition-phase Method/Constructor IDs.
292
+ // Use the same MethodExtractor (getMethodInfo) as the definition phase.
293
+ // When same-arity collisions exist, also append ~type1,type2.
294
+ let arity;
295
+ let encTypeTag = '';
296
+ if (finalLabel === 'Method' || finalLabel === 'Constructor') {
297
+ const encLang = getLanguageFromFilename(filePath);
298
+ const classNode = findEnclosingClassNode(current) ?? findClassNodeByQualifiedName(current);
299
+ if (classNode && encLang) {
300
+ const methodMap = getMethodInfo(classNode, provider, {
301
+ filePath,
302
+ language: encLang,
303
+ });
304
+ const defLine = current.startPosition.row + 1;
305
+ const info = methodMap?.get(`${funcName}:${defLine}`);
306
+ if (info) {
307
+ arity = info.parameters.some((p) => p.isVariadic)
308
+ ? undefined
309
+ : info.parameters.length;
310
+ if (methodMap && arity !== undefined) {
311
+ const g = buildCollisionGroups(methodMap);
312
+ encTypeTag =
313
+ typeTagForId(methodMap, funcName, arity, info, encLang, g) +
314
+ constTagForId(methodMap, funcName, arity, info, g);
315
+ }
316
+ }
317
+ }
318
+ }
319
+ const arityTag = arity !== undefined ? `#${arity}${encTypeTag}` : '';
320
+ const result = generateId(finalLabel, `${filePath}:${qualifiedName}${arityTag}`);
203
321
  functionIdCache.set(node, result);
204
322
  return result;
205
323
  }
@@ -215,7 +333,41 @@ const findEnclosingFunctionId = (node, filePath, provider) => {
215
333
  if (override !== null)
216
334
  finalLabel = override;
217
335
  }
218
- const result = generateId(finalLabel, `${filePath}:${customResult.funcName}`);
336
+ // Qualify custom result with enclosing class
337
+ const classInfo = cachedFindEnclosingClassInfo(current.previousSibling ?? current, filePath, provider.resolveEnclosingOwner);
338
+ const qualifiedName = classInfo
339
+ ? `${classInfo.className}.${customResult.funcName}`
340
+ : customResult.funcName;
341
+ // Include #<arity> suffix to match definition-phase Method/Constructor IDs.
342
+ // When same-arity collisions exist, also append ~type1,type2.
343
+ const sigNode = current.previousSibling ?? current;
344
+ let arity2;
345
+ let encTypeTag2 = '';
346
+ if (finalLabel === 'Method' || finalLabel === 'Constructor') {
347
+ const encLang2 = getLanguageFromFilename(filePath);
348
+ const classNode2 = findEnclosingClassNode(sigNode) ?? findClassNodeByQualifiedName(sigNode);
349
+ if (classNode2 && encLang2) {
350
+ const methodMap2 = getMethodInfo(classNode2, provider, {
351
+ filePath,
352
+ language: encLang2,
353
+ });
354
+ const defLine2 = sigNode.startPosition.row + 1;
355
+ const info2 = methodMap2?.get(`${customResult.funcName}:${defLine2}`);
356
+ if (info2) {
357
+ arity2 = info2.parameters.some((p) => p.isVariadic)
358
+ ? undefined
359
+ : info2.parameters.length;
360
+ if (methodMap2 && arity2 !== undefined) {
361
+ const g2 = buildCollisionGroups(methodMap2);
362
+ encTypeTag2 =
363
+ typeTagForId(methodMap2, customResult.funcName, arity2, info2, encLang2, g2) +
364
+ constTagForId(methodMap2, customResult.funcName, arity2, info2, g2);
365
+ }
366
+ }
367
+ }
368
+ }
369
+ const arityTag2 = arity2 !== undefined ? `#${arity2}${encTypeTag2}` : '';
370
+ const result = generateId(finalLabel, `${filePath}:${qualifiedName}${arityTag2}`);
219
371
  functionIdCache.set(node, result);
220
372
  return result;
221
373
  }
@@ -225,12 +377,12 @@ const findEnclosingFunctionId = (node, filePath, provider) => {
225
377
  functionIdCache.set(node, null);
226
378
  return null;
227
379
  };
228
- /** Cached wrapper for findEnclosingClassId — avoids repeated parent walks. */
229
- const cachedFindEnclosingClassId = (node, filePath) => {
380
+ /** Cached wrapper for findEnclosingClassInfo — avoids repeated parent walks. */
381
+ const cachedFindEnclosingClassInfo = (node, filePath, resolveEnclosingOwner) => {
230
382
  const cached = classIdCache.get(node);
231
383
  if (cached !== undefined)
232
384
  return cached;
233
- const result = findEnclosingClassId(node, filePath);
385
+ const result = findEnclosingClassInfo(node, filePath, resolveEnclosingOwner);
234
386
  classIdCache.set(node, result);
235
387
  return result;
236
388
  };
@@ -263,7 +415,7 @@ const processBatch = (files, onProgress) => {
263
415
  toolDefs: [],
264
416
  ormQueries: [],
265
417
  constructorBindings: [],
266
- typeEnvBindings: [],
418
+ fileScopeBindings: [],
267
419
  skippedLanguages: {},
268
420
  fileCount: 0,
269
421
  };
@@ -311,7 +463,10 @@ const processBatch = (files, onProgress) => {
311
463
  }
312
464
  }
313
465
  else {
314
- regularFiles.push(...langFiles);
466
+ // Manual loop (not spread) — `push(...arr)` blows the stack on very
467
+ // large arrays when langFiles has tens of thousands of entries.
468
+ for (const f of langFiles)
469
+ regularFiles.push(f);
315
470
  }
316
471
  // Process regular files for this language
317
472
  if (regularFiles.length > 0) {
@@ -375,6 +530,27 @@ const EXPRESS_ROUTE_METHODS = new Set([
375
530
  // the express_route handler as route definitions, not consumers. The fetch() global
376
531
  // function is captured separately by the route.fetch query.
377
532
  const HTTP_CLIENT_ONLY_METHODS = new Set(['head', 'options', 'request', 'ajax']);
533
+ // Known HTTP client receivers u2014 skip these, they're API consumers not routes
534
+ const HTTP_CLIENT_RECEIVERS = new Set([
535
+ 'axios',
536
+ 'request',
537
+ 'fetch',
538
+ 'http',
539
+ 'https',
540
+ 'got',
541
+ 'ky',
542
+ 'superagent',
543
+ 'needle',
544
+ 'undici',
545
+ 'apiclient',
546
+ 'client',
547
+ 'httpclient',
548
+ 'api',
549
+ '$http',
550
+ 'session',
551
+ 'httpservice',
552
+ 'conn',
553
+ ]);
378
554
  // Decorator names that indicate HTTP route handlers (NestJS, Flask, FastAPI, Spring)
379
555
  const ROUTE_DECORATOR_NAMES = new Set([
380
556
  'Get',
@@ -684,24 +860,28 @@ function extractLaravelRoutes(tree, filePath) {
684
860
  });
685
861
  }
686
862
  }
687
- function walk(node, groupStack) {
863
+ const walkStack = [{ node: tree.rootNode, groupSnapshot: [] }];
864
+ while (walkStack.length > 0) {
865
+ const { node, groupSnapshot } = walkStack.pop();
688
866
  // Case 1: Simple Route::get(...), Route::post(...), etc.
689
867
  if (isRouteStaticCall(node)) {
690
868
  const method = getCallMethodName(node);
691
869
  if (method && (ROUTE_HTTP_METHODS.has(method) || ROUTE_RESOURCE_METHODS.has(method))) {
692
- emitRoute(method, getArguments(node), node.startPosition.row, groupStack, []);
693
- return;
870
+ emitRoute(method, getArguments(node), node.startPosition.row, groupSnapshot, []);
871
+ continue;
694
872
  }
695
873
  if (method === 'group') {
696
874
  const argsNode = getArguments(node);
697
875
  const groupCtx = parseArrayGroupArgs(argsNode);
698
876
  const body = findClosureBody(argsNode);
699
877
  if (body) {
700
- groupStack.push(groupCtx);
701
- walkChildren(body, groupStack);
702
- groupStack.pop();
878
+ const childSnapshot = [...groupSnapshot, groupCtx];
879
+ const children = body.children ?? [];
880
+ for (let i = children.length - 1; i >= 0; i--) {
881
+ walkStack.push({ node: children[i], groupSnapshot: childSnapshot });
882
+ }
703
883
  }
704
- return;
884
+ continue;
705
885
  }
706
886
  }
707
887
  // Case 2: Fluent chain — Route::middleware(...)->group(...) or Route::middleware(...)->get(...)
@@ -719,27 +899,26 @@ function extractLaravelRoutes(tree, filePath) {
719
899
  }
720
900
  const body = findClosureBody(chain.terminalArgs);
721
901
  if (body) {
722
- groupStack.push(groupCtx);
723
- walkChildren(body, groupStack);
724
- groupStack.pop();
902
+ const childSnapshot = [...groupSnapshot, groupCtx];
903
+ const children = body.children ?? [];
904
+ for (let i = children.length - 1; i >= 0; i--) {
905
+ walkStack.push({ node: children[i], groupSnapshot: childSnapshot });
906
+ }
725
907
  }
726
- return;
908
+ continue;
727
909
  }
728
910
  if (ROUTE_HTTP_METHODS.has(chain.terminalMethod) ||
729
911
  ROUTE_RESOURCE_METHODS.has(chain.terminalMethod)) {
730
- emitRoute(chain.terminalMethod, chain.terminalArgs, node.startPosition.row, groupStack, chain.attributes);
731
- return;
912
+ emitRoute(chain.terminalMethod, chain.terminalArgs, node.startPosition.row, groupSnapshot, chain.attributes);
913
+ continue;
732
914
  }
733
915
  }
734
- // Default: recurse into children
735
- walkChildren(node, groupStack);
736
- }
737
- function walkChildren(node, groupStack) {
738
- for (const child of node.children ?? []) {
739
- walk(child, groupStack);
916
+ // Default: push children in reverse so leftmost is processed first
917
+ const children = node.children ?? [];
918
+ for (let i = children.length - 1; i >= 0; i--) {
919
+ walkStack.push({ node: children[i], groupSnapshot });
740
920
  }
741
921
  }
742
- walk(tree.rootNode, []);
743
922
  return routes;
744
923
  }
745
924
  // ============================================================================
@@ -806,11 +985,23 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
806
985
  // Skip files larger than the max tree-sitter buffer (32 MB)
807
986
  if (file.content.length > TREE_SITTER_MAX_BUFFER)
808
987
  continue;
988
+ // Vue SFC preprocessing: extract <script> block content
989
+ let parseContent = file.content;
990
+ let lineOffset = 0;
991
+ let isVueSetup = false;
992
+ if (language === SupportedLanguages.Vue) {
993
+ const extracted = extractVueScript(file.content);
994
+ if (!extracted)
995
+ continue; // skip .vue files with no script block
996
+ parseContent = extracted.scriptContent;
997
+ lineOffset = extracted.lineOffset;
998
+ isVueSetup = extracted.isSetup;
999
+ }
809
1000
  clearCaches(); // Reset memoization before each new file
810
1001
  let tree;
811
1002
  try {
812
- tree = parser.parse(file.content, undefined, {
813
- bufferSize: getTreeSitterBufferSize(file.content.length),
1003
+ tree = parser.parse(parseContent, undefined, {
1004
+ bufferSize: getTreeSitterBufferSize(parseContent.length),
814
1005
  });
815
1006
  }
816
1007
  catch (err) {
@@ -861,6 +1052,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
861
1052
  const typeEnv = buildTypeEnv(tree, language, {
862
1053
  parentMap,
863
1054
  enclosingFunctionFinder: provider?.enclosingFunctionFinder,
1055
+ extractFunctionName: provider?.methodExtractor?.extractFunctionName,
864
1056
  });
865
1057
  const callRouter = provider.callRouter;
866
1058
  if (typeEnv.constructorBindings.length > 0) {
@@ -869,15 +1061,23 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
869
1061
  bindings: [...typeEnv.constructorBindings],
870
1062
  });
871
1063
  }
872
- // Extract file-scope bindings for ExportedTypeMap (closes worker/sequential quality gap).
873
- // Sequential path uses collectExportedBindings(typeEnv) directly; worker path serializes
874
- // these bindings so the main thread can merge them into ExportedTypeMap.
1064
+ // Serialize file-scope bindings for BindingAccumulator. These feed the
1065
+ // ExportedTypeMap enrichment loop in pipeline.ts the only current
1066
+ // consumer of worker-path binding data.
1067
+ //
1068
+ // Historical note: we previously serialized all scopes
1069
+ // (`typeEnv.allScopes()`), which pushed ~4.9 MB of function-scope
1070
+ // bindings across the IPC boundary on every worker batch with zero
1071
+ // downstream readers. Narrowing to `fileScope()` recovers that cost.
1072
+ // See the `FileScopeBindings` JSDoc above for the Phase 9 reversion
1073
+ // path when a function-scope consumer lands.
875
1074
  const fileScope = typeEnv.fileScope();
876
1075
  if (fileScope.size > 0) {
877
- const bindings = [];
878
- for (const [name, type] of fileScope)
879
- bindings.push([name, type]);
880
- result.typeEnvBindings.push({ filePath: file.path, bindings });
1076
+ const scopeBindings = [];
1077
+ for (const [varName, typeName] of fileScope) {
1078
+ scopeBindings.push([varName, typeName]);
1079
+ }
1080
+ result.fileScopeBindings.push({ filePath: file.path, bindings: scopeBindings });
881
1081
  }
882
1082
  // Per-file map: decorator end-line → decorator info, for associating with definitions
883
1083
  const fileDecorators = new Map();
@@ -946,7 +1146,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
946
1146
  routePath,
947
1147
  httpMethod,
948
1148
  decoratorName,
949
- lineNumber: decoratorNode.startPosition.row,
1149
+ lineNumber: decoratorNode.startPosition.row + lineOffset,
950
1150
  });
951
1151
  }
952
1152
  // MCP/RPC tool detection: @mcp.tool(), @app.tool(), @server.tool()
@@ -967,7 +1167,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
967
1167
  result.fetchCalls.push({
968
1168
  filePath: file.path,
969
1169
  fetchURL: urlNode.text,
970
- lineNumber: captureMap['route.fetch'].startPosition.row,
1170
+ lineNumber: captureMap['route.fetch'].startPosition.row + lineOffset,
971
1171
  });
972
1172
  }
973
1173
  continue;
@@ -982,7 +1182,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
982
1182
  result.fetchCalls.push({
983
1183
  filePath: file.path,
984
1184
  fetchURL: url,
985
- lineNumber: captureMap['http_client'].startPosition.row,
1185
+ lineNumber: captureMap['http_client'].startPosition.row + lineOffset,
986
1186
  });
987
1187
  }
988
1188
  continue;
@@ -994,6 +1194,45 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
994
1194
  const method = captureMap['express_route.method'].text;
995
1195
  const routePath = captureMap['express_route.path'].text;
996
1196
  if (EXPRESS_ROUTE_METHODS.has(method) && routePath.startsWith('/')) {
1197
+ // Extract the receiver (the object the method is called on) to filter out
1198
+ // HTTP client calls like axios.get('/api/users') that match the same pattern
1199
+ // as Express route registrations.
1200
+ const callNode = captureMap['express_route'];
1201
+ const funcNode = callNode.childForFieldName?.('function') ?? callNode.children?.[0];
1202
+ // Walk through nested member_expressions and call_expressions to
1203
+ // reach the innermost receiver identifier. Handles chains like:
1204
+ // this.httpService.get('/path') -> member chain -> 'httpservice'
1205
+ // getClient().get('/path') -> call_expression -> 'getclient'
1206
+ // axios.get('/path') -> bare identifier -> 'axios'
1207
+ let receiverNode = funcNode?.childForFieldName?.('object') ?? funcNode?.children?.[0];
1208
+ while (receiverNode?.type === 'member_expression' ||
1209
+ receiverNode?.type === 'call_expression') {
1210
+ if (receiverNode.type === 'member_expression') {
1211
+ // Drill into the property (rightmost part) of the member expression
1212
+ const propNode = receiverNode.childForFieldName?.('property');
1213
+ if (propNode) {
1214
+ receiverNode = propNode;
1215
+ }
1216
+ else {
1217
+ break;
1218
+ }
1219
+ }
1220
+ else {
1221
+ // call_expression: unwrap to the function being called
1222
+ const innerFunc = receiverNode.childForFieldName?.('function') ?? receiverNode.children?.[0];
1223
+ if (innerFunc && innerFunc !== receiverNode) {
1224
+ receiverNode = innerFunc;
1225
+ }
1226
+ else {
1227
+ break;
1228
+ }
1229
+ }
1230
+ }
1231
+ const receiverText = receiverNode?.text?.toLowerCase() ?? '';
1232
+ if (HTTP_CLIENT_RECEIVERS.has(receiverText)) {
1233
+ // This is an HTTP client call, not a route definition u2014 skip it
1234
+ continue;
1235
+ }
997
1236
  const httpMethod = method === 'all' || method === 'use' || method === 'route'
998
1237
  ? 'GET'
999
1238
  : method.toUpperCase();
@@ -1002,7 +1241,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1002
1241
  routePath,
1003
1242
  httpMethod,
1004
1243
  decoratorName: `express.${method}`,
1005
- lineNumber: captureMap['express_route'].startPosition.row,
1244
+ lineNumber: captureMap['express_route'].startPosition.row + lineOffset,
1006
1245
  });
1007
1246
  }
1008
1247
  continue;
@@ -1069,7 +1308,8 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1069
1308
  continue;
1070
1309
  }
1071
1310
  if (routed.kind === 'properties') {
1072
- const propEnclosingClassId = cachedFindEnclosingClassId(captureMap['call'], file.path);
1311
+ const propEnclosingInfo = cachedFindEnclosingClassInfo(captureMap['call'], file.path, provider.resolveEnclosingOwner);
1312
+ const propEnclosingClassId = propEnclosingInfo?.classId ?? null;
1073
1313
  // Enrich routed properties with FieldExtractor metadata
1074
1314
  let routedFieldMap;
1075
1315
  if (provider.fieldExtractor && typeEnv) {
@@ -1085,7 +1325,10 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1085
1325
  }
1086
1326
  for (const item of routed.items) {
1087
1327
  const routedFieldInfo = routedFieldMap?.get(item.propName);
1088
- const nodeId = generateId('Property', `${file.path}:${item.propName}`);
1328
+ const propQualifiedName = propEnclosingInfo
1329
+ ? `${propEnclosingInfo.className}.${item.propName}`
1330
+ : item.propName;
1331
+ const nodeId = generateId('Property', `${file.path}:${propQualifiedName}`);
1089
1332
  result.nodes.push({
1090
1333
  id: nodeId,
1091
1334
  label: 'Property',
@@ -1250,25 +1493,119 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1250
1493
  continue;
1251
1494
  }
1252
1495
  }
1253
- const nodeLabel = getLabelFromCaptures(captureMap, provider);
1254
- if (!nodeLabel)
1496
+ const definitionNode = getDefinitionNodeFromCaptures(captureMap);
1497
+ const defaultNodeLabel = getLabelFromCaptures(captureMap, provider);
1498
+ if (!defaultNodeLabel)
1255
1499
  continue;
1256
1500
  const nameNode = captureMap['name'];
1501
+ const extractedClassSymbol = definitionNode && provider.classExtractor?.isTypeDeclaration(definitionNode)
1502
+ ? provider.classExtractor.extract(definitionNode, {
1503
+ name: nameNode?.text,
1504
+ type: defaultNodeLabel,
1505
+ })
1506
+ : null;
1507
+ const nodeLabel = extractedClassSymbol?.type ?? defaultNodeLabel;
1257
1508
  // Synthesize name for constructors without explicit @name capture (e.g. Swift init)
1258
- if (!nameNode && nodeLabel !== 'Constructor')
1509
+ if (!nameNode && nodeLabel !== 'Constructor' && !extractedClassSymbol)
1259
1510
  continue;
1260
- const nodeName = nameNode ? nameNode.text : 'init';
1261
- const definitionNode = getDefinitionNodeFromCaptures(captureMap);
1511
+ const nodeName = extractedClassSymbol?.name ?? (nameNode ? nameNode.text : 'init');
1262
1512
  const startLine = definitionNode
1263
- ? definitionNode.startPosition.row
1513
+ ? definitionNode.startPosition.row + lineOffset
1264
1514
  : nameNode
1265
- ? nameNode.startPosition.row
1266
- : 0;
1267
- const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}`);
1515
+ ? nameNode.startPosition.row + lineOffset
1516
+ : lineOffset;
1517
+ // Compute enclosing class BEFORE node ID — needed to qualify method IDs
1518
+ const needsOwner = nodeLabel === 'Method' ||
1519
+ nodeLabel === 'Constructor' ||
1520
+ nodeLabel === 'Property' ||
1521
+ nodeLabel === 'Function';
1522
+ const enclosingClassInfo = needsOwner
1523
+ ? cachedFindEnclosingClassInfo(nameNode || definitionNode, file.path, provider.resolveEnclosingOwner)
1524
+ : null;
1525
+ const enclosingClassId = enclosingClassInfo?.classId ?? null;
1526
+ // Qualify method/property IDs with enclosing class name to avoid collisions
1527
+ const qualifiedName = enclosingClassInfo
1528
+ ? `${enclosingClassInfo.className}.${nodeName}`
1529
+ : nodeName;
1530
+ // Extract method metadata BEFORE generating node ID — parameterCount is needed
1531
+ // to disambiguate overloaded methods via #<arity> suffix in the ID.
1532
+ let declaredType;
1533
+ let methodProps = {};
1534
+ let arityForId; // raw param count for ID, even for variadic
1535
+ let defMethodMap;
1536
+ let defMethodInfo;
1537
+ if (nodeLabel === 'Function' || nodeLabel === 'Method' || nodeLabel === 'Constructor') {
1538
+ // Use MethodExtractor for method metadata — provides parameterCount, parameterTypes,
1539
+ // returnType, isAbstract/isFinal/annotations, visibility, and more.
1540
+ let enrichedByMethodExtractor = false;
1541
+ if (provider.methodExtractor && definitionNode) {
1542
+ const classNode = findEnclosingClassNode(definitionNode) ?? findClassNodeByQualifiedName(definitionNode);
1543
+ if (classNode) {
1544
+ const methodMap = getMethodInfo(classNode, provider, {
1545
+ filePath: file.path,
1546
+ language,
1547
+ });
1548
+ const defLine = definitionNode.startPosition.row + 1;
1549
+ const info = methodMap?.get(`${nodeName}:${defLine}`);
1550
+ if (info) {
1551
+ enrichedByMethodExtractor = true;
1552
+ arityForId = arityForIdFromInfo(info);
1553
+ methodProps = buildMethodProps(info);
1554
+ defMethodMap = methodMap;
1555
+ defMethodInfo = info;
1556
+ }
1557
+ }
1558
+ }
1559
+ // For top-level methods (e.g. Go method_declaration), try extractFromNode
1560
+ if (!enrichedByMethodExtractor &&
1561
+ provider.methodExtractor?.extractFromNode &&
1562
+ definitionNode) {
1563
+ const info = provider.methodExtractor.extractFromNode(definitionNode, {
1564
+ filePath: file.path,
1565
+ language,
1566
+ });
1567
+ if (info) {
1568
+ enrichedByMethodExtractor = true;
1569
+ arityForId = arityForIdFromInfo(info);
1570
+ methodProps = buildMethodProps(info);
1571
+ }
1572
+ }
1573
+ }
1574
+ // Append #<paramCount> to Method/Constructor IDs to disambiguate overloads.
1575
+ // Functions are not suffixed — they don't overload by name in the same scope.
1576
+ // When same-arity collisions exist, append ~type1,type2 for further disambiguation.
1577
+ const needsAritySuffix = nodeLabel === 'Method' || nodeLabel === 'Constructor';
1578
+ let arityTag = needsAritySuffix && arityForId !== undefined ? `#${arityForId}` : '';
1579
+ if (arityTag && defMethodMap && defMethodInfo) {
1580
+ const groups = buildCollisionGroups(defMethodMap);
1581
+ arityTag += typeTagForId(defMethodMap, nodeName, arityForId, defMethodInfo, language, groups);
1582
+ arityTag += constTagForId(defMethodMap, nodeName, arityForId, defMethodInfo, groups);
1583
+ }
1584
+ const nodeId = generateId(nodeLabel, `${file.path}:${qualifiedName}${arityTag}`);
1585
+ const classNodeForSymbol = definitionNode || nameNode;
1586
+ const qualifiedTypeName = extractedClassSymbol?.qualifiedName ??
1587
+ (classNodeForSymbol && provider.classExtractor?.isTypeDeclaration(classNodeForSymbol)
1588
+ ? (provider.classExtractor.extractQualifiedName(classNodeForSymbol, nodeName) ?? nodeName)
1589
+ : undefined);
1268
1590
  const description = provider.descriptionExtractor?.(nodeLabel, nodeName, captureMap);
1269
1591
  let frameworkHint = definitionNode
1270
1592
  ? detectFrameworkFromAST(language, (definitionNode.text || '').slice(0, 300))
1271
1593
  : null;
1594
+ // Suppress Spring framework hint for methods inside interfaces
1595
+ // (Feign clients, JAX-RS proxies are consumers, not providers)
1596
+ if (frameworkHint && definitionNode) {
1597
+ let classCheck = definitionNode.parent;
1598
+ while (classCheck) {
1599
+ if (classCheck.type === 'interface_declaration') {
1600
+ frameworkHint = null;
1601
+ break;
1602
+ }
1603
+ if (classCheck.type === 'class_declaration' || classCheck.type === 'program') {
1604
+ break;
1605
+ }
1606
+ classCheck = classCheck.parent;
1607
+ }
1608
+ }
1272
1609
  // Decorators appear on lines immediately before their definition; allow up to
1273
1610
  // MAX_DECORATOR_SCAN_LINES gap for blank lines / multi-line decorator stacks.
1274
1611
  const MAX_DECORATOR_SCAN_LINES = 5;
@@ -1291,94 +1628,15 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1291
1628
  filePath: file.path,
1292
1629
  toolName: nodeName,
1293
1630
  description: dec.arg || '',
1294
- lineNumber: definitionNode.startPosition.row,
1631
+ lineNumber: definitionNode.startPosition.row + lineOffset,
1295
1632
  });
1296
1633
  }
1297
1634
  fileDecorators.delete(checkLine);
1298
1635
  }
1299
1636
  }
1300
1637
  }
1301
- let parameterCount;
1302
- let requiredParameterCount;
1303
- let parameterTypes;
1304
- let returnType;
1305
- let declaredType;
1306
- let visibility;
1307
- let isStatic;
1308
- let isReadonly;
1309
- let isAbstract;
1310
- let isFinal;
1311
- let isVirtual;
1312
- let isOverride;
1313
- let isAsync;
1314
- let isPartial;
1315
- let annotations;
1316
- if (nodeLabel === 'Function' || nodeLabel === 'Method' || nodeLabel === 'Constructor') {
1317
- // Try MethodExtractor first — it provides everything extractMethodSignature does, plus
1318
- // isAbstract/isFinal/annotations. Only fall back to extractMethodSignature when no
1319
- // MethodExtractor is available or the method isn't inside a class body.
1320
- let enrichedByMethodExtractor = false;
1321
- if (provider.methodExtractor && definitionNode) {
1322
- const classNode = findEnclosingClassNode(definitionNode);
1323
- if (classNode) {
1324
- const methodMap = getMethodInfo(classNode, provider, {
1325
- filePath: file.path,
1326
- language,
1327
- });
1328
- const defLine = definitionNode.startPosition.row + 1;
1329
- const info = methodMap?.get(`${nodeName}:${defLine}`);
1330
- if (info) {
1331
- enrichedByMethodExtractor = true;
1332
- parameterCount = info.parameters.length;
1333
- const types = [];
1334
- let optionalCount = 0;
1335
- for (const p of info.parameters) {
1336
- if (p.type !== null)
1337
- types.push(p.type);
1338
- if (p.isOptional)
1339
- optionalCount++;
1340
- }
1341
- parameterTypes = types.length > 0 ? types : undefined;
1342
- requiredParameterCount =
1343
- optionalCount > 0 ? parameterCount - optionalCount : undefined;
1344
- returnType = info.returnType ?? undefined;
1345
- visibility = info.visibility;
1346
- isStatic = info.isStatic;
1347
- isAbstract = info.isAbstract;
1348
- isFinal = info.isFinal;
1349
- if (info.isVirtual)
1350
- isVirtual = info.isVirtual;
1351
- if (info.isOverride)
1352
- isOverride = info.isOverride;
1353
- if (info.isAsync)
1354
- isAsync = info.isAsync;
1355
- if (info.isPartial)
1356
- isPartial = info.isPartial;
1357
- if (info.annotations.length > 0)
1358
- annotations = info.annotations;
1359
- }
1360
- }
1361
- }
1362
- if (!enrichedByMethodExtractor) {
1363
- const sig = extractMethodSignature(definitionNode);
1364
- parameterCount = sig.parameterCount;
1365
- requiredParameterCount = sig.requiredParameterCount;
1366
- parameterTypes = sig.parameterTypes;
1367
- returnType = sig.returnType;
1368
- }
1369
- // Language-specific return type fallback (e.g. Ruby YARD @return [Type])
1370
- // Also upgrades uninformative AST types like PHP `array` with PHPDoc `@return User[]`
1371
- if ((!returnType || returnType === 'array' || returnType === 'iterable') &&
1372
- definitionNode) {
1373
- const tc = provider.typeConfig;
1374
- if (tc?.extractReturnType) {
1375
- const docReturn = tc.extractReturnType(definitionNode);
1376
- if (docReturn)
1377
- returnType = docReturn;
1378
- }
1379
- }
1380
- }
1381
- else if (nodeLabel === 'Property' && definitionNode) {
1638
+ // Property metadata extraction (not needed before nodeId — Properties don't overload)
1639
+ if (nodeLabel === 'Property' && definitionNode) {
1382
1640
  // FieldExtractor is the single source of truth when available
1383
1641
  if (provider.fieldExtractor && typeEnv) {
1384
1642
  const classNode = findEnclosingClassNode(definitionNode);
@@ -1392,9 +1650,9 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1392
1650
  const info = fieldMap?.get(nodeName);
1393
1651
  if (info) {
1394
1652
  declaredType = info.type ?? undefined;
1395
- visibility = info.visibility;
1396
- isStatic = info.isStatic;
1397
- isReadonly = info.isReadonly;
1653
+ methodProps.visibility = info.visibility;
1654
+ methodProps.isStatic = info.isStatic;
1655
+ methodProps.isReadonly = info.isReadonly;
1398
1656
  }
1399
1657
  }
1400
1658
  }
@@ -1405,10 +1663,13 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1405
1663
  properties: {
1406
1664
  name: nodeName,
1407
1665
  filePath: file.path,
1408
- startLine: definitionNode ? definitionNode.startPosition.row : startLine,
1409
- endLine: definitionNode ? definitionNode.endPosition.row : startLine,
1666
+ startLine: definitionNode ? definitionNode.startPosition.row + lineOffset : startLine,
1667
+ endLine: definitionNode ? definitionNode.endPosition.row + lineOffset : startLine,
1410
1668
  language: language,
1411
- isExported: cachedExportCheck(provider.exportChecker, nameNode || definitionNode, nodeName),
1669
+ isExported: language === SupportedLanguages.Vue && isVueSetup
1670
+ ? isVueSetupTopLevel(nameNode || definitionNode)
1671
+ : cachedExportCheck(provider.exportChecker, nameNode || definitionNode, nodeName),
1672
+ ...(qualifiedTypeName !== undefined ? { qualifiedName: qualifiedTypeName } : {}),
1412
1673
  ...(frameworkHint
1413
1674
  ? {
1414
1675
  astFrameworkMultiplier: frameworkHint.entryPointMultiplier,
@@ -1416,53 +1677,41 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1416
1677
  }
1417
1678
  : {}),
1418
1679
  ...(description !== undefined ? { description } : {}),
1419
- ...(parameterCount !== undefined ? { parameterCount } : {}),
1420
- ...(requiredParameterCount !== undefined ? { requiredParameterCount } : {}),
1421
- ...(parameterTypes !== undefined ? { parameterTypes } : {}),
1422
- ...(returnType !== undefined ? { returnType } : {}),
1680
+ ...methodProps,
1423
1681
  ...(declaredType !== undefined ? { declaredType } : {}),
1424
- ...(visibility !== undefined ? { visibility } : {}),
1425
- ...(isStatic !== undefined ? { isStatic } : {}),
1426
- ...(isReadonly !== undefined ? { isReadonly } : {}),
1427
- ...(isAbstract !== undefined ? { isAbstract } : {}),
1428
- ...(isFinal !== undefined ? { isFinal } : {}),
1429
- ...(isVirtual !== undefined ? { isVirtual } : {}),
1430
- ...(isOverride !== undefined ? { isOverride } : {}),
1431
- ...(isAsync !== undefined ? { isAsync } : {}),
1432
- ...(isPartial !== undefined ? { isPartial } : {}),
1433
- ...(annotations !== undefined ? { annotations } : {}),
1434
1682
  },
1435
1683
  });
1436
- // Compute enclosing class for Method/Constructor/Property/Function used for both ownerId and HAS_METHOD
1437
- // Function is included because Kotlin/Rust/Python capture class methods as Function nodes
1438
- const needsOwner = nodeLabel === 'Method' ||
1439
- nodeLabel === 'Constructor' ||
1440
- nodeLabel === 'Property' ||
1441
- nodeLabel === 'Function';
1442
- const enclosingClassId = needsOwner
1443
- ? cachedFindEnclosingClassId(nameNode || definitionNode, file.path)
1444
- : null;
1684
+ // enclosingClassId already computed above (before nodeId generation)
1445
1685
  result.symbols.push({
1446
1686
  filePath: file.path,
1447
1687
  name: nodeName,
1448
1688
  nodeId,
1449
1689
  type: nodeLabel,
1450
- ...(parameterCount !== undefined ? { parameterCount } : {}),
1451
- ...(requiredParameterCount !== undefined ? { requiredParameterCount } : {}),
1452
- ...(parameterTypes !== undefined ? { parameterTypes } : {}),
1453
- ...(returnType !== undefined ? { returnType } : {}),
1690
+ ...(qualifiedTypeName !== undefined ? { qualifiedName: qualifiedTypeName } : {}),
1691
+ parameterCount: methodProps.parameterCount,
1692
+ requiredParameterCount: methodProps.requiredParameterCount,
1693
+ parameterTypes: methodProps.parameterTypes,
1694
+ returnType: methodProps.returnType,
1454
1695
  ...(declaredType !== undefined ? { declaredType } : {}),
1455
1696
  ...(enclosingClassId ? { ownerId: enclosingClassId } : {}),
1456
- ...(visibility !== undefined ? { visibility } : {}),
1457
- ...(isStatic !== undefined ? { isStatic } : {}),
1458
- ...(isReadonly !== undefined ? { isReadonly } : {}),
1459
- ...(isAbstract !== undefined ? { isAbstract } : {}),
1460
- ...(isFinal !== undefined ? { isFinal } : {}),
1461
- ...(isVirtual !== undefined ? { isVirtual } : {}),
1462
- ...(isOverride !== undefined ? { isOverride } : {}),
1463
- ...(isAsync !== undefined ? { isAsync } : {}),
1464
- ...(isPartial !== undefined ? { isPartial } : {}),
1465
- ...(annotations !== undefined ? { annotations } : {}),
1697
+ visibility: methodProps.visibility,
1698
+ isStatic: methodProps.isStatic,
1699
+ isReadonly: methodProps.isReadonly,
1700
+ isAbstract: methodProps.isAbstract,
1701
+ isFinal: methodProps.isFinal,
1702
+ ...(methodProps.isVirtual !== undefined
1703
+ ? { isVirtual: methodProps.isVirtual }
1704
+ : {}),
1705
+ ...(methodProps.isOverride !== undefined
1706
+ ? { isOverride: methodProps.isOverride }
1707
+ : {}),
1708
+ ...(methodProps.isAsync !== undefined ? { isAsync: methodProps.isAsync } : {}),
1709
+ ...(methodProps.isPartial !== undefined
1710
+ ? { isPartial: methodProps.isPartial }
1711
+ : {}),
1712
+ ...(methodProps.annotations !== undefined
1713
+ ? { annotations: methodProps.annotations }
1714
+ : {}),
1466
1715
  });
1467
1716
  const fileId = generateId('File', file.path);
1468
1717
  const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
@@ -1490,10 +1739,23 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1490
1739
  // Extract framework routes via provider detection (e.g., Laravel routes.php)
1491
1740
  if (provider.isRouteFile?.(file.path)) {
1492
1741
  const extractedRoutes = extractLaravelRoutes(tree, file.path);
1493
- result.routes.push(...extractedRoutes);
1742
+ for (const r of extractedRoutes)
1743
+ result.routes.push(r);
1494
1744
  }
1495
1745
  // Extract ORM queries (Prisma, Supabase)
1496
- extractORMQueries(file.path, file.content, result.ormQueries);
1746
+ extractORMQueries(file.path, parseContent, result.ormQueries);
1747
+ // Vue: emit CALLS edges for components used in <template>
1748
+ if (language === SupportedLanguages.Vue) {
1749
+ const templateComponents = extractTemplateComponents(file.content);
1750
+ for (const componentName of templateComponents) {
1751
+ result.calls.push({
1752
+ filePath: file.path,
1753
+ calledName: componentName,
1754
+ sourceId: generateId('File', file.path),
1755
+ callForm: 'free',
1756
+ });
1757
+ }
1758
+ }
1497
1759
  }
1498
1760
  };
1499
1761
  // ============================================================================
@@ -1514,26 +1776,33 @@ let accumulated = {
1514
1776
  toolDefs: [],
1515
1777
  ormQueries: [],
1516
1778
  constructorBindings: [],
1517
- typeEnvBindings: [],
1779
+ fileScopeBindings: [],
1518
1780
  skippedLanguages: {},
1519
1781
  fileCount: 0,
1520
1782
  };
1521
1783
  let cumulativeProcessed = 0;
1784
+ // Use a loop instead of push(...spread) to avoid hitting V8's argument limit
1785
+ // when merging large result sets (push(...arr) calls apply() under the hood
1786
+ // and blows the stack when arr has >~65k elements).
1787
+ const appendAll = (target, src) => {
1788
+ for (let i = 0; i < src.length; i++)
1789
+ target.push(src[i]);
1790
+ };
1522
1791
  const mergeResult = (target, src) => {
1523
- target.nodes.push(...src.nodes);
1524
- target.relationships.push(...src.relationships);
1525
- target.symbols.push(...src.symbols);
1526
- target.imports.push(...src.imports);
1527
- target.calls.push(...src.calls);
1528
- target.assignments.push(...src.assignments);
1529
- target.heritage.push(...src.heritage);
1530
- target.routes.push(...src.routes);
1531
- target.fetchCalls.push(...src.fetchCalls);
1532
- target.decoratorRoutes.push(...src.decoratorRoutes);
1533
- target.toolDefs.push(...src.toolDefs);
1534
- target.ormQueries.push(...src.ormQueries);
1535
- target.constructorBindings.push(...src.constructorBindings);
1536
- target.typeEnvBindings.push(...src.typeEnvBindings);
1792
+ appendAll(target.nodes, src.nodes);
1793
+ appendAll(target.relationships, src.relationships);
1794
+ appendAll(target.symbols, src.symbols);
1795
+ appendAll(target.imports, src.imports);
1796
+ appendAll(target.calls, src.calls);
1797
+ appendAll(target.assignments, src.assignments);
1798
+ appendAll(target.heritage, src.heritage);
1799
+ appendAll(target.routes, src.routes);
1800
+ appendAll(target.fetchCalls, src.fetchCalls);
1801
+ appendAll(target.decoratorRoutes, src.decoratorRoutes);
1802
+ appendAll(target.toolDefs, src.toolDefs);
1803
+ appendAll(target.ormQueries, src.ormQueries);
1804
+ appendAll(target.constructorBindings, src.constructorBindings);
1805
+ appendAll(target.fileScopeBindings, src.fileScopeBindings);
1537
1806
  for (const [lang, count] of Object.entries(src.skippedLanguages)) {
1538
1807
  target.skippedLanguages[lang] = (target.skippedLanguages[lang] || 0) + count;
1539
1808
  }
@@ -1581,7 +1850,7 @@ parentPort.on('message', (msg) => {
1581
1850
  toolDefs: [],
1582
1851
  ormQueries: [],
1583
1852
  constructorBindings: [],
1584
- typeEnvBindings: [],
1853
+ fileScopeBindings: [],
1585
1854
  skippedLanguages: {},
1586
1855
  fileCount: 0,
1587
1856
  };