@optave/codegraph 3.7.0 → 3.8.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 (401) hide show
  1. package/README.md +28 -16
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +195 -30
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/metrics.d.ts +0 -3
  6. package/dist/ast-analysis/metrics.d.ts.map +1 -1
  7. package/dist/ast-analysis/metrics.js +30 -13
  8. package/dist/ast-analysis/metrics.js.map +1 -1
  9. package/dist/ast-analysis/rules/javascript.d.ts.map +1 -1
  10. package/dist/ast-analysis/rules/javascript.js +0 -1
  11. package/dist/ast-analysis/rules/javascript.js.map +1 -1
  12. package/dist/ast-analysis/shared.d.ts.map +1 -1
  13. package/dist/ast-analysis/shared.js +24 -19
  14. package/dist/ast-analysis/shared.js.map +1 -1
  15. package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
  16. package/dist/ast-analysis/visitor-utils.js +55 -39
  17. package/dist/ast-analysis/visitor-utils.js.map +1 -1
  18. package/dist/ast-analysis/visitor.d.ts.map +1 -1
  19. package/dist/ast-analysis/visitor.js +91 -70
  20. package/dist/ast-analysis/visitor.js.map +1 -1
  21. package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
  22. package/dist/ast-analysis/visitors/ast-store-visitor.js +52 -129
  23. package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
  24. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  25. package/dist/ast-analysis/visitors/complexity-visitor.js +32 -39
  26. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  27. package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
  28. package/dist/ast-analysis/visitors/dataflow-visitor.js +57 -38
  29. package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
  30. package/dist/cli/commands/ast.js +2 -2
  31. package/dist/cli/commands/ast.js.map +1 -1
  32. package/dist/cli/commands/watch.d.ts.map +1 -1
  33. package/dist/cli/commands/watch.js +16 -2
  34. package/dist/cli/commands/watch.js.map +1 -1
  35. package/dist/db/connection.d.ts.map +1 -1
  36. package/dist/db/connection.js +29 -26
  37. package/dist/db/connection.js.map +1 -1
  38. package/dist/db/query-builder.d.ts.map +1 -1
  39. package/dist/db/query-builder.js +16 -5
  40. package/dist/db/query-builder.js.map +1 -1
  41. package/dist/db/repository/base.d.ts +10 -0
  42. package/dist/db/repository/base.d.ts.map +1 -1
  43. package/dist/db/repository/base.js +17 -0
  44. package/dist/db/repository/base.js.map +1 -1
  45. package/dist/db/repository/native-repository.d.ts +6 -1
  46. package/dist/db/repository/native-repository.d.ts.map +1 -1
  47. package/dist/db/repository/native-repository.js +77 -1
  48. package/dist/db/repository/native-repository.js.map +1 -1
  49. package/dist/db/repository/nodes.d.ts.map +1 -1
  50. package/dist/db/repository/nodes.js +8 -4
  51. package/dist/db/repository/nodes.js.map +1 -1
  52. package/dist/db/repository/sqlite-repository.d.ts +3 -0
  53. package/dist/db/repository/sqlite-repository.d.ts.map +1 -1
  54. package/dist/db/repository/sqlite-repository.js +26 -0
  55. package/dist/db/repository/sqlite-repository.js.map +1 -1
  56. package/dist/domain/analysis/brief.d.ts.map +1 -1
  57. package/dist/domain/analysis/brief.js +13 -17
  58. package/dist/domain/analysis/brief.js.map +1 -1
  59. package/dist/domain/analysis/context.d.ts.map +1 -1
  60. package/dist/domain/analysis/context.js +14 -11
  61. package/dist/domain/analysis/context.js.map +1 -1
  62. package/dist/domain/analysis/dependencies.d.ts.map +1 -1
  63. package/dist/domain/analysis/dependencies.js +53 -52
  64. package/dist/domain/analysis/dependencies.js.map +1 -1
  65. package/dist/domain/analysis/fn-impact.d.ts +2 -7
  66. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  67. package/dist/domain/analysis/fn-impact.js +33 -31
  68. package/dist/domain/analysis/fn-impact.js.map +1 -1
  69. package/dist/domain/analysis/implementations.d.ts.map +1 -1
  70. package/dist/domain/analysis/implementations.js +11 -19
  71. package/dist/domain/analysis/implementations.js.map +1 -1
  72. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  73. package/dist/domain/analysis/module-map.js +55 -76
  74. package/dist/domain/analysis/module-map.js.map +1 -1
  75. package/dist/domain/analysis/query-helpers.d.ts +7 -0
  76. package/dist/domain/analysis/query-helpers.d.ts.map +1 -1
  77. package/dist/domain/analysis/query-helpers.js +15 -1
  78. package/dist/domain/analysis/query-helpers.js.map +1 -1
  79. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  80. package/dist/domain/graph/builder/pipeline.js +315 -43
  81. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  82. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  83. package/dist/domain/graph/builder/stages/build-edges.js +106 -1
  84. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  85. package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
  86. package/dist/domain/graph/builder/stages/collect-files.js +17 -5
  87. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  88. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  89. package/dist/domain/graph/builder/stages/detect-changes.js +99 -51
  90. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  91. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  92. package/dist/domain/graph/builder/stages/finalize.js +34 -7
  93. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  94. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  95. package/dist/domain/graph/builder/stages/insert-nodes.js +50 -26
  96. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  97. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  98. package/dist/domain/graph/builder/stages/resolve-imports.js +95 -84
  99. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  100. package/dist/domain/graph/cycles.d.ts +6 -0
  101. package/dist/domain/graph/cycles.d.ts.map +1 -1
  102. package/dist/domain/graph/cycles.js +114 -22
  103. package/dist/domain/graph/cycles.js.map +1 -1
  104. package/dist/domain/graph/resolve.js +1 -1
  105. package/dist/domain/graph/resolve.js.map +1 -1
  106. package/dist/domain/graph/watcher.d.ts +2 -0
  107. package/dist/domain/graph/watcher.d.ts.map +1 -1
  108. package/dist/domain/graph/watcher.js +170 -75
  109. package/dist/domain/graph/watcher.js.map +1 -1
  110. package/dist/domain/parser.d.ts +1 -6
  111. package/dist/domain/parser.d.ts.map +1 -1
  112. package/dist/domain/parser.js +101 -32
  113. package/dist/domain/parser.js.map +1 -1
  114. package/dist/domain/search/generator.js +1 -1
  115. package/dist/domain/search/generator.js.map +1 -1
  116. package/dist/domain/search/models.d.ts +4 -3
  117. package/dist/domain/search/models.d.ts.map +1 -1
  118. package/dist/domain/search/models.js +18 -5
  119. package/dist/domain/search/models.js.map +1 -1
  120. package/dist/domain/search/search/hybrid.d.ts.map +1 -1
  121. package/dist/domain/search/search/hybrid.js +29 -18
  122. package/dist/domain/search/search/hybrid.js.map +1 -1
  123. package/dist/extractors/clojure.d.ts +12 -0
  124. package/dist/extractors/clojure.d.ts.map +1 -0
  125. package/dist/extractors/clojure.js +245 -0
  126. package/dist/extractors/clojure.js.map +1 -0
  127. package/dist/extractors/cuda.d.ts +11 -0
  128. package/dist/extractors/cuda.d.ts.map +1 -0
  129. package/dist/extractors/cuda.js +302 -0
  130. package/dist/extractors/cuda.js.map +1 -0
  131. package/dist/extractors/erlang.d.ts +14 -0
  132. package/dist/extractors/erlang.d.ts.map +1 -0
  133. package/dist/extractors/erlang.js +239 -0
  134. package/dist/extractors/erlang.js.map +1 -0
  135. package/dist/extractors/fsharp.d.ts +13 -0
  136. package/dist/extractors/fsharp.d.ts.map +1 -0
  137. package/dist/extractors/fsharp.js +218 -0
  138. package/dist/extractors/fsharp.js.map +1 -0
  139. package/dist/extractors/gleam.d.ts +14 -0
  140. package/dist/extractors/gleam.d.ts.map +1 -0
  141. package/dist/extractors/gleam.js +229 -0
  142. package/dist/extractors/gleam.js.map +1 -0
  143. package/dist/extractors/go.js +36 -33
  144. package/dist/extractors/go.js.map +1 -1
  145. package/dist/extractors/groovy.d.ts +10 -0
  146. package/dist/extractors/groovy.d.ts.map +1 -0
  147. package/dist/extractors/groovy.js +304 -0
  148. package/dist/extractors/groovy.js.map +1 -0
  149. package/dist/extractors/helpers.d.ts.map +1 -1
  150. package/dist/extractors/helpers.js +40 -29
  151. package/dist/extractors/helpers.js.map +1 -1
  152. package/dist/extractors/index.d.ts +11 -0
  153. package/dist/extractors/index.d.ts.map +1 -1
  154. package/dist/extractors/index.js +11 -0
  155. package/dist/extractors/index.js.map +1 -1
  156. package/dist/extractors/java.js +58 -46
  157. package/dist/extractors/java.js.map +1 -1
  158. package/dist/extractors/javascript.js +46 -45
  159. package/dist/extractors/javascript.js.map +1 -1
  160. package/dist/extractors/julia.d.ts +16 -0
  161. package/dist/extractors/julia.d.ts.map +1 -0
  162. package/dist/extractors/julia.js +287 -0
  163. package/dist/extractors/julia.js.map +1 -0
  164. package/dist/extractors/kotlin.js +84 -78
  165. package/dist/extractors/kotlin.js.map +1 -1
  166. package/dist/extractors/objc.d.ts +9 -0
  167. package/dist/extractors/objc.d.ts.map +1 -0
  168. package/dist/extractors/objc.js +406 -0
  169. package/dist/extractors/objc.js.map +1 -0
  170. package/dist/extractors/ocaml.js +74 -0
  171. package/dist/extractors/ocaml.js.map +1 -1
  172. package/dist/extractors/python.js +29 -24
  173. package/dist/extractors/python.js.map +1 -1
  174. package/dist/extractors/r.d.ts +13 -0
  175. package/dist/extractors/r.d.ts.map +1 -0
  176. package/dist/extractors/r.js +251 -0
  177. package/dist/extractors/r.js.map +1 -0
  178. package/dist/extractors/rust.js +41 -32
  179. package/dist/extractors/rust.js.map +1 -1
  180. package/dist/extractors/solidity.d.ts +9 -0
  181. package/dist/extractors/solidity.d.ts.map +1 -0
  182. package/dist/extractors/solidity.js +365 -0
  183. package/dist/extractors/solidity.js.map +1 -0
  184. package/dist/extractors/swift.js +83 -81
  185. package/dist/extractors/swift.js.map +1 -1
  186. package/dist/extractors/verilog.d.ts +9 -0
  187. package/dist/extractors/verilog.d.ts.map +1 -0
  188. package/dist/extractors/verilog.js +286 -0
  189. package/dist/extractors/verilog.js.map +1 -0
  190. package/dist/extractors/zig.js +58 -60
  191. package/dist/extractors/zig.js.map +1 -1
  192. package/dist/features/ast.d.ts +16 -14
  193. package/dist/features/ast.d.ts.map +1 -1
  194. package/dist/features/ast.js +84 -83
  195. package/dist/features/ast.js.map +1 -1
  196. package/dist/features/audit.d.ts.map +1 -1
  197. package/dist/features/audit.js +8 -6
  198. package/dist/features/audit.js.map +1 -1
  199. package/dist/features/branch-compare.d.ts.map +1 -1
  200. package/dist/features/branch-compare.js +69 -72
  201. package/dist/features/branch-compare.js.map +1 -1
  202. package/dist/features/communities.d.ts.map +1 -1
  203. package/dist/features/communities.js +19 -7
  204. package/dist/features/communities.js.map +1 -1
  205. package/dist/features/complexity.d.ts.map +1 -1
  206. package/dist/features/complexity.js +120 -125
  207. package/dist/features/complexity.js.map +1 -1
  208. package/dist/features/dataflow.d.ts.map +1 -1
  209. package/dist/features/dataflow.js +136 -137
  210. package/dist/features/dataflow.js.map +1 -1
  211. package/dist/features/flow.d.ts.map +1 -1
  212. package/dist/features/flow.js +84 -79
  213. package/dist/features/flow.js.map +1 -1
  214. package/dist/features/structure-query.d.ts.map +1 -1
  215. package/dist/features/structure-query.js +69 -65
  216. package/dist/features/structure-query.js.map +1 -1
  217. package/dist/graph/algorithms/bfs.d.ts +2 -0
  218. package/dist/graph/algorithms/bfs.d.ts.map +1 -1
  219. package/dist/graph/algorithms/bfs.js +27 -0
  220. package/dist/graph/algorithms/bfs.js.map +1 -1
  221. package/dist/graph/algorithms/centrality.d.ts +2 -0
  222. package/dist/graph/algorithms/centrality.d.ts.map +1 -1
  223. package/dist/graph/algorithms/centrality.js +28 -0
  224. package/dist/graph/algorithms/centrality.js.map +1 -1
  225. package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
  226. package/dist/graph/algorithms/leiden/optimiser.js +70 -55
  227. package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
  228. package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
  229. package/dist/graph/algorithms/leiden/partition.js +288 -266
  230. package/dist/graph/algorithms/leiden/partition.js.map +1 -1
  231. package/dist/graph/algorithms/louvain.d.ts +3 -4
  232. package/dist/graph/algorithms/louvain.d.ts.map +1 -1
  233. package/dist/graph/algorithms/louvain.js +29 -0
  234. package/dist/graph/algorithms/louvain.js.map +1 -1
  235. package/dist/graph/algorithms/shortest-path.d.ts +2 -0
  236. package/dist/graph/algorithms/shortest-path.d.ts.map +1 -1
  237. package/dist/graph/algorithms/shortest-path.js +18 -1
  238. package/dist/graph/algorithms/shortest-path.js.map +1 -1
  239. package/dist/graph/model.d.ts.map +1 -1
  240. package/dist/graph/model.js +5 -1
  241. package/dist/graph/model.js.map +1 -1
  242. package/dist/infrastructure/config.d.ts.map +1 -1
  243. package/dist/infrastructure/config.js +6 -4
  244. package/dist/infrastructure/config.js.map +1 -1
  245. package/dist/infrastructure/suppress.d.ts +25 -0
  246. package/dist/infrastructure/suppress.d.ts.map +1 -0
  247. package/dist/infrastructure/suppress.js +43 -0
  248. package/dist/infrastructure/suppress.js.map +1 -0
  249. package/dist/mcp/server.d.ts.map +1 -1
  250. package/dist/mcp/server.js +29 -24
  251. package/dist/mcp/server.js.map +1 -1
  252. package/dist/presentation/dataflow.d.ts.map +1 -1
  253. package/dist/presentation/dataflow.js +47 -38
  254. package/dist/presentation/dataflow.js.map +1 -1
  255. package/dist/presentation/diff-impact-mermaid.d.ts.map +1 -1
  256. package/dist/presentation/diff-impact-mermaid.js +60 -51
  257. package/dist/presentation/diff-impact-mermaid.js.map +1 -1
  258. package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
  259. package/dist/presentation/queries-cli/exports.js +20 -14
  260. package/dist/presentation/queries-cli/exports.js.map +1 -1
  261. package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
  262. package/dist/presentation/queries-cli/impact.js +15 -13
  263. package/dist/presentation/queries-cli/impact.js.map +1 -1
  264. package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
  265. package/dist/presentation/queries-cli/inspect.js +101 -79
  266. package/dist/presentation/queries-cli/inspect.js.map +1 -1
  267. package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
  268. package/dist/presentation/queries-cli/overview.js +25 -16
  269. package/dist/presentation/queries-cli/overview.js.map +1 -1
  270. package/dist/presentation/queries-cli/path.js +26 -20
  271. package/dist/presentation/queries-cli/path.js.map +1 -1
  272. package/dist/presentation/result-formatter.d.ts +10 -0
  273. package/dist/presentation/result-formatter.d.ts.map +1 -1
  274. package/dist/presentation/result-formatter.js +16 -1
  275. package/dist/presentation/result-formatter.js.map +1 -1
  276. package/dist/presentation/viewer.d.ts.map +1 -1
  277. package/dist/presentation/viewer.js +18 -12
  278. package/dist/presentation/viewer.js.map +1 -1
  279. package/dist/shared/errors.d.ts +5 -0
  280. package/dist/shared/errors.d.ts.map +1 -1
  281. package/dist/shared/errors.js +5 -0
  282. package/dist/shared/errors.js.map +1 -1
  283. package/dist/shared/hierarchy.d.ts +8 -2
  284. package/dist/shared/hierarchy.d.ts.map +1 -1
  285. package/dist/shared/hierarchy.js +42 -1
  286. package/dist/shared/hierarchy.js.map +1 -1
  287. package/dist/shared/normalize.d.ts +6 -1
  288. package/dist/shared/normalize.d.ts.map +1 -1
  289. package/dist/shared/normalize.js +20 -12
  290. package/dist/shared/normalize.js.map +1 -1
  291. package/dist/shared/paginate.d.ts +0 -9
  292. package/dist/shared/paginate.d.ts.map +1 -1
  293. package/dist/shared/paginate.js +0 -15
  294. package/dist/shared/paginate.js.map +1 -1
  295. package/dist/types.d.ts +129 -3
  296. package/dist/types.d.ts.map +1 -1
  297. package/grammars/tree-sitter-clojure.wasm +0 -0
  298. package/grammars/tree-sitter-cuda.wasm +0 -0
  299. package/grammars/tree-sitter-erlang.wasm +0 -0
  300. package/grammars/tree-sitter-fsharp.wasm +0 -0
  301. package/grammars/tree-sitter-gleam.wasm +0 -0
  302. package/grammars/tree-sitter-groovy.wasm +0 -0
  303. package/grammars/tree-sitter-julia.wasm +0 -0
  304. package/grammars/tree-sitter-objc.wasm +0 -0
  305. package/grammars/tree-sitter-ocaml_interface.wasm +0 -0
  306. package/grammars/tree-sitter-r.wasm +0 -0
  307. package/grammars/tree-sitter-solidity.wasm +0 -0
  308. package/grammars/tree-sitter-verilog.wasm +0 -0
  309. package/package.json +18 -7
  310. package/src/ast-analysis/engine.ts +245 -42
  311. package/src/ast-analysis/metrics.ts +33 -11
  312. package/src/ast-analysis/rules/javascript.ts +0 -1
  313. package/src/ast-analysis/shared.ts +33 -24
  314. package/src/ast-analysis/visitor-utils.ts +52 -32
  315. package/src/ast-analysis/visitor.ts +132 -71
  316. package/src/ast-analysis/visitors/ast-store-visitor.ts +49 -119
  317. package/src/ast-analysis/visitors/complexity-visitor.ts +35 -40
  318. package/src/ast-analysis/visitors/dataflow-visitor.ts +87 -43
  319. package/src/cli/commands/ast.ts +2 -2
  320. package/src/cli/commands/watch.ts +16 -2
  321. package/src/db/connection.ts +29 -28
  322. package/src/db/query-builder.ts +15 -3
  323. package/src/db/repository/base.ts +20 -0
  324. package/src/db/repository/native-repository.ts +79 -1
  325. package/src/db/repository/nodes.ts +13 -8
  326. package/src/db/repository/sqlite-repository.ts +29 -0
  327. package/src/domain/analysis/brief.ts +15 -25
  328. package/src/domain/analysis/context.ts +17 -10
  329. package/src/domain/analysis/dependencies.ts +67 -76
  330. package/src/domain/analysis/fn-impact.ts +36 -43
  331. package/src/domain/analysis/implementations.ts +11 -17
  332. package/src/domain/analysis/module-map.ts +58 -92
  333. package/src/domain/analysis/query-helpers.ts +18 -1
  334. package/src/domain/graph/builder/pipeline.ts +366 -41
  335. package/src/domain/graph/builder/stages/build-edges.ts +162 -1
  336. package/src/domain/graph/builder/stages/collect-files.ts +18 -7
  337. package/src/domain/graph/builder/stages/detect-changes.ts +110 -56
  338. package/src/domain/graph/builder/stages/finalize.ts +41 -11
  339. package/src/domain/graph/builder/stages/insert-nodes.ts +75 -39
  340. package/src/domain/graph/builder/stages/resolve-imports.ts +122 -100
  341. package/src/domain/graph/cycles.ts +110 -23
  342. package/src/domain/graph/resolve.ts +1 -1
  343. package/src/domain/graph/watcher.ts +202 -96
  344. package/src/domain/parser.ts +122 -28
  345. package/src/domain/search/generator.ts +1 -1
  346. package/src/domain/search/models.ts +17 -4
  347. package/src/domain/search/search/hybrid.ts +69 -51
  348. package/src/extractors/clojure.ts +273 -0
  349. package/src/extractors/cuda.ts +316 -0
  350. package/src/extractors/erlang.ts +252 -0
  351. package/src/extractors/fsharp.ts +253 -0
  352. package/src/extractors/gleam.ts +246 -0
  353. package/src/extractors/go.ts +43 -33
  354. package/src/extractors/groovy.ts +332 -0
  355. package/src/extractors/helpers.ts +37 -23
  356. package/src/extractors/index.ts +11 -0
  357. package/src/extractors/java.ts +66 -47
  358. package/src/extractors/javascript.ts +45 -46
  359. package/src/extractors/julia.ts +318 -0
  360. package/src/extractors/kotlin.ts +84 -77
  361. package/src/extractors/objc.ts +431 -0
  362. package/src/extractors/ocaml.ts +78 -0
  363. package/src/extractors/python.ts +31 -25
  364. package/src/extractors/r.ts +253 -0
  365. package/src/extractors/rust.ts +37 -29
  366. package/src/extractors/solidity.ts +394 -0
  367. package/src/extractors/swift.ts +81 -80
  368. package/src/extractors/verilog.ts +315 -0
  369. package/src/extractors/zig.ts +58 -61
  370. package/src/features/ast.ts +131 -112
  371. package/src/features/audit.ts +8 -6
  372. package/src/features/branch-compare.ts +105 -79
  373. package/src/features/communities.ts +25 -10
  374. package/src/features/complexity.ts +171 -134
  375. package/src/features/dataflow.ts +165 -175
  376. package/src/features/flow.ts +129 -92
  377. package/src/features/structure-query.ts +79 -64
  378. package/src/graph/algorithms/bfs.ts +34 -0
  379. package/src/graph/algorithms/centrality.ts +30 -0
  380. package/src/graph/algorithms/leiden/optimiser.ts +99 -55
  381. package/src/graph/algorithms/leiden/partition.ts +359 -294
  382. package/src/graph/algorithms/louvain.ts +31 -4
  383. package/src/graph/algorithms/shortest-path.ts +20 -1
  384. package/src/graph/model.ts +6 -1
  385. package/src/infrastructure/config.ts +6 -4
  386. package/src/infrastructure/suppress.ts +47 -0
  387. package/src/mcp/server.ts +53 -37
  388. package/src/presentation/dataflow.ts +50 -44
  389. package/src/presentation/diff-impact-mermaid.ts +104 -62
  390. package/src/presentation/queries-cli/exports.ts +21 -13
  391. package/src/presentation/queries-cli/impact.ts +15 -13
  392. package/src/presentation/queries-cli/inspect.ts +100 -81
  393. package/src/presentation/queries-cli/overview.ts +26 -16
  394. package/src/presentation/queries-cli/path.ts +33 -25
  395. package/src/presentation/result-formatter.ts +19 -1
  396. package/src/presentation/viewer.ts +42 -14
  397. package/src/shared/errors.ts +6 -0
  398. package/src/shared/hierarchy.ts +50 -2
  399. package/src/shared/normalize.ts +31 -12
  400. package/src/shared/paginate.ts +0 -17
  401. package/src/types.ts +138 -3
