@optave/codegraph 3.10.0 → 3.11.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 (312) hide show
  1. package/README.md +40 -33
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +91 -60
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/rules/index.d.ts.map +1 -1
  6. package/dist/ast-analysis/rules/index.js +77 -0
  7. package/dist/ast-analysis/rules/index.js.map +1 -1
  8. package/dist/ast-analysis/visitor-utils.d.ts +3 -0
  9. package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
  10. package/dist/ast-analysis/visitor-utils.js +83 -49
  11. package/dist/ast-analysis/visitor-utils.js.map +1 -1
  12. package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
  13. package/dist/ast-analysis/visitors/ast-store-visitor.js +78 -62
  14. package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
  15. package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
  16. package/dist/ast-analysis/visitors/dataflow-visitor.js +61 -42
  17. package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
  18. package/dist/cli/commands/audit.js +1 -1
  19. package/dist/cli/commands/audit.js.map +1 -1
  20. package/dist/cli/commands/build.d.ts.map +1 -1
  21. package/dist/cli/commands/build.js +2 -0
  22. package/dist/cli/commands/build.js.map +1 -1
  23. package/dist/cli/commands/check.js +1 -1
  24. package/dist/cli/commands/check.js.map +1 -1
  25. package/dist/cli/commands/children.js +1 -1
  26. package/dist/cli/commands/children.js.map +1 -1
  27. package/dist/cli/commands/diff-impact.js +1 -1
  28. package/dist/cli/commands/diff-impact.js.map +1 -1
  29. package/dist/cli/commands/embed.d.ts.map +1 -1
  30. package/dist/cli/commands/embed.js +49 -4
  31. package/dist/cli/commands/embed.js.map +1 -1
  32. package/dist/cli/commands/roles.js +1 -1
  33. package/dist/cli/commands/roles.js.map +1 -1
  34. package/dist/cli/commands/structure.js +1 -1
  35. package/dist/cli/commands/structure.js.map +1 -1
  36. package/dist/cli/shared/options.js +1 -1
  37. package/dist/cli/shared/options.js.map +1 -1
  38. package/dist/db/connection.d.ts.map +1 -1
  39. package/dist/db/connection.js +8 -0
  40. package/dist/db/connection.js.map +1 -1
  41. package/dist/domain/analysis/dependencies.d.ts.map +1 -1
  42. package/dist/domain/analysis/dependencies.js +106 -80
  43. package/dist/domain/analysis/dependencies.js.map +1 -1
  44. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  45. package/dist/domain/analysis/fn-impact.js +77 -52
  46. package/dist/domain/analysis/fn-impact.js.map +1 -1
  47. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  48. package/dist/domain/analysis/module-map.js +132 -121
  49. package/dist/domain/analysis/module-map.js.map +1 -1
  50. package/dist/domain/graph/builder/helpers.d.ts +4 -4
  51. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  52. package/dist/domain/graph/builder/helpers.js +47 -33
  53. package/dist/domain/graph/builder/helpers.js.map +1 -1
  54. package/dist/domain/graph/builder/incremental.d.ts +6 -6
  55. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  56. package/dist/domain/graph/builder/incremental.js +148 -99
  57. package/dist/domain/graph/builder/incremental.js.map +1 -1
  58. package/dist/domain/graph/builder/pipeline.d.ts +1 -0
  59. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  60. package/dist/domain/graph/builder/pipeline.js +23 -637
  61. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  62. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  63. package/dist/domain/graph/builder/stages/build-edges.js +141 -98
  64. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  65. package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
  66. package/dist/domain/graph/builder/stages/build-structure.js +82 -65
  67. package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
  68. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  69. package/dist/domain/graph/builder/stages/detect-changes.js +84 -56
  70. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  71. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  72. package/dist/domain/graph/builder/stages/finalize.js +60 -51
  73. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  74. package/dist/domain/graph/builder/stages/insert-nodes.d.ts +8 -6
  75. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  76. package/dist/domain/graph/builder/stages/insert-nodes.js +107 -122
  77. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  78. package/dist/domain/graph/builder/stages/native-db-lifecycle.d.ts +14 -0
  79. package/dist/domain/graph/builder/stages/native-db-lifecycle.d.ts.map +1 -0
  80. package/dist/domain/graph/builder/stages/native-db-lifecycle.js +77 -0
  81. package/dist/domain/graph/builder/stages/native-db-lifecycle.js.map +1 -0
  82. package/dist/domain/graph/builder/stages/native-orchestrator.d.ts +62 -0
  83. package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -0
  84. package/dist/domain/graph/builder/stages/native-orchestrator.js +747 -0
  85. package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -0
  86. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  87. package/dist/domain/graph/builder/stages/resolve-imports.js +73 -22
  88. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  89. package/dist/domain/graph/cycles.d.ts +6 -4
  90. package/dist/domain/graph/cycles.d.ts.map +1 -1
  91. package/dist/domain/graph/cycles.js +50 -55
  92. package/dist/domain/graph/cycles.js.map +1 -1
  93. package/dist/domain/graph/journal.d.ts.map +1 -1
  94. package/dist/domain/graph/journal.js +89 -70
  95. package/dist/domain/graph/journal.js.map +1 -1
  96. package/dist/domain/graph/watcher.d.ts.map +1 -1
  97. package/dist/domain/graph/watcher.js +28 -20
  98. package/dist/domain/graph/watcher.js.map +1 -1
  99. package/dist/domain/parser.d.ts +12 -23
  100. package/dist/domain/parser.d.ts.map +1 -1
  101. package/dist/domain/parser.js +153 -80
  102. package/dist/domain/parser.js.map +1 -1
  103. package/dist/domain/search/generator.d.ts +3 -1
  104. package/dist/domain/search/generator.d.ts.map +1 -1
  105. package/dist/domain/search/generator.js +68 -45
  106. package/dist/domain/search/generator.js.map +1 -1
  107. package/dist/domain/search/models.d.ts +18 -0
  108. package/dist/domain/search/models.d.ts.map +1 -1
  109. package/dist/domain/search/models.js +72 -4
  110. package/dist/domain/search/models.js.map +1 -1
  111. package/dist/domain/search/search/hybrid.d.ts.map +1 -1
  112. package/dist/domain/search/search/hybrid.js +49 -40
  113. package/dist/domain/search/search/hybrid.js.map +1 -1
  114. package/dist/domain/search/search/semantic.d.ts.map +1 -1
  115. package/dist/domain/search/search/semantic.js +69 -49
  116. package/dist/domain/search/search/semantic.js.map +1 -1
  117. package/dist/domain/wasm-worker-entry.js +209 -137
  118. package/dist/domain/wasm-worker-entry.js.map +1 -1
  119. package/dist/extractors/c.js +25 -6
  120. package/dist/extractors/c.js.map +1 -1
  121. package/dist/extractors/cpp.js +47 -6
  122. package/dist/extractors/cpp.js.map +1 -1
  123. package/dist/extractors/cuda.js +90 -14
  124. package/dist/extractors/cuda.js.map +1 -1
  125. package/dist/extractors/elixir.js +108 -4
  126. package/dist/extractors/elixir.js.map +1 -1
  127. package/dist/extractors/erlang.js +56 -20
  128. package/dist/extractors/erlang.js.map +1 -1
  129. package/dist/extractors/fsharp.d.ts +7 -0
  130. package/dist/extractors/fsharp.d.ts.map +1 -1
  131. package/dist/extractors/fsharp.js +94 -0
  132. package/dist/extractors/fsharp.js.map +1 -1
  133. package/dist/extractors/gleam.d.ts.map +1 -1
  134. package/dist/extractors/gleam.js +29 -33
  135. package/dist/extractors/gleam.js.map +1 -1
  136. package/dist/extractors/groovy.js +41 -1
  137. package/dist/extractors/groovy.js.map +1 -1
  138. package/dist/extractors/haskell.js +48 -4
  139. package/dist/extractors/haskell.js.map +1 -1
  140. package/dist/extractors/helpers.d.ts +79 -1
  141. package/dist/extractors/helpers.d.ts.map +1 -1
  142. package/dist/extractors/helpers.js +137 -0
  143. package/dist/extractors/helpers.js.map +1 -1
  144. package/dist/extractors/java.d.ts.map +1 -1
  145. package/dist/extractors/java.js +37 -49
  146. package/dist/extractors/java.js.map +1 -1
  147. package/dist/extractors/javascript.d.ts.map +1 -1
  148. package/dist/extractors/javascript.js +44 -44
  149. package/dist/extractors/javascript.js.map +1 -1
  150. package/dist/extractors/julia.js +198 -74
  151. package/dist/extractors/julia.js.map +1 -1
  152. package/dist/extractors/kotlin.js +4 -0
  153. package/dist/extractors/kotlin.js.map +1 -1
  154. package/dist/extractors/objc.js +184 -47
  155. package/dist/extractors/objc.js.map +1 -1
  156. package/dist/extractors/python.js +7 -4
  157. package/dist/extractors/python.js.map +1 -1
  158. package/dist/extractors/r.d.ts.map +1 -1
  159. package/dist/extractors/r.js +103 -87
  160. package/dist/extractors/r.js.map +1 -1
  161. package/dist/extractors/scala.d.ts.map +1 -1
  162. package/dist/extractors/scala.js +18 -32
  163. package/dist/extractors/scala.js.map +1 -1
  164. package/dist/extractors/solidity.d.ts.map +1 -1
  165. package/dist/extractors/solidity.js +55 -69
  166. package/dist/extractors/solidity.js.map +1 -1
  167. package/dist/extractors/verilog.js +80 -15
  168. package/dist/extractors/verilog.js.map +1 -1
  169. package/dist/features/boundaries.d.ts.map +1 -1
  170. package/dist/features/boundaries.js +49 -39
  171. package/dist/features/boundaries.js.map +1 -1
  172. package/dist/features/cfg.d.ts.map +1 -1
  173. package/dist/features/cfg.js +90 -63
  174. package/dist/features/cfg.js.map +1 -1
  175. package/dist/features/check.d.ts.map +1 -1
  176. package/dist/features/check.js +43 -34
  177. package/dist/features/check.js.map +1 -1
  178. package/dist/features/cochange.d.ts.map +1 -1
  179. package/dist/features/cochange.js +68 -56
  180. package/dist/features/cochange.js.map +1 -1
  181. package/dist/features/complexity.d.ts.map +1 -1
  182. package/dist/features/complexity.js +105 -75
  183. package/dist/features/complexity.js.map +1 -1
  184. package/dist/features/dataflow.d.ts.map +1 -1
  185. package/dist/features/dataflow.js +37 -29
  186. package/dist/features/dataflow.js.map +1 -1
  187. package/dist/features/flow.d.ts.map +1 -1
  188. package/dist/features/flow.js +31 -22
  189. package/dist/features/flow.js.map +1 -1
  190. package/dist/features/graph-enrichment.d.ts.map +1 -1
  191. package/dist/features/graph-enrichment.js +77 -70
  192. package/dist/features/graph-enrichment.js.map +1 -1
  193. package/dist/features/owners.d.ts +17 -26
  194. package/dist/features/owners.d.ts.map +1 -1
  195. package/dist/features/owners.js +120 -109
  196. package/dist/features/owners.js.map +1 -1
  197. package/dist/features/sequence.d.ts.map +1 -1
  198. package/dist/features/sequence.js +59 -54
  199. package/dist/features/sequence.js.map +1 -1
  200. package/dist/features/structure-query.d.ts.map +1 -1
  201. package/dist/features/structure-query.js +60 -60
  202. package/dist/features/structure-query.js.map +1 -1
  203. package/dist/features/structure.js +28 -36
  204. package/dist/features/structure.js.map +1 -1
  205. package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
  206. package/dist/graph/algorithms/leiden/optimiser.js +100 -69
  207. package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
  208. package/dist/graph/classifiers/roles.d.ts.map +1 -1
  209. package/dist/graph/classifiers/roles.js +63 -59
  210. package/dist/graph/classifiers/roles.js.map +1 -1
  211. package/dist/infrastructure/config.d.ts +1 -1
  212. package/dist/infrastructure/config.d.ts.map +1 -1
  213. package/dist/infrastructure/config.js +1 -1
  214. package/dist/infrastructure/config.js.map +1 -1
  215. package/dist/mcp/tool-registry.d.ts.map +1 -1
  216. package/dist/mcp/tool-registry.js +4 -0
  217. package/dist/mcp/tool-registry.js.map +1 -1
  218. package/dist/mcp/tools/semantic-search.d.ts +1 -0
  219. package/dist/mcp/tools/semantic-search.d.ts.map +1 -1
  220. package/dist/mcp/tools/semantic-search.js +1 -0
  221. package/dist/mcp/tools/semantic-search.js.map +1 -1
  222. package/dist/presentation/cfg.d.ts.map +1 -1
  223. package/dist/presentation/cfg.js +44 -29
  224. package/dist/presentation/cfg.js.map +1 -1
  225. package/dist/presentation/flow.d.ts.map +1 -1
  226. package/dist/presentation/flow.js +58 -38
  227. package/dist/presentation/flow.js.map +1 -1
  228. package/dist/types.d.ts +16 -2
  229. package/dist/types.d.ts.map +1 -1
  230. package/grammars/tree-sitter-erlang.wasm +0 -0
  231. package/grammars/tree-sitter-fsharp.wasm +0 -0
  232. package/grammars/tree-sitter-fsharp_signature.wasm +0 -0
  233. package/grammars/tree-sitter-gleam.wasm +0 -0
  234. package/package.json +10 -10
  235. package/src/ast-analysis/engine.ts +145 -61
  236. package/src/ast-analysis/rules/index.ts +87 -0
  237. package/src/ast-analysis/visitor-utils.ts +86 -46
  238. package/src/ast-analysis/visitors/ast-store-visitor.ts +104 -69
  239. package/src/ast-analysis/visitors/dataflow-visitor.ts +86 -47
  240. package/src/cli/commands/audit.ts +1 -1
  241. package/src/cli/commands/build.ts +2 -0
  242. package/src/cli/commands/check.ts +1 -1
  243. package/src/cli/commands/children.ts +1 -1
  244. package/src/cli/commands/diff-impact.ts +1 -1
  245. package/src/cli/commands/embed.ts +54 -4
  246. package/src/cli/commands/roles.ts +1 -1
  247. package/src/cli/commands/structure.ts +1 -1
  248. package/src/cli/shared/options.ts +1 -1
  249. package/src/db/connection.ts +8 -0
  250. package/src/domain/analysis/dependencies.ts +166 -85
  251. package/src/domain/analysis/fn-impact.ts +120 -50
  252. package/src/domain/analysis/module-map.ts +175 -140
  253. package/src/domain/graph/builder/helpers.ts +85 -76
  254. package/src/domain/graph/builder/incremental.ts +223 -131
  255. package/src/domain/graph/builder/pipeline.ts +32 -785
  256. package/src/domain/graph/builder/stages/build-edges.ts +207 -142
  257. package/src/domain/graph/builder/stages/build-structure.ts +115 -82
  258. package/src/domain/graph/builder/stages/detect-changes.ts +107 -64
  259. package/src/domain/graph/builder/stages/finalize.ts +72 -70
  260. package/src/domain/graph/builder/stages/insert-nodes.ts +154 -120
  261. package/src/domain/graph/builder/stages/native-db-lifecycle.ts +74 -0
  262. package/src/domain/graph/builder/stages/native-orchestrator.ts +942 -0
  263. package/src/domain/graph/builder/stages/resolve-imports.ts +79 -25
  264. package/src/domain/graph/cycles.ts +51 -49
  265. package/src/domain/graph/journal.ts +84 -69
  266. package/src/domain/graph/watcher.ts +29 -25
  267. package/src/domain/parser.ts +170 -67
  268. package/src/domain/search/generator.ts +132 -74
  269. package/src/domain/search/models.ts +75 -4
  270. package/src/domain/search/search/hybrid.ts +53 -42
  271. package/src/domain/search/search/semantic.ts +105 -65
  272. package/src/domain/wasm-worker-entry.ts +243 -153
  273. package/src/extractors/c.ts +27 -8
  274. package/src/extractors/cpp.ts +50 -8
  275. package/src/extractors/cuda.ts +90 -16
  276. package/src/extractors/elixir.ts +103 -4
  277. package/src/extractors/erlang.ts +63 -20
  278. package/src/extractors/fsharp.ts +104 -0
  279. package/src/extractors/gleam.ts +40 -39
  280. package/src/extractors/groovy.ts +45 -1
  281. package/src/extractors/haskell.ts +45 -4
  282. package/src/extractors/helpers.ts +205 -1
  283. package/src/extractors/java.ts +42 -45
  284. package/src/extractors/javascript.ts +44 -43
  285. package/src/extractors/julia.ts +191 -77
  286. package/src/extractors/kotlin.ts +4 -0
  287. package/src/extractors/objc.ts +171 -47
  288. package/src/extractors/python.ts +5 -3
  289. package/src/extractors/r.ts +104 -82
  290. package/src/extractors/scala.ts +24 -36
  291. package/src/extractors/solidity.ts +59 -78
  292. package/src/extractors/verilog.ts +83 -15
  293. package/src/features/boundaries.ts +64 -46
  294. package/src/features/cfg.ts +145 -74
  295. package/src/features/check.ts +60 -43
  296. package/src/features/cochange.ts +95 -72
  297. package/src/features/complexity.ts +134 -79
  298. package/src/features/dataflow.ts +57 -34
  299. package/src/features/flow.ts +48 -24
  300. package/src/features/graph-enrichment.ts +105 -70
  301. package/src/features/owners.ts +186 -146
  302. package/src/features/sequence.ts +99 -69
  303. package/src/features/structure-query.ts +94 -79
  304. package/src/features/structure.ts +56 -56
  305. package/src/graph/algorithms/leiden/optimiser.ts +142 -87
  306. package/src/graph/classifiers/roles.ts +64 -54
  307. package/src/infrastructure/config.ts +1 -1
  308. package/src/mcp/tool-registry.ts +5 -0
  309. package/src/mcp/tools/semantic-search.ts +2 -0
  310. package/src/presentation/cfg.ts +48 -32
  311. package/src/presentation/flow.ts +100 -52
  312. package/src/types.ts +16 -1
@@ -1,5 +1,6 @@
1
1
  import type {
2
2
  Call,
3
+ Definition,
3
4
  ExtractorOutput,
4
5
  SubDeclaration,
5
6
  TreeSitterNode,
@@ -59,52 +60,37 @@ function walkScalaNode(node: TreeSitterNode, ctx: ExtractorOutput): void {
59
60
  // ── Walk-path per-node-type handlers ────────────────────────────────────────
60
61
 
61
62
  function handleScalaClassDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
62
- const nameNode = node.childForFieldName('name');
63
- if (!nameNode) return;
64
- const name = nameNode.text;
65
- const children = extractScalaBodyMembers(node, name, ctx);
66
-
67
- ctx.definitions.push({
68
- name,
69
- kind: 'class',
70
- line: node.startPosition.row + 1,
71
- endLine: nodeEndLine(node),
72
- children: children.length > 0 ? children : undefined,
73
- });
74
-
75
- extractScalaInheritance(node, name, ctx);
63
+ emitScalaTypeDef(node, ctx, 'class');
76
64
  }
77
65
 
78
66
  function handleScalaTraitDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
79
- const nameNode = node.childForFieldName('name');
80
- if (!nameNode) return;
81
- const name = nameNode.text;
82
- const children = extractScalaBodyMembers(node, name, ctx);
83
-
84
- ctx.definitions.push({
85
- name,
86
- kind: 'interface',
87
- line: node.startPosition.row + 1,
88
- endLine: nodeEndLine(node),
89
- children: children.length > 0 ? children : undefined,
90
- });
91
-
92
- extractScalaInheritance(node, name, ctx);
67
+ emitScalaTypeDef(node, ctx, 'interface');
93
68
  }