@@ -0,0 +1,332 @@
1
+ import type {
2
+ Call,
3
+ ExtractorOutput,
4
+ SubDeclaration,
5
+ TreeSitterNode,
6
+ TreeSitterTree,
7
+ } from '../types.js';
8
+ import {
9
+ extractModifierVisibility,
10
+ findChild,
11
+ findParentNode,
12
+ lastPathSegment,
13
+ nodeEndLine,
14
+ } from './helpers.js';
15
+
16
+ /**
17
+ * Extract symbols from Groovy files.
18
+ *
19
+ * Groovy is a JVM language with Java-like class/interface/enum structures
20
+ * plus closures, traits, and dynamic typing. The tree-sitter-groovy grammar
21
+ * models classes, methods, imports, and call expressions similarly to Java.
22
+ */
23
+ export function extractGroovySymbols(tree: TreeSitterTree, _filePath: string): ExtractorOutput {
24
+ const ctx: ExtractorOutput = {
25
+ definitions: [],
26
+ calls: [],
27
+ imports: [],
28
+ classes: [],
29
+ exports: [],
30
+ typeMap: new Map(),
31
+ };
32
+
33
+ walkGroovyNode(tree.rootNode, ctx);
34
+ return ctx;
35
+ }
36
+
37
+ function walkGroovyNode(node: TreeSitterNode, ctx: ExtractorOutput): void {
38
+ switch (node.type) {
39
+ case 'class_definition':
40
+ case 'class_declaration':
41
+ handleGroovyClassDecl(node, ctx);
42
+ break;
43
+ case 'interface_definition':
44
+ case 'interface_declaration':
45
+ handleGroovyInterfaceDecl(node, ctx);
46
+ break;
47
+ case 'enum_definition':
48
+ case 'enum_declaration':
49
+ handleGroovyEnumDecl(node, ctx);
50
+ break;
51
+ case 'method_definition':
52
+ case 'method_declaration':
53
+ handleGroovyMethodDecl(node, ctx);
54
+ break;
55
+ case 'constructor_definition':
56
+ case 'constructor_declaration':
57
+ handleGroovyConstructorDecl(node, ctx);
58
+ break;
59
+ case 'function_definition':
60
+ case 'function_declaration':
61
+ handleGroovyFunctionDecl(node, ctx);
62
+ break;
63
+ case 'import_statement':
64
+ case 'import_declaration':
65
+ handleGroovyImport(node, ctx);
66
+ break;
67
+ case 'method_call':
68
+ case 'method_invocation':
69
+ case 'call_expression':
70
+ case 'function_call':
71
+ handleGroovyCallExpr(node, ctx);
72
+ break;
73
+ case 'object_creation_expression':
74
+ handleGroovyObjectCreation(node, ctx);
75
+ break;
76
+ }
77
+
78
+ for (let i = 0; i < node.childCount; i++) {
79
+ const child = node.child(i);
80
+ if (child) walkGroovyNode(child, ctx);
81
+ }
82
+ }
83
+
84
+ // ── Handlers ───────────────────────────────────────────────────────────────
85
+
86
+ const GROOVY_PARENT_TYPES = [
87
+ 'class_definition',
88
+ 'class_declaration',
89
+ 'enum_definition',
90
+ 'enum_declaration',
91
+ 'interface_definition',
92
+ 'interface_declaration',
93
+ ] as const;
94
+
95
+ function handleGroovyClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
96
+ const nameNode = node.childForFieldName('name');
97
+ if (!nameNode) return;
98
+ const name = nameNode.text;
99
+
100
+ const members = extractGroovyClassMembers(node);
101
+ ctx.definitions.push({
102
+ name,
103
+ kind: 'class',
104
+ line: node.startPosition.row + 1,
105
+ endLine: nodeEndLine(node),
106
+ children: members.length > 0 ? members : undefined,
107
+ visibility: extractModifierVisibility(node),
108
+ });
109
+
110
+ // Superclass
111
+ const superclass = node.childForFieldName('superclass');
112
+ if (superclass) {
113
+ const superName =
114
+ superclass.type === 'generic_type' ? superclass.child(0)?.text : superclass.text;
115
+ if (superName) {
116
+ ctx.classes.push({ name, extends: superName, line: node.startPosition.row + 1 });
117
+ }
118
+ }
119
+
120
+ // Interfaces
121
+ const interfaces = node.childForFieldName('interfaces');
122
+ if (interfaces) {
123
+ for (let i = 0; i < interfaces.childCount; i++) {
124
+ const iface = interfaces.child(i);
125
+ if (
126
+ iface &&
127
+ (iface.type === 'type_identifier' ||
128
+ iface.type === 'identifier' ||
129
+ iface.type === 'generic_type')
130
+ ) {
131
+ const ifaceName = iface.type === 'generic_type' ? iface.child(0)?.text : iface.text;
132
+ if (ifaceName) {
133
+ ctx.classes.push({ name, implements: ifaceName, line: node.startPosition.row + 1 });
134
+ }
135
+ }
136
+ }
137
+ }
138
+ }
139
+
140
+ function handleGroovyInterfaceDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
141
+ const nameNode = node.childForFieldName('name');
142
+ if (!nameNode) return;
143
+
144
+ ctx.definitions.push({
145
+ name: nameNode.text,
146
+ kind: 'interface',
147
+ line: node.startPosition.row + 1,
148
+ endLine: nodeEndLine(node),
149
+ visibility: extractModifierVisibility(node),
150
+ });
151
+ }
152
+
153
+ function handleGroovyEnumDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
154
+ const nameNode = node.childForFieldName('name');
155
+ if (!nameNode) return;
156
+
157
+ const members: SubDeclaration[] = [];
158
+ const body = node.childForFieldName('body') || findChild(node, 'enum_body');
159
+ if (body) {
160
+ for (let i = 0; i < body.childCount; i++) {
161
+ const child = body.child(i);
162
+ if (!child) continue;
163
+ if (child.type === 'enum_constant' || child.type === 'identifier') {
164
+ const constName = child.childForFieldName('name') || child;
165
+ members.push({ name: constName.text, kind: 'constant', line: child.startPosition.row + 1 });
166
+ }
167
+ }
168
+ }
169
+
170
+ ctx.definitions.push({
171
+ name: nameNode.text,
172
+ kind: 'enum',
173
+ line: node.startPosition.row + 1,
174
+ endLine: nodeEndLine(node),
175
+ children: members.length > 0 ? members : undefined,
176
+ });
177
+ }
178
+
179
+ function handleGroovyMethodDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
180
+ const nameNode = node.childForFieldName('name');
181
+ if (!nameNode) return;
182
+ const parentClass = findParentNode(node, GROOVY_PARENT_TYPES);
183
+ const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
184
+
185
+ const params = extractGroovyParams(node);
186
+ ctx.definitions.push({
187
+ name: fullName,
188
+ kind: 'method',
189
+ line: node.startPosition.row + 1,
190
+ endLine: nodeEndLine(node),
191
+ children: params.length > 0 ? params : undefined,
192
+ visibility: extractModifierVisibility(node),
193
+ });
194
+ }
195
+
196
+ function handleGroovyConstructorDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
197
+ const nameNode = node.childForFieldName('name');
198
+ if (!nameNode) return;
199
+ const parentClass = findParentNode(node, GROOVY_PARENT_TYPES);
200
+ const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
201
+
202
+ const params = extractGroovyParams(node);
203
+ ctx.definitions.push({
204
+ name: fullName,
205
+ kind: 'method',
206
+ line: node.startPosition.row + 1,
207
+ endLine: nodeEndLine(node),
208
+ children: params.length > 0 ? params : undefined,
209
+ visibility: extractModifierVisibility(node),
210
+ });
211
+ }
212
+
213
+ function handleGroovyFunctionDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
214
+ const nameNode = node.childForFieldName('name');
215
+ if (!nameNode) return;
216
+
217
+ const params = extractGroovyParams(node);
218
+ ctx.definitions.push({
219
+ name: nameNode.text,
220
+ kind: 'function',
221
+ line: node.startPosition.row + 1,
222
+ endLine: nodeEndLine(node),
223
+ children: params.length > 0 ? params : undefined,
224
+ });
225
+ }
226
+
227
+ function handleGroovyImport(node: TreeSitterNode, ctx: ExtractorOutput): void {
228
+ // import foo.bar.Baz or import foo.bar.*
229
+ for (let i = 0; i < node.childCount; i++) {
230
+ const child = node.child(i);
231
+ if (!child) continue;
232
+ if (
233
+ child.type === 'dotted_identifier' ||
234
+ child.type === 'scoped_identifier' ||
235
+ child.type === 'identifier' ||
236
+ child.type === 'qualified_name'
237
+ ) {
238
+ const fullPath = child.text;
239
+ const lastName = lastPathSegment(fullPath, '.');
240
+ ctx.imports.push({
241
+ source: fullPath,
242
+ names: [lastName],
243
+ line: node.startPosition.row + 1,
244
+ javaImport: true,
245
+ });
246
+ return;
247
+ }
248
+ }
249
+ }
250
+
251
+ function handleGroovyCallExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
252
+ const call: Call = { name: '', line: node.startPosition.row + 1 };
253
+
254
+ // Try standard call_expression pattern
255
+ const funcNode = node.childForFieldName('function') || node.childForFieldName('method');
256
+ if (funcNode) {
257
+ if (funcNode.type === 'field_expression' || funcNode.type === 'member_access') {
258
+ const field = funcNode.childForFieldName('field') || funcNode.childForFieldName('property');
259
+ const obj = funcNode.childForFieldName('argument') || funcNode.childForFieldName('object');
260
+ if (field) call.name = field.text;
261
+ if (obj) call.receiver = obj.text;
262
+ } else {
263
+ call.name = funcNode.text;
264
+ }
265
+ } else {
266
+ // method_call: first child is receiver/name
267
+ const nameNode = node.childForFieldName('name');
268
+ const obj = node.childForFieldName('object');
269
+ if (nameNode) {
270
+ call.name = nameNode.text;
271
+ if (obj) call.receiver = obj.text;
272
+ }
273
+ }
274
+
275
+ if (call.name) ctx.calls.push(call);
276
+ }
277
+
278
+ function handleGroovyObjectCreation(node: TreeSitterNode, ctx: ExtractorOutput): void {
279
+ const typeNode = node.childForFieldName('type');
280
+ if (!typeNode) return;
281
+ const typeName = typeNode.type === 'generic_type' ? typeNode.child(0)?.text : typeNode.text;
282
+ if (typeName) ctx.calls.push({ name: typeName, line: node.startPosition.row + 1 });
283
+ }
284
+
285
+ // ── Helpers ────────────────────────────────────────────────────────────────
286
+
287
+ function extractGroovyParams(funcNode: TreeSitterNode): SubDeclaration[] {
288
+ const params: SubDeclaration[] = [];
289
+ const paramList =
290
+ funcNode.childForFieldName('parameters') || findChild(funcNode, 'formal_parameters');
291
+ if (!paramList) return params;
292
+
293
+ for (let i = 0; i < paramList.childCount; i++) {
294
+ const param = paramList.child(i);
295
+ if (!param) continue;
296
+ if (param.type === 'formal_parameter' || param.type === 'parameter') {
297
+ const nameNode = param.childForFieldName('name');
298
+ if (nameNode) {
299
+ params.push({ name: nameNode.text, kind: 'parameter', line: param.startPosition.row + 1 });
300
+ }
301
+ }
302
+ }
303
+ return params;
304
+ }
305
+
306
+ function extractGroovyClassMembers(classNode: TreeSitterNode): SubDeclaration[] {
307
+ const members: SubDeclaration[] = [];
308
+ const body = classNode.childForFieldName('body') || findChild(classNode, 'class_body');
309
+ if (!body) return members;
310
+
311
+ for (let i = 0; i < body.childCount; i++) {
312
+ const child = body.child(i);
313
+ if (!child) continue;
314
+ if (child.type === 'field_declaration') {
315
+ for (let j = 0; j < child.childCount; j++) {
316
+ const varDecl = child.child(j);
317
+ if (varDecl?.type === 'variable_declarator') {
318
+ const nameNode = varDecl.childForFieldName('name');
319
+ if (nameNode) {
320
+ members.push({
321
+ name: nameNode.text,
322
+ kind: 'property',
323
+ line: child.startPosition.row + 1,
324
+ visibility: extractModifierVisibility(child),
325
+ });
326
+ }
327
+ }
328
+ }
329
+ }
330
+ }
331
+ return members;
332
+ }
@@ -110,6 +110,20 @@ export function findParentNode(
110
110
  return null;
111
111
  }
112
112
 
113
+ /**
114
+ * Resolve a container's body node by trying each field name in order.
115
+ */
116
+ function resolveBodyNode(
117
+ containerNode: TreeSitterNode,
118
+ bodyFields: readonly string[],
119
+ ): TreeSitterNode | null {
120
+ for (const field of bodyFields) {
121
+ const body = containerNode.childForFieldName(field) || findChild(containerNode, field);
122
+ if (body) return body;
123
+ }
124
+ return null;
125
+ }
126
+
113
127
  /**
114
128
  * Extract child declarations from a container node's body.
115
129
  * Finds the body via `bodyFields` (tries childForFieldName then findChild for each),
@@ -126,22 +140,17 @@ export function extractBodyMembers(
126
140
  nameField: string = 'name',
127
141
  visibility?: (member: TreeSitterNode) => SubDeclaration['visibility'],
128
142
  ): SubDeclaration[] {
143
+ const body = resolveBodyNode(containerNode, bodyFields);
144
+ if (!body) return [];
129
145
  const members: SubDeclaration[] = [];
130
- let body: TreeSitterNode | null = null;
131
- for (const field of bodyFields) {
132
- body = containerNode.childForFieldName(field) || findChild(containerNode, field);
133
- if (body) break;
134
- }
135
- if (!body) return members;
136
146
  for (let i = 0; i < body.childCount; i++) {
137
147
  const member = body.child(i);
138
148
  if (!member || member.type !== memberType) continue;
139
149
  const nn = member.childForFieldName(nameField);
140
- if (nn) {
141
- const entry: SubDeclaration = { name: nn.text, kind, line: member.startPosition.row + 1 };
142
- if (visibility) entry.visibility = visibility(member);
143
- members.push(entry);
144
- }
150
+ if (!nn) continue;
151
+ const entry: SubDeclaration = { name: nn.text, kind, line: member.startPosition.row + 1 };
152
+ if (visibility) entry.visibility = visibility(member);
153
+ members.push(entry);
145
154
  }
146
155
  return members;
147
156
  }
@@ -162,24 +171,29 @@ export function lastPathSegment(path: string, separator: string = '/'): string {
162
171
  return path.split(separator).pop() ?? path;
163
172
  }
164
173
 
174
+ /**
175
+ * Parse visibility from a modifier node's text content.
176
+ */
177
+ function parseModifierText(text: string): 'public' | 'private' | 'protected' | undefined {
178
+ if (VISIBILITY_KEYWORDS.has(text)) return text as 'public' | 'private' | 'protected';
179
+ // C# 'private protected' — accessible to derived types in same assembly → protected
180
+ if (text === 'private protected') return 'protected';
181
+ // Compound modifiers node (Java: "public static") — scan its text for a keyword
182
+ for (const kw of VISIBILITY_KEYWORDS) {
183
+ if (text.includes(kw)) return kw as 'public' | 'private' | 'protected';
184
+ }
185
+ return undefined;
186
+ }
187
+
165
188
  export function extractModifierVisibility(
166
189
  node: TreeSitterNode,
167
190
  modifierTypes: Set<string> = DEFAULT_MODIFIER_TYPES,
168
191
  ): 'public' | 'private' | 'protected' | undefined {
169
192
  for (let i = 0; i < node.childCount; i++) {
170
193
  const child = node.child(i);
171
- if (!child) continue;
172
- // Direct keyword match (e.g., PHP visibility_modifier = "public")
173
- if (modifierTypes.has(child.type)) {
174
- const text = child.text;
175
- if (VISIBILITY_KEYWORDS.has(text)) return text as 'public' | 'private' | 'protected';
176
- // C# 'private protected' — accessible to derived types in same assembly → protected
177
- if (text === 'private protected') return 'protected';
178
- // Compound modifiers node (Java: "public static") — scan its text for a keyword
179
- for (const kw of VISIBILITY_KEYWORDS) {
180
- if (text.includes(kw)) return kw as 'public' | 'private' | 'protected';
181
- }
182
- }
194
+ if (!child || !modifierTypes.has(child.type)) continue;
195
+ const result = parseModifierText(child.text);
196
+ if (result) return result;
183
197
  }
184
198
  return undefined;
185
199
  }