94
69
 
95
70
  function handleScalaObjectDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
71
+ emitScalaTypeDef(node, ctx, 'class');
72
+ }
73
+
74
+ function emitScalaTypeDef(
75
+ node: TreeSitterNode,
76
+ ctx: ExtractorOutput,
77
+ kind: 'class' | 'interface',
78
+ ): void {
96
79
  const nameNode = node.childForFieldName('name');
97
80
  if (!nameNode) return;
98
81
  const name = nameNode.text;
99
- const children = extractScalaBodyMembers(node, name, ctx);
82
+ const { children, methods } = collectScalaBodyMembers(node, name);
100
83
 
84
+ // Push the type def before its methods so the definition order follows
85
+ // source-line order (matches native, which walks depth-first).
101
86
  ctx.definitions.push({
102
87
  name,
103
- kind: 'class',
88
+ kind,
104
89
  line: node.startPosition.row + 1,
105
90
  endLine: nodeEndLine(node),
106
91
  children: children.length > 0 ? children : undefined,
107
92
  });
93
+ for (const m of methods) ctx.definitions.push(m);
108
94
 
109
95
  extractScalaInheritance(node, name, ctx);
110
96
  }
@@ -224,14 +210,14 @@ function extractScalaInheritance(node: TreeSitterNode, name: string, ctx: Extrac
224
210
 
225
211
  // ── Body member extraction ──────────────────────────────────────────────────
226
212
 
227
- function extractScalaBodyMembers(
213
+ function collectScalaBodyMembers(
228
214
  parentNode: TreeSitterNode,
229
215
  parentName: string,
230
- ctx: ExtractorOutput,
231
- ): SubDeclaration[] {
216
+ ): { children: SubDeclaration[]; methods: Definition[] } {
232
217
  const children: SubDeclaration[] = [];
218
+ const methods: Definition[] = [];
233
219
  const body = findChild(parentNode, 'template_body');
234
- if (!body) return children;
220
+ if (!body) return { children, methods };
235
221
 
236
222
  for (let i = 0; i < body.childCount; i++) {
237
223
  const member = body.child(i);
@@ -240,12 +226,14 @@ function extractScalaBodyMembers(
240
226
  if (member.type === 'function_definition') {
241
227
  const methName = member.childForFieldName('name');
242
228
  if (methName) {
243
- ctx.definitions.push({
229
+ const params = extractScalaParameters(member);
230
+ methods.push({
244
231
  name: `${parentName}.${methName.text}`,
245
232
  kind: 'method',
246
233
  line: member.startPosition.row + 1,
247
234
  endLine: member.endPosition.row + 1,
248
235
  visibility: extractModifierVisibility(member),
236
+ children: params.length > 0 ? params : undefined,
249
237
  });
250
238
  }
251
239
  } else if (member.type === 'val_definition' || member.type === 'var_definition') {
@@ -264,7 +252,7 @@ function extractScalaBodyMembers(
264
252
  }
265
253
  }
266
254
 
267
- return children;
255
+ return { children, methods };
268
256
  }
269
257
 
270
258
  // ── Parameter extraction ────────────────────────────────────────────────────
@@ -1,15 +1,14 @@
1
- import type {
2
- Call,
3
- ExtractorOutput,
4
- SubDeclaration,
5
- TreeSitterNode,
6
- TreeSitterTree,
7
- } from '../types.js';
1
+ import type { ExtractorOutput, SubDeclaration, TreeSitterNode, TreeSitterTree } from '../types.js';
8
2
  import {
9
3
  extractModifierVisibility,
4
+ extractSimpleParameters,
10
5
  findChild,
6
+ findFirstChildOfTypes,
11
7
  findParentNode,
12
8
  nodeEndLine,
9
+ nodeStartLine,
10
+ pushCall,
11
+ pushImport,
13
12
  stripQuotes,
14
13
  } from './helpers.js';
15
14
 
@@ -103,7 +102,7 @@ function handleContractDecl(
103
102
  ctx.definitions.push({
104
103
  name,
105
104
  kind,
106
- line: node.startPosition.row + 1,
105
+ line: nodeStartLine(node),
107
106
  endLine: nodeEndLine(node),
108
107
  children: members.length > 0 ? members : undefined,
109
108
  });
@@ -125,7 +124,7 @@ function extractContractMembers(body: TreeSitterNode): SubDeclaration[] {
125
124
 
126
125
  /** Map a single contract body child to a SubDeclaration, or null if not a recognized member. */
127
126
  function extractContractMember(child: TreeSitterNode): SubDeclaration | null {
128
- const line = child.startPosition.row + 1;
127
+ const line = nodeStartLine(child);
129
128
  switch (child.type) {
130
129
  case 'function_definition': {
131
130
  const fnName = child.childForFieldName('name');
@@ -156,15 +155,24 @@ function extractContractMember(child: TreeSitterNode): SubDeclaration | null {
156
155
  }
157
156
  }
158
157
 
159
- /** Extract inheritance (extends) relationships from a contract node. */
158
+ /**
159
+ * Extract inheritance (extends) relationships from a contract node.
160
+ *
161
+ * Each parent in `contract A is B, C, D { }` is its own `inheritance_specifier`
162
+ * sibling under the contract node (see tree-sitter-solidity grammar:
163
+ * `_class_heritage: "is" commaSep1($.inheritance_specifier)`), so we must walk
164
+ * all direct children rather than stopping at the first match.
165
+ */
160
166
  function extractInheritance(node: TreeSitterNode, name: string, ctx: ExtractorOutput): void {
161
- const inheritance = findChild(node, 'inheritance_specifier');
162
- if (!inheritance) return;
163
- for (let i = 0; i < inheritance.childCount; i++) {
164
- const child = inheritance.child(i);
165
- if (!child) continue;
166
- if (child.type === 'user_defined_type' || child.type === 'identifier') {
167
- ctx.classes.push({ name, extends: child.text, line: node.startPosition.row + 1 });
167
+ for (let i = 0; i < node.childCount; i++) {
168
+ const inheritance = node.child(i);
169
+ if (!inheritance || inheritance.type !== 'inheritance_specifier') continue;
170
+ for (let j = 0; j < inheritance.childCount; j++) {
171
+ const child = inheritance.child(j);
172
+ if (!child) continue;
173
+ if (child.type === 'user_defined_type' || child.type === 'identifier') {
174
+ ctx.classes.push({ name, extends: child.text, line: nodeStartLine(node) });
175
+ }
168
176
  }
169
177
  }
170
178
  }
@@ -182,19 +190,16 @@ function handleStructDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
182
190
  members.push({
183
191
  name: memberName.text,
184
192
  kind: 'property',
185
- line: child.startPosition.row + 1,
193
+ line: nodeStartLine(child),
186
194
  });
187
195
  }
188
196
  }
189
197
  }
190
198
 
191
- const parent = findParentNode(node, SOL_PARENT_TYPES);
192
- const fullName = parent ? `${parent}.${nameNode.text}` : nameNode.text;
193
-
194
199
  ctx.definitions.push({
195
- name: fullName,
200
+ name: qualifyWithParent(node, nameNode.text),
196
201
  kind: 'struct',
197
- line: node.startPosition.row + 1,
202
+ line: nodeStartLine(node),
198
203
  endLine: nodeEndLine(node),
199
204
  children: members.length > 0 ? members : undefined,
200
205
  });
@@ -208,17 +213,14 @@ function handleEnumDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
208
213
  for (let i = 0; i < node.childCount; i++) {
209
214
  const child = node.child(i);
210
215
  if (child && child.type === 'enum_value') {
211
- members.push({ name: child.text, kind: 'constant', line: child.startPosition.row + 1 });
216
+ members.push({ name: child.text, kind: 'constant', line: nodeStartLine(child) });
212
217
  }
213
218
  }
214
219
 
215
- const parent = findParentNode(node, SOL_PARENT_TYPES);
216
- const fullName = parent ? `${parent}.${nameNode.text}` : nameNode.text;
217
-
218
220
  ctx.definitions.push({
219
- name: fullName,
221
+ name: qualifyWithParent(node, nameNode.text),
220
222
  kind: 'enum',
221
- line: node.startPosition.row + 1,
223
+ line: nodeStartLine(node),
222
224
  endLine: nodeEndLine(node),
223
225
  children: members.length > 0 ? members : undefined,
224
226
  });
@@ -235,7 +237,7 @@ function handleFunctionDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
235
237
  ctx.definitions.push({
236
238
  name: fullName,
237
239
  kind,
238
- line: node.startPosition.row + 1,
240
+ line: nodeStartLine(node),
239
241
  endLine: nodeEndLine(node),
240
242
  children: params.length > 0 ? params : undefined,
241
243
  visibility: extractSolVisibility(node),
@@ -245,13 +247,10 @@ function handleFunctionDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
245
247
  function handleModifierDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
246
248
  const nameNode = node.childForFieldName('name');
247
249
  if (!nameNode) return;
248
- const parent = findParentNode(node, SOL_PARENT_TYPES);
249
- const fullName = parent ? `${parent}.${nameNode.text}` : nameNode.text;
250
-
251
250
  ctx.definitions.push({
252
- name: fullName,
251
+ name: qualifyWithParent(node, nameNode.text),
253
252
  kind: 'function',
254
- line: node.startPosition.row + 1,
253
+ line: nodeStartLine(node),
255
254
  endLine: nodeEndLine(node),
256
255
  decorators: ['modifier'],
257
256
  });
@@ -260,13 +259,10 @@ function handleModifierDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
260
259
  function handleEventDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
261
260
  const nameNode = node.childForFieldName('name');
262
261
  if (!nameNode) return;
263
- const parent = findParentNode(node, SOL_PARENT_TYPES);
264
- const fullName = parent ? `${parent}.${nameNode.text}` : nameNode.text;
265
-
266
262
  ctx.definitions.push({
267
- name: fullName,
263
+ name: qualifyWithParent(node, nameNode.text),
268
264
  kind: 'type',
269
- line: node.startPosition.row + 1,
265
+ line: nodeStartLine(node),
270
266
  endLine: nodeEndLine(node),
271
267
  decorators: ['event'],
272
268
  });
@@ -275,13 +271,10 @@ function handleEventDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
275
271
  function handleErrorDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
276
272
  const nameNode = node.childForFieldName('name');
277
273
  if (!nameNode) return;
278
- const parent = findParentNode(node, SOL_PARENT_TYPES);
279
- const fullName = parent ? `${parent}.${nameNode.text}` : nameNode.text;
280
-
281
274
  ctx.definitions.push({
282
- name: fullName,
275
+ name: qualifyWithParent(node, nameNode.text),
283
276
  kind: 'type',
284
- line: node.startPosition.row + 1,
277
+ line: nodeStartLine(node),
285
278
  endLine: nodeEndLine(node),
286
279
  decorators: ['error'],
287
280
  });
@@ -290,18 +283,21 @@ function handleErrorDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
290
283
  function handleStateVarDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
291
284
  const nameNode = node.childForFieldName('name');
292
285
  if (!nameNode) return;
293
- const parent = findParentNode(node, SOL_PARENT_TYPES);
294
- const fullName = parent ? `${parent}.${nameNode.text}` : nameNode.text;
295
-
296
286
  ctx.definitions.push({
297
- name: fullName,
287
+ name: qualifyWithParent(node, nameNode.text),
298
288
  kind: 'variable',
299
- line: node.startPosition.row + 1,
289
+ line: nodeStartLine(node),
300
290
  endLine: nodeEndLine(node),
301
291
  visibility: extractSolVisibility(node),
302
292
  });
303
293
  }
304
294
 
295
+ /** Qualify `name` with the nearest contract/interface/library, if any. */
296
+ function qualifyWithParent(node: TreeSitterNode, name: string): string {
297
+ const parent = findParentNode(node, SOL_PARENT_TYPES);
298
+ return parent ? `${parent}.${name}` : name;
299
+ }
300
+
305
301
  function handleImportDirective(node: TreeSitterNode, ctx: ExtractorOutput): void {
306
302
  // import "path"; or import { X } from "path"; or import "path" as Alias;
307
303
  for (let i = 0; i < node.childCount; i++) {
@@ -319,22 +315,17 @@ function handleImportDirective(node: TreeSitterNode, ctx: ExtractorOutput): void
319
315
  if (id) names.push(id.text);
320
316
  }
321
317
  }
322
- ctx.imports.push({
323
- source,
324
- names: names.length > 0 ? names : ['*'],
325
- line: node.startPosition.row + 1,
326
- });
318
+ // Preserve the explicit `['*']` fallback — pushImport's default uses the
319
+ // source basename, but Solidity's convention here is to mark unqualified
320
+ // imports as `*`.
321
+ pushImport(ctx, node, source, names.length > 0 ? names : ['*']);
327
322
  return;
328
323
  }
329
324
  // source_import: handles `import * as X from "path"`
330
325
  if (child.type === 'source_import' || child.type === 'import_clause') {
331
- const strNode = findChild(child, 'string') || findChild(child, 'string_literal');
326
+ const strNode = findFirstChildOfTypes(child, ['string', 'string_literal']);
332
327
  if (strNode) {
333
- ctx.imports.push({
334
- source: stripQuotes(strNode.text),
335
- names: ['*'],
336
- line: node.startPosition.row + 1,
337
- });
328
+ pushImport(ctx, node, stripQuotes(strNode.text), ['*']);
338
329
  return;
339
330
  }
340
331
  }
@@ -345,35 +336,25 @@ function handleCallExpression(node: TreeSitterNode, ctx: ExtractorOutput): void
345
336
  const funcNode = node.childForFieldName('function') || node.childForFieldName('callee');
346
337
  if (!funcNode) return;
347
338
 
348
- const call: Call = { name: '', line: node.startPosition.row + 1 };
339
+ let name = '';
340
+ let receiver: string | undefined;
349
341
  if (funcNode.type === 'member_expression' || funcNode.type === 'member_access') {
350
342
  const prop = funcNode.childForFieldName('property') || funcNode.childForFieldName('member');
351
343
  const obj = funcNode.childForFieldName('object') || funcNode.childForFieldName('expression');
352
- if (prop) call.name = prop.text;
353
- if (obj) call.receiver = obj.text;
344
+ if (prop) name = prop.text;
345
+ if (obj) receiver = obj.text;
354
346
  } else {
355
- call.name = funcNode.text;
347
+ name = funcNode.text;
356
348
  }
357
- if (call.name) ctx.calls.push(call);
349
+ if (name) pushCall(ctx, node, name, receiver !== undefined ? { receiver } : {});
358
350
  }
359
351
 
360
352
  // ── Helpers ────────────────────────────────────────────────────────────────
361
353
 
362
354
  function extractSolParams(funcNode: TreeSitterNode): SubDeclaration[] {
363
- const params: SubDeclaration[] = [];
364
355
  const paramList =
365
356
  funcNode.childForFieldName('parameters') || findChild(funcNode, 'parameter_list');
366
- if (!paramList) return params;
367
-
368
- for (let i = 0; i < paramList.childCount; i++) {
369
- const param = paramList.child(i);
370
- if (!param || param.type !== 'parameter') continue;
371
- const nameNode = param.childForFieldName('name');
372
- if (nameNode) {
373
- params.push({ name: nameNode.text, kind: 'parameter', line: param.startPosition.row + 1 });
374
- }
375
- }
376
- return params;
357
+ return extractSimpleParameters(paramList, { paramTypes: ['parameter'] });
377
358
  }
378
359
 
379
360
  function extractSolVisibility(
@@ -99,27 +99,60 @@ function handlePackageDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
99
99
  }
100
100
 
101
101
  function handleClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
102
- const nameNode = node.childForFieldName('name');
103
- if (!nameNode) return;
102
+ // tree-sitter-verilog exposes no field names on `class_declaration`. The
103
+ // class name lives under a `class_identifier > simple_identifier` chain, and
104
+ // the superclass appears as a `class_type` child (no `superclass` field).
105
+ // The Rust extractor in `crates/codegraph-core/src/extractors/verilog.rs`
106
+ // uses the same structural lookups so both engines emit identical class
107
+ // definitions and `extends` relations.
108
+ const name = findClassName(node);
109
+ if (!name) return;
104
110
 
105
111
  ctx.definitions.push({
106
- name: nameNode.text,
112
+ name,
107
113
  kind: 'class',
108
114
  line: node.startPosition.row + 1,
109
115
  endLine: nodeEndLine(node),
110
116
  });
111
117
 
112
- // Superclass via extends
113
- const superclass = node.childForFieldName('superclass');
118
+ const superclass = findClassSuperclass(node);
114
119
  if (superclass) {
115
120
  ctx.classes.push({
116
- name: nameNode.text,
117
- extends: superclass.text,
121
+ name,
122
+ extends: superclass,
118
123
  line: node.startPosition.row + 1,
119
124
  });
120
125
  }
121
126
  }
122
127
 
128
+ function findClassName(node: TreeSitterNode): string | null {
129
+ const fieldName = node.childForFieldName('name');
130
+ if (fieldName) return fieldName.text;
131
+ for (let i = 0; i < node.childCount; i++) {
132
+ const child = node.child(i);
133
+ if (child && child.type === 'class_identifier') {
134
+ const simple = findChild(child, 'simple_identifier');
135
+ return (simple ?? child).text.trim();
136
+ }
137
+ }
138
+ return null;
139
+ }
140
+
141
+ function findClassSuperclass(node: TreeSitterNode): string | null {
142
+ for (let i = 0; i < node.childCount; i++) {
143
+ const child = node.child(i);
144
+ if (child && child.type === 'class_type') {
145
+ const id = findChild(child, 'class_identifier');
146
+ if (id) {
147
+ const simple = findChild(id, 'simple_identifier');
148
+ return (simple ?? id).text.trim();
149
+ }
150
+ return child.text.trim();
151
+ }
152
+ }
153
+ return null;
154
+ }
155
+
123
156
  function handleFunctionDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
124
157
  const nameNode = findFunctionOrTaskName(node, 'function_identifier');
125
158
  if (!nameNode) return;
@@ -151,8 +184,12 @@ function handleTaskDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
151
184
  }
152
185
 
153
186
  function handleModuleInstantiation(node: TreeSitterNode, ctx: ExtractorOutput): void {
154
- // Module instantiations are like function calls: `ModuleName instance_name(...);`
155
- const moduleType = node.childForFieldName('type') || node.child(0);
187
+ // Module instantiations are like function calls: `ModuleName instance_name(...);`.
188
+ // The module type identifier is the first *named* child; using
189
+ // `namedChild(0)` (instead of `child(0)`) skips anonymous tokens like a
190
+ // leading `#` parameter-override punctuation so we never capture that as a
191
+ // call name. The Rust extractor uses the same lookup for parity.
192
+ const moduleType = node.childForFieldName('type') ?? node.namedChild(0);
156
193
  if (!moduleType) return;
157
194
 
158
195
  ctx.calls.push({
@@ -169,7 +206,10 @@ function handlePackageImport(node: TreeSitterNode, ctx: ExtractorOutput): void {
169
206
  if (child.type === 'package_import_item') {
170
207
  const text = child.text;
171
208
  const parts = text.split('::');
172
- const pkg = parts[0] ?? text;
209
+ // `String.split('::')` always yields at least one element — when the
210
+ // delimiter is absent the whole string is the sole item, so the
211
+ // empty-string fallback is unreachable in practice.
212
+ const pkg = parts[0] ?? '';
173
213
  const item = parts[1] ?? '*';
174
214
  ctx.imports.push({
175
215
  source: pkg,
@@ -182,9 +222,18 @@ function handlePackageImport(node: TreeSitterNode, ctx: ExtractorOutput): void {
182
222
 
183
223
  function handleIncludeDirective(node: TreeSitterNode, ctx: ExtractorOutput): void {
184
224
  // `include "file.vh"
225
+ // Mirrors the Rust `handle_include_directive` which checks all three node
226
+ // kinds — tree-sitter-verilog has emitted `double_quoted_string` in some
227
+ // grammar revisions, and missing it would silently drop the import in WASM
228
+ // while the native engine still records it.
185
229
  for (let i = 0; i < node.childCount; i++) {
186
230
  const child = node.child(i);
187
- if (child && (child.type === 'string_literal' || child.type === 'quoted_string')) {
231
+ if (
232
+ child &&
233
+ (child.type === 'string_literal' ||
234
+ child.type === 'quoted_string' ||
235
+ child.type === 'double_quoted_string')
236
+ ) {
188
237
  const source = child.text.replace(/^["']|["']$/g, '');
189
238
  ctx.imports.push({
190
239
  source,
@@ -266,8 +315,14 @@ function findVerilogParent(node: TreeSitterNode): string | null {
266
315
  current.type === 'package_declaration' ||
267
316
  current.type === 'class_declaration'
268
317
  ) {
269
- const name = findDeclName(current) || findModuleName(current);
270
- return name ? name.text : null;
318
+ // `class_declaration` wraps its name in `class_identifier >
319
+ // simple_identifier`; `findDeclName` / `findModuleName` only look at
320
+ // bare `simple_identifier`/`identifier` children, so they miss it.
321
+ // `findClassName` already handles the wrapper, so consult it last to
322
+ // qualify tasks/functions nested inside a SystemVerilog class.
323
+ const nameNode = findDeclName(current) || findModuleName(current);
324
+ if (nameNode) return nameNode.text;
325
+ return findClassName(current);
271
326
  }
272
327
  current = current.parent;
273
328
  }
@@ -292,17 +347,30 @@ function extractPorts(moduleNode: TreeSitterNode): SubDeclaration[] {
292
347
  ) {
293
348
  const nameNode =
294
349
  child.childForFieldName('name') ||
350
+ findChild(child, 'port_identifier') ||
295
351
  findChild(child, 'simple_identifier') ||
296
352
  findChild(child, 'identifier');
297
353
  if (nameNode) {
298
- ports.push({ name: nameNode.text, kind: 'property', line: child.startPosition.row + 1 });
354
+ // `port_identifier` wraps a `simple_identifier`; descend to the
355
+ // innermost identifier for a clean, whitespace-free name.
356
+ const inner =
357
+ findChild(nameNode, 'simple_identifier') ||
358
+ findChild(nameNode, 'identifier') ||
359
+ nameNode;
360
+ ports.push({ name: inner.text, kind: 'property', line: child.startPosition.row + 1 });
299
361
  }
300
362
  }
301
363
 
302
- // Recurse into port list containers
364
+ // Recurse into port list containers. `module_ansi_header` wraps the
365
+ // ANSI-style declarations emitted by tree-sitter-verilog (e.g.
366
+ // `module top(input clk, output reg q);`) — without this branch the
367
+ // WASM engine returns an empty children array while the native engine
368
+ // (which includes the same kind in its CONTAINER_KINDS list) returns
369
+ // the correct ports, breaking engine parity.
303
370
  if (
304
371
  child.type === 'list_of_port_declarations' ||
305
372
  child.type === 'module_header' ||
373
+ child.type === 'module_ansi_header' ||
306
374
  child.type === 'port_declaration_list'
307
375
  ) {
308
376
  collectFromNode(child);