@@ -1,21 +1,32 @@
1
1
  export { extractBashSymbols } from './bash.js';
2
2
  export { extractCSymbols } from './c.js';
3
+ export { extractClojureSymbols } from './clojure.js';
3
4
  export { extractCppSymbols } from './cpp.js';
4
5
  export { extractCSharpSymbols } from './csharp.js';
6
+ export { extractCudaSymbols } from './cuda.js';
5
7
  export { extractDartSymbols } from './dart.js';
6
8
  export { extractElixirSymbols } from './elixir.js';
9
+ export { extractErlangSymbols } from './erlang.js';
10
+ export { extractFSharpSymbols } from './fsharp.js';
11
+ export { extractGleamSymbols } from './gleam.js';
7
12
  export { extractGoSymbols } from './go.js';
13
+ export { extractGroovySymbols } from './groovy.js';
8
14
  export { extractHaskellSymbols } from './haskell.js';
9
15
  export { extractHCLSymbols } from './hcl.js';
10
16
  export { extractJavaSymbols } from './java.js';
11
17
  export { extractSymbols } from './javascript.js';
18
+ export { extractJuliaSymbols } from './julia.js';
12
19
  export { extractKotlinSymbols } from './kotlin.js';
13
20
  export { extractLuaSymbols } from './lua.js';
21
+ export { extractObjCSymbols } from './objc.js';
14
22
  export { extractOCamlSymbols } from './ocaml.js';
15
23
  export { extractPHPSymbols } from './php.js';
16
24
  export { extractPythonSymbols } from './python.js';
25
+ export { extractRSymbols } from './r.js';
17
26
  export { extractRubySymbols } from './ruby.js';
18
27
  export { extractRustSymbols } from './rust.js';
19
28
  export { extractScalaSymbols } from './scala.js';
29
+ export { extractSoliditySymbols } from './solidity.js';
20
30
  export { extractSwiftSymbols } from './swift.js';
31
+ export { extractVerilogSymbols } from './verilog.js';
21
32
  export { extractZigSymbols } from './zig.js';
@@ -83,27 +83,7 @@ function handleJavaClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
83
83
  children: classChildren.length > 0 ? classChildren : undefined,
84
84
  });
85
85
 
86
- const superclass = node.childForFieldName('superclass');
87
- if (superclass) {
88
- for (let i = 0; i < superclass.childCount; i++) {
89
- const child = superclass.child(i);
90
- if (
91
- child &&
92
- (child.type === 'type_identifier' ||
93
- child.type === 'identifier' ||
94
- child.type === 'generic_type')
95
- ) {
96
- const superName = child.type === 'generic_type' ? child.child(0)?.text : child.text;
97
- if (superName)
98
- ctx.classes.push({
99
- name: nameNode.text,
100
- extends: superName,
101
- line: node.startPosition.row + 1,
102
- });
103
- break;
104
- }
105
- }
106
- }
86
+ extractJavaSuperclass(node, nameNode.text, ctx);
107
87
 
108
88
  const interfaces = node.childForFieldName('interfaces');
109
89
  if (interfaces) {
@@ -111,6 +91,32 @@ function handleJavaClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
111
91
  }
112
92
  }
113
93
 
94
+ /** Extract the superclass (extends) relationship from a Java class declaration. */
95
+ function extractJavaSuperclass(
96
+ node: TreeSitterNode,
97
+ className: string,
98
+ ctx: ExtractorOutput,
99
+ ): void {
100
+ const superclass = node.childForFieldName('superclass');
101
+ if (!superclass) return;
102
+ const superName = findJavaSuperTypeName(superclass);
103
+ if (superName) {
104
+ ctx.classes.push({ name: className, extends: superName, line: node.startPosition.row + 1 });
105
+ }
106
+ }
107
+
108
+ /** Find the type name from a superclass node (handles generic_type unwrapping). */
109
+ function findJavaSuperTypeName(superclass: TreeSitterNode): string | undefined {
110
+ for (let i = 0; i < superclass.childCount; i++) {
111
+ const child = superclass.child(i);
112
+ if (!child) continue;
113
+ if (JAVA_TYPE_NODE_TYPES.has(child.type)) {
114
+ return resolveJavaIfaceName(child);
115
+ }
116
+ }
117
+ return undefined;
118
+ }
119
+
114
120
  const JAVA_TYPE_NODE_TYPES = new Set(['type_identifier', 'identifier', 'generic_type']);
115
121
 
116
122
  /** Resolve interface name from a type node (handles generic_type unwrapping). */
@@ -161,19 +167,26 @@ function handleJavaInterfaceDecl(node: TreeSitterNode, ctx: ExtractorOutput): vo
161
167
  endLine: nodeEndLine(node),
162
168
  });
163
169
  const body = node.childForFieldName('body');
164
- if (body) {
165
- for (let i = 0; i < body.childCount; i++) {
166
- const child = body.child(i);
167
- if (child && child.type === 'method_declaration') {
168
- const methName = child.childForFieldName('name');
169
- if (methName) {
170
- ctx.definitions.push({
171
- name: `${nameNode.text}.${methName.text}`,
172
- kind: 'method',
173
- line: child.startPosition.row + 1,
174
- endLine: child.endPosition.row + 1,
175
- });
176
- }
170
+ if (body) extractJavaInterfaceMethods(body, nameNode.text, ctx);
171
+ }
172
+
173
+ /** Extract method declarations from a Java interface body. */
174
+ function extractJavaInterfaceMethods(
175
+ body: TreeSitterNode,
176
+ ifaceName: string,
177
+ ctx: ExtractorOutput,
178
+ ): void {
179
+ for (let i = 0; i < body.childCount; i++) {
180
+ const child = body.child(i);
181
+ if (child && child.type === 'method_declaration') {
182
+ const methName = child.childForFieldName('name');
183
+ if (methName) {
184
+ ctx.definitions.push({
185
+ name: `${ifaceName}.${methName.text}`,
186
+ kind: 'method',
187
+ line: child.startPosition.row + 1,
188
+ endLine: child.endPosition.row + 1,
189
+ });
177
190
  }
178
191
  }
179
192
  }
@@ -321,23 +334,29 @@ function extractClassFields(classNode: TreeSitterNode): SubDeclaration[] {
321
334
  for (let i = 0; i < body.childCount; i++) {
322
335
  const member = body.child(i);
323
336
  if (!member || member.type !== 'field_declaration') continue;
324
- for (let j = 0; j < member.childCount; j++) {
325
- const child = member.child(j);
326
- if (!child || child.type !== 'variable_declarator') continue;
327
- const nameNode = child.childForFieldName('name');
328
- if (nameNode) {
329
- fields.push({
330
- name: nameNode.text,
331
- kind: 'property',
332
- line: member.startPosition.row + 1,
333
- visibility: extractModifierVisibility(member),
334
- });
335
- }
336
- }
337
+ extractFieldDeclarators(member, fields);
337
338
  }
338
339
  return fields;
339
340
  }
340
341
 
342
+ /** Extract variable_declarator names from a field_declaration node. */
343
+ function extractFieldDeclarators(member: TreeSitterNode, fields: SubDeclaration[]): void {
344
+ const vis = extractModifierVisibility(member);
345
+ for (let j = 0; j < member.childCount; j++) {
346
+ const child = member.child(j);
347
+ if (!child || child.type !== 'variable_declarator') continue;
348
+ const nameNode = child.childForFieldName('name');
349
+ if (nameNode) {
350
+ fields.push({
351
+ name: nameNode.text,
352
+ kind: 'property',
353
+ line: member.startPosition.row + 1,
354
+ visibility: vis,
355
+ });
356
+ }
357
+ }
358
+ }
359
+
341
360
  function extractEnumConstants(enumNode: TreeSitterNode): SubDeclaration[] {
342
361
  return extractBodyMembers(enumNode, ['body', 'enum_body'], 'enum_constant', 'constant');
343
362
  }