@colbymchenry/codegraph-darwin-x64 0.9.9 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/lib/dist/bin/codegraph.d.ts +1 -0
  2. package/lib/dist/bin/codegraph.d.ts.map +1 -1
  3. package/lib/dist/bin/codegraph.js +246 -3
  4. package/lib/dist/bin/codegraph.js.map +1 -1
  5. package/lib/dist/context/index.d.ts.map +1 -1
  6. package/lib/dist/context/index.js +7 -0
  7. package/lib/dist/context/index.js.map +1 -1
  8. package/lib/dist/db/index.d.ts.map +1 -1
  9. package/lib/dist/db/index.js +2 -1
  10. package/lib/dist/db/index.js.map +1 -1
  11. package/lib/dist/db/migrations.d.ts +1 -1
  12. package/lib/dist/db/migrations.d.ts.map +1 -1
  13. package/lib/dist/db/migrations.js +10 -1
  14. package/lib/dist/db/migrations.js.map +1 -1
  15. package/lib/dist/db/queries.d.ts +43 -0
  16. package/lib/dist/db/queries.d.ts.map +1 -1
  17. package/lib/dist/db/queries.js +103 -7
  18. package/lib/dist/db/queries.js.map +1 -1
  19. package/lib/dist/db/schema.sql +1 -0
  20. package/lib/dist/db/sqlite-adapter.d.ts +7 -0
  21. package/lib/dist/db/sqlite-adapter.d.ts.map +1 -1
  22. package/lib/dist/db/sqlite-adapter.js +3 -0
  23. package/lib/dist/db/sqlite-adapter.js.map +1 -1
  24. package/lib/dist/directory.d.ts +34 -2
  25. package/lib/dist/directory.d.ts.map +1 -1
  26. package/lib/dist/directory.js +129 -35
  27. package/lib/dist/directory.js.map +1 -1
  28. package/lib/dist/extraction/astro-extractor.d.ts +79 -0
  29. package/lib/dist/extraction/astro-extractor.d.ts.map +1 -0
  30. package/lib/dist/extraction/astro-extractor.js +320 -0
  31. package/lib/dist/extraction/astro-extractor.js.map +1 -0
  32. package/lib/dist/extraction/extraction-version.d.ts +25 -0
  33. package/lib/dist/extraction/extraction-version.d.ts.map +1 -0
  34. package/lib/dist/extraction/extraction-version.js +28 -0
  35. package/lib/dist/extraction/extraction-version.js.map +1 -0
  36. package/lib/dist/extraction/function-ref.d.ts +118 -0
  37. package/lib/dist/extraction/function-ref.d.ts.map +1 -0
  38. package/lib/dist/extraction/function-ref.js +727 -0
  39. package/lib/dist/extraction/function-ref.js.map +1 -0
  40. package/lib/dist/extraction/generated-detection.d.ts.map +1 -1
  41. package/lib/dist/extraction/generated-detection.js +3 -0
  42. package/lib/dist/extraction/generated-detection.js.map +1 -1
  43. package/lib/dist/extraction/grammars.d.ts +7 -1
  44. package/lib/dist/extraction/grammars.d.ts.map +1 -1
  45. package/lib/dist/extraction/grammars.js +52 -4
  46. package/lib/dist/extraction/grammars.js.map +1 -1
  47. package/lib/dist/extraction/index.d.ts +34 -0
  48. package/lib/dist/extraction/index.d.ts.map +1 -1
  49. package/lib/dist/extraction/index.js +346 -62
  50. package/lib/dist/extraction/index.js.map +1 -1
  51. package/lib/dist/extraction/languages/c-cpp.d.ts +8 -0
  52. package/lib/dist/extraction/languages/c-cpp.d.ts.map +1 -1
  53. package/lib/dist/extraction/languages/c-cpp.js +87 -28
  54. package/lib/dist/extraction/languages/c-cpp.js.map +1 -1
  55. package/lib/dist/extraction/languages/csharp.d.ts +22 -0
  56. package/lib/dist/extraction/languages/csharp.d.ts.map +1 -1
  57. package/lib/dist/extraction/languages/csharp.js +84 -2
  58. package/lib/dist/extraction/languages/csharp.js.map +1 -1
  59. package/lib/dist/extraction/languages/dart.d.ts.map +1 -1
  60. package/lib/dist/extraction/languages/dart.js +161 -1
  61. package/lib/dist/extraction/languages/dart.js.map +1 -1
  62. package/lib/dist/extraction/languages/go.d.ts.map +1 -1
  63. package/lib/dist/extraction/languages/go.js +43 -2
  64. package/lib/dist/extraction/languages/go.js.map +1 -1
  65. package/lib/dist/extraction/languages/index.d.ts.map +1 -1
  66. package/lib/dist/extraction/languages/index.js +2 -0
  67. package/lib/dist/extraction/languages/index.js.map +1 -1
  68. package/lib/dist/extraction/languages/java.d.ts.map +1 -1
  69. package/lib/dist/extraction/languages/java.js +42 -1
  70. package/lib/dist/extraction/languages/java.js.map +1 -1
  71. package/lib/dist/extraction/languages/javascript.d.ts.map +1 -1
  72. package/lib/dist/extraction/languages/javascript.js +16 -0
  73. package/lib/dist/extraction/languages/javascript.js.map +1 -1
  74. package/lib/dist/extraction/languages/kotlin.d.ts.map +1 -1
  75. package/lib/dist/extraction/languages/kotlin.js +69 -0
  76. package/lib/dist/extraction/languages/kotlin.js.map +1 -1
  77. package/lib/dist/extraction/languages/objc.d.ts.map +1 -1
  78. package/lib/dist/extraction/languages/objc.js +42 -0
  79. package/lib/dist/extraction/languages/objc.js.map +1 -1
  80. package/lib/dist/extraction/languages/pascal.d.ts.map +1 -1
  81. package/lib/dist/extraction/languages/pascal.js +11 -0
  82. package/lib/dist/extraction/languages/pascal.js.map +1 -1
  83. package/lib/dist/extraction/languages/php.d.ts.map +1 -1
  84. package/lib/dist/extraction/languages/php.js +90 -1
  85. package/lib/dist/extraction/languages/php.js.map +1 -1
  86. package/lib/dist/extraction/languages/r.d.ts +3 -0
  87. package/lib/dist/extraction/languages/r.d.ts.map +1 -0
  88. package/lib/dist/extraction/languages/r.js +314 -0
  89. package/lib/dist/extraction/languages/r.js.map +1 -0
  90. package/lib/dist/extraction/languages/ruby.d.ts.map +1 -1
  91. package/lib/dist/extraction/languages/ruby.js +35 -0
  92. package/lib/dist/extraction/languages/ruby.js.map +1 -1
  93. package/lib/dist/extraction/languages/rust.d.ts.map +1 -1
  94. package/lib/dist/extraction/languages/rust.js +35 -2
  95. package/lib/dist/extraction/languages/rust.js.map +1 -1
  96. package/lib/dist/extraction/languages/scala.d.ts.map +1 -1
  97. package/lib/dist/extraction/languages/scala.js +61 -1
  98. package/lib/dist/extraction/languages/scala.js.map +1 -1
  99. package/lib/dist/extraction/languages/swift.d.ts.map +1 -1
  100. package/lib/dist/extraction/languages/swift.js +61 -0
  101. package/lib/dist/extraction/languages/swift.js.map +1 -1
  102. package/lib/dist/extraction/languages/typescript.d.ts +13 -0
  103. package/lib/dist/extraction/languages/typescript.d.ts.map +1 -1
  104. package/lib/dist/extraction/languages/typescript.js +38 -0
  105. package/lib/dist/extraction/languages/typescript.js.map +1 -1
  106. package/lib/dist/extraction/liquid-extractor.d.ts +7 -0
  107. package/lib/dist/extraction/liquid-extractor.d.ts.map +1 -1
  108. package/lib/dist/extraction/liquid-extractor.js +53 -9
  109. package/lib/dist/extraction/liquid-extractor.js.map +1 -1
  110. package/lib/dist/extraction/razor-extractor.d.ts +42 -0
  111. package/lib/dist/extraction/razor-extractor.d.ts.map +1 -0
  112. package/lib/dist/extraction/razor-extractor.js +285 -0
  113. package/lib/dist/extraction/razor-extractor.js.map +1 -0
  114. package/lib/dist/extraction/svelte-extractor.d.ts.map +1 -1
  115. package/lib/dist/extraction/svelte-extractor.js +6 -3
  116. package/lib/dist/extraction/svelte-extractor.js.map +1 -1
  117. package/lib/dist/extraction/tree-sitter-helpers.d.ts.map +1 -1
  118. package/lib/dist/extraction/tree-sitter-helpers.js +59 -10
  119. package/lib/dist/extraction/tree-sitter-helpers.js.map +1 -1
  120. package/lib/dist/extraction/tree-sitter-types.d.ts +33 -0
  121. package/lib/dist/extraction/tree-sitter-types.d.ts.map +1 -1
  122. package/lib/dist/extraction/tree-sitter.d.ts +211 -0
  123. package/lib/dist/extraction/tree-sitter.d.ts.map +1 -1
  124. package/lib/dist/extraction/tree-sitter.js +1681 -49
  125. package/lib/dist/extraction/tree-sitter.js.map +1 -1
  126. package/lib/dist/extraction/vue-extractor.d.ts +15 -0
  127. package/lib/dist/extraction/vue-extractor.d.ts.map +1 -1
  128. package/lib/dist/extraction/vue-extractor.js +94 -3
  129. package/lib/dist/extraction/vue-extractor.js.map +1 -1
  130. package/lib/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  131. package/lib/dist/extraction/wasm/tree-sitter-r.wasm +0 -0
  132. package/lib/dist/graph/queries.d.ts.map +1 -1
  133. package/lib/dist/graph/queries.js +13 -40
  134. package/lib/dist/graph/queries.js.map +1 -1
  135. package/lib/dist/graph/traversal.d.ts.map +1 -1
  136. package/lib/dist/graph/traversal.js +16 -4
  137. package/lib/dist/graph/traversal.js.map +1 -1
  138. package/lib/dist/index.d.ts +34 -2
  139. package/lib/dist/index.d.ts.map +1 -1
  140. package/lib/dist/index.js +90 -8
  141. package/lib/dist/index.js.map +1 -1
  142. package/lib/dist/installer/index.d.ts.map +1 -1
  143. package/lib/dist/installer/index.js +52 -2
  144. package/lib/dist/installer/index.js.map +1 -1
  145. package/lib/dist/installer/instructions-template.d.ts +34 -11
  146. package/lib/dist/installer/instructions-template.d.ts.map +1 -1
  147. package/lib/dist/installer/instructions-template.js +44 -12
  148. package/lib/dist/installer/instructions-template.js.map +1 -1
  149. package/lib/dist/installer/targets/claude.d.ts.map +1 -1
  150. package/lib/dist/installer/targets/claude.js +6 -10
  151. package/lib/dist/installer/targets/claude.js.map +1 -1
  152. package/lib/dist/installer/targets/codex.js +4 -6
  153. package/lib/dist/installer/targets/codex.js.map +1 -1
  154. package/lib/dist/installer/targets/gemini.js +4 -6
  155. package/lib/dist/installer/targets/gemini.js.map +1 -1
  156. package/lib/dist/installer/targets/opencode.d.ts +9 -1
  157. package/lib/dist/installer/targets/opencode.d.ts.map +1 -1
  158. package/lib/dist/installer/targets/opencode.js +91 -40
  159. package/lib/dist/installer/targets/opencode.js.map +1 -1
  160. package/lib/dist/installer/targets/shared.d.ts +14 -0
  161. package/lib/dist/installer/targets/shared.d.ts.map +1 -1
  162. package/lib/dist/installer/targets/shared.js +16 -0
  163. package/lib/dist/installer/targets/shared.js.map +1 -1
  164. package/lib/dist/mcp/daemon.d.ts +60 -1
  165. package/lib/dist/mcp/daemon.d.ts.map +1 -1
  166. package/lib/dist/mcp/daemon.js +221 -8
  167. package/lib/dist/mcp/daemon.js.map +1 -1
  168. package/lib/dist/mcp/dynamic-boundaries.d.ts +41 -0
  169. package/lib/dist/mcp/dynamic-boundaries.d.ts.map +1 -0
  170. package/lib/dist/mcp/dynamic-boundaries.js +359 -0
  171. package/lib/dist/mcp/dynamic-boundaries.js.map +1 -0
  172. package/lib/dist/mcp/index.d.ts.map +1 -1
  173. package/lib/dist/mcp/index.js +18 -9
  174. package/lib/dist/mcp/index.js.map +1 -1
  175. package/lib/dist/mcp/ppid-watchdog.d.ts +44 -0
  176. package/lib/dist/mcp/ppid-watchdog.d.ts.map +1 -0
  177. package/lib/dist/mcp/ppid-watchdog.js +27 -0
  178. package/lib/dist/mcp/ppid-watchdog.js.map +1 -0
  179. package/lib/dist/mcp/proxy.d.ts +6 -0
  180. package/lib/dist/mcp/proxy.d.ts.map +1 -1
  181. package/lib/dist/mcp/proxy.js +153 -24
  182. package/lib/dist/mcp/proxy.js.map +1 -1
  183. package/lib/dist/mcp/server-instructions.d.ts +12 -1
  184. package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
  185. package/lib/dist/mcp/server-instructions.js +43 -16
  186. package/lib/dist/mcp/server-instructions.js.map +1 -1
  187. package/lib/dist/mcp/session.d.ts +2 -0
  188. package/lib/dist/mcp/session.d.ts.map +1 -1
  189. package/lib/dist/mcp/session.js +49 -2
  190. package/lib/dist/mcp/session.js.map +1 -1
  191. package/lib/dist/mcp/stdin-teardown.d.ts +27 -0
  192. package/lib/dist/mcp/stdin-teardown.d.ts.map +1 -0
  193. package/lib/dist/mcp/stdin-teardown.js +49 -0
  194. package/lib/dist/mcp/stdin-teardown.js.map +1 -0
  195. package/lib/dist/mcp/tools.d.ts +71 -0
  196. package/lib/dist/mcp/tools.d.ts.map +1 -1
  197. package/lib/dist/mcp/tools.js +703 -85
  198. package/lib/dist/mcp/tools.js.map +1 -1
  199. package/lib/dist/mcp/transport.d.ts.map +1 -1
  200. package/lib/dist/mcp/transport.js +18 -2
  201. package/lib/dist/mcp/transport.js.map +1 -1
  202. package/lib/dist/resolution/callback-synthesizer.d.ts +3 -3
  203. package/lib/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  204. package/lib/dist/resolution/callback-synthesizer.js +549 -21
  205. package/lib/dist/resolution/callback-synthesizer.js.map +1 -1
  206. package/lib/dist/resolution/frameworks/astro.d.ts +9 -0
  207. package/lib/dist/resolution/frameworks/astro.d.ts.map +1 -0
  208. package/lib/dist/resolution/frameworks/astro.js +169 -0
  209. package/lib/dist/resolution/frameworks/astro.js.map +1 -0
  210. package/lib/dist/resolution/frameworks/expo-modules.d.ts.map +1 -1
  211. package/lib/dist/resolution/frameworks/expo-modules.js +6 -1
  212. package/lib/dist/resolution/frameworks/expo-modules.js.map +1 -1
  213. package/lib/dist/resolution/frameworks/index.d.ts +1 -0
  214. package/lib/dist/resolution/frameworks/index.d.ts.map +1 -1
  215. package/lib/dist/resolution/frameworks/index.js +5 -1
  216. package/lib/dist/resolution/frameworks/index.js.map +1 -1
  217. package/lib/dist/resolution/frameworks/java.js +6 -1
  218. package/lib/dist/resolution/frameworks/java.js.map +1 -1
  219. package/lib/dist/resolution/frameworks/python.d.ts.map +1 -1
  220. package/lib/dist/resolution/frameworks/python.js +7 -3
  221. package/lib/dist/resolution/frameworks/python.js.map +1 -1
  222. package/lib/dist/resolution/frameworks/react-native.d.ts.map +1 -1
  223. package/lib/dist/resolution/frameworks/react-native.js +53 -3
  224. package/lib/dist/resolution/frameworks/react-native.js.map +1 -1
  225. package/lib/dist/resolution/frameworks/react.d.ts.map +1 -1
  226. package/lib/dist/resolution/frameworks/react.js +15 -3
  227. package/lib/dist/resolution/frameworks/react.js.map +1 -1
  228. package/lib/dist/resolution/frameworks/svelte.js +5 -1
  229. package/lib/dist/resolution/frameworks/svelte.js.map +1 -1
  230. package/lib/dist/resolution/frameworks/vue.js +24 -27
  231. package/lib/dist/resolution/frameworks/vue.js.map +1 -1
  232. package/lib/dist/resolution/import-resolver.d.ts +10 -0
  233. package/lib/dist/resolution/import-resolver.d.ts.map +1 -1
  234. package/lib/dist/resolution/import-resolver.js +564 -2
  235. package/lib/dist/resolution/import-resolver.js.map +1 -1
  236. package/lib/dist/resolution/index.d.ts +80 -0
  237. package/lib/dist/resolution/index.d.ts.map +1 -1
  238. package/lib/dist/resolution/index.js +457 -7
  239. package/lib/dist/resolution/index.js.map +1 -1
  240. package/lib/dist/resolution/name-matcher.d.ts +61 -0
  241. package/lib/dist/resolution/name-matcher.d.ts.map +1 -1
  242. package/lib/dist/resolution/name-matcher.js +590 -14
  243. package/lib/dist/resolution/name-matcher.js.map +1 -1
  244. package/lib/dist/resolution/types.d.ts +27 -3
  245. package/lib/dist/resolution/types.d.ts.map +1 -1
  246. package/lib/dist/resolution/workspace-packages.d.ts +48 -0
  247. package/lib/dist/resolution/workspace-packages.d.ts.map +1 -0
  248. package/lib/dist/resolution/workspace-packages.js +208 -0
  249. package/lib/dist/resolution/workspace-packages.js.map +1 -0
  250. package/lib/dist/search/query-utils.d.ts +17 -1
  251. package/lib/dist/search/query-utils.d.ts.map +1 -1
  252. package/lib/dist/search/query-utils.js +79 -10
  253. package/lib/dist/search/query-utils.js.map +1 -1
  254. package/lib/dist/sync/watcher.d.ts +124 -32
  255. package/lib/dist/sync/watcher.d.ts.map +1 -1
  256. package/lib/dist/sync/watcher.js +326 -111
  257. package/lib/dist/sync/watcher.js.map +1 -1
  258. package/lib/dist/telemetry/index.d.ts +146 -0
  259. package/lib/dist/telemetry/index.d.ts.map +1 -0
  260. package/lib/dist/telemetry/index.js +544 -0
  261. package/lib/dist/telemetry/index.js.map +1 -0
  262. package/lib/dist/types.d.ts +17 -2
  263. package/lib/dist/types.d.ts.map +1 -1
  264. package/lib/dist/types.js +3 -0
  265. package/lib/dist/types.js.map +1 -1
  266. package/lib/dist/upgrade/index.d.ts +132 -0
  267. package/lib/dist/upgrade/index.d.ts.map +1 -0
  268. package/lib/dist/upgrade/index.js +462 -0
  269. package/lib/dist/upgrade/index.js.map +1 -0
  270. package/lib/dist/utils.d.ts +30 -24
  271. package/lib/dist/utils.d.ts.map +1 -1
  272. package/lib/dist/utils.js +64 -48
  273. package/lib/dist/utils.js.map +1 -1
  274. package/lib/node_modules/.package-lock.json +1 -29
  275. package/lib/package.json +1 -2
  276. package/package.json +1 -1
  277. package/lib/node_modules/chokidar/LICENSE +0 -21
  278. package/lib/node_modules/chokidar/README.md +0 -305
  279. package/lib/node_modules/chokidar/esm/handler.d.ts +0 -90
  280. package/lib/node_modules/chokidar/esm/handler.js +0 -629
  281. package/lib/node_modules/chokidar/esm/index.d.ts +0 -215
  282. package/lib/node_modules/chokidar/esm/index.js +0 -798
  283. package/lib/node_modules/chokidar/esm/package.json +0 -1
  284. package/lib/node_modules/chokidar/handler.d.ts +0 -90
  285. package/lib/node_modules/chokidar/handler.js +0 -635
  286. package/lib/node_modules/chokidar/index.d.ts +0 -215
  287. package/lib/node_modules/chokidar/index.js +0 -804
  288. package/lib/node_modules/chokidar/package.json +0 -69
  289. package/lib/node_modules/readdirp/LICENSE +0 -21
  290. package/lib/node_modules/readdirp/README.md +0 -120
  291. package/lib/node_modules/readdirp/esm/index.d.ts +0 -108
  292. package/lib/node_modules/readdirp/esm/index.js +0 -257
  293. package/lib/node_modules/readdirp/esm/package.json +0 -1
  294. package/lib/node_modules/readdirp/index.d.ts +0 -108
  295. package/lib/node_modules/readdirp/index.js +0 -263
  296. package/lib/node_modules/readdirp/package.json +0 -70
@@ -17,6 +17,10 @@ const MAX_JSX_CHILDREN = 30;
17
17
  // event bindings (@click="fn" / v-on:click="fn"). PascalCase children (<VPNav/>)
18
18
  // are already caught by JSX_TAG_RE via the SFC component node.
19
19
  const VUE_KEBAB_RE = /<([a-z][a-z0-9]*(?:-[a-z0-9]+)+)[\s/>]/g;
20
+ // PascalCase component tags — `<MediaCard ...>`, `<NavBar/>`. HTML elements are
21
+ // lowercase, so an uppercase-initial tag is a component usage; built-ins
22
+ // (`<NuxtLink>`, `<Transition>`) simply resolve to nothing and emit no edge.
23
+ const VUE_PASCAL_RE = /<([A-Z][A-Za-z0-9]*)[\s/>]/g;
20
24
  const VUE_HANDLER_RE = /(?:@|v-on:)([a-zA-Z][\w-]*)(?:\.[\w]+)*\s*=\s*"([^"]+)"/g;
21
25
  // Composable/hook destructure: `const { close: closeSidebar } = useSidebarControl()`.
22
26
  // Captures the destructure body + the called composable; only `use*` calls qualify.
@@ -36,6 +40,33 @@ const CC_FANOUT_CAP = 8; // skip a field name with more dispatchers/registrars t
36
40
  function kebabToPascal(s) {
37
41
  return s.split('-').map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('');
38
42
  }
43
+ /**
44
+ * Nuxt auto-import name for a component, derived from its path UNDER `components/`:
45
+ * `components/media/Card.vue` → `MediaCard`, `components/base/foo/Bar.vue` →
46
+ * `BaseFooBar`. Each directory segment and the filename is PascalCased and
47
+ * concatenated; a directory whose PascalCase name prefixes the next segment is
48
+ * collapsed (Nuxt's de-dup: `base/BaseButton.vue` → `BaseButton`, not
49
+ * `BaseBaseButton`). Returns null for a flat component (`components/NavBar.vue`)
50
+ * — its node is already named by basename, so a direct tag match finds it.
51
+ */
52
+ function nuxtComponentName(filePath) {
53
+ const marker = filePath.lastIndexOf('components/');
54
+ if (marker === -1)
55
+ return null;
56
+ const rel = filePath.slice(marker + 'components/'.length).replace(/\.(vue|ts|tsx|js|jsx)$/i, '');
57
+ const segs = rel.split('/').filter(Boolean).map(kebabToPascal);
58
+ if (segs.length < 2)
59
+ return null;
60
+ const out = [];
61
+ for (const s of segs) {
62
+ const prev = out[out.length - 1];
63
+ if (prev && s.startsWith(prev))
64
+ out[out.length - 1] = s;
65
+ else
66
+ out.push(s);
67
+ }
68
+ return out.join('');
69
+ }
39
70
  function sliceLines(content, startLine, endLine) {
40
71
  if (!startLine || !endLine)
41
72
  return null;
@@ -69,12 +100,21 @@ function enclosingFn(nodesInFile, line) {
69
100
  }
70
101
  return best;
71
102
  }
103
+ /**
104
+ * Stream method + function nodes lazily. The synthesizers only scan-and-filter
105
+ * down to a tiny matched subset, so materializing every function/method (which
106
+ * is gigabytes on a symbol-dense project) just to iterate it once is what OOM'd
107
+ * #610. Iterating keeps memory O(1) in the node count.
108
+ */
109
+ function* methodAndFunctionNodes(queries) {
110
+ yield* queries.iterateNodesByKind('method');
111
+ yield* queries.iterateNodesByKind('function');
112
+ }
72
113
  /** Phase 1: field-backed observer channels (registrar/dispatcher share a store). */
73
114
  function fieldChannelEdges(queries, ctx) {
74
- const candidates = [...queries.getNodesByKind('method'), ...queries.getNodesByKind('function')];
75
115
  const registrars = [];
76
116
  const dispatchers = [];
77
- for (const m of candidates) {
117
+ for (const m of methodAndFunctionNodes(queries)) {
78
118
  const isReg = REGISTRAR_NAME.test(m.name);
79
119
  const isDisp = DISPATCHER_NAME.test(m.name);
80
120
  if (!isReg && !isDisp)
@@ -156,7 +196,6 @@ function fieldChannelEdges(queries, ctx) {
156
196
  * name shared across unrelated classes can't fan out into noise.
157
197
  */
158
198
  function closureCollectionEdges(queries, ctx) {
159
- const candidates = [...queries.getNodesByKind('method'), ...queries.getNodesByKind('function')];
160
199
  const dispatchers = new Map(); // field → dispatcher methods + forEach line
161
200
  const registrars = new Map(); // field → registrar methods + append line
162
201
  const addReg = (field, node, absLine) => {
@@ -167,7 +206,7 @@ function closureCollectionEdges(queries, ctx) {
167
206
  arr.push({ node, line: absLine });
168
207
  registrars.set(field, arr);
169
208
  };
170
- for (const m of candidates) {
209
+ for (const m of methodAndFunctionNodes(queries)) {
171
210
  const content = ctx.readFile(m.filePath);
172
211
  const src = content && sliceLines(content, m.startLine, m.endLine);
173
212
  if (!src)
@@ -446,8 +485,210 @@ function cppOverrideEdges(queries) {
446
485
  // and are added below; their concrete-side nodes can be a `struct` (Swift)
447
486
  // or an `object` (Scala) so the loop also iterates those kinds.
448
487
  const IFACE_OVERRIDE_LANGS = new Set([
449
- 'java', 'kotlin', 'csharp', 'typescript', 'javascript', 'swift', 'scala',
488
+ 'java', 'kotlin', 'csharp', 'typescript', 'javascript', 'swift', 'scala', 'go', 'rust',
450
489
  ]);
490
+ /**
491
+ * Go implicit interface satisfaction (#584). Go has no `implements` keyword — a
492
+ * struct satisfies an interface structurally when its method set covers the
493
+ * interface's. Synthesize the missing `implements` edge (struct → interface) by
494
+ * matching method-NAME sets, so impl-navigation works and the interface-dispatch
495
+ * bridge ({@link interfaceOverrideEdges}, now 'go'-enabled) can link an interface
496
+ * method call to the concrete overrides.
497
+ *
498
+ * Name-only matching (signatures ignored) — over-approximation accepted, in line
499
+ * with the other dispatch synthesizers; capped per interface. Empty interfaces
500
+ * (`any`) are skipped so they don't match every struct.
501
+ */
502
+ function goImplementsEdges(queries) {
503
+ const edges = [];
504
+ const seen = new Set();
505
+ const methodNameSet = (id) => new Set(queries
506
+ .getOutgoingEdges(id, ['contains'])
507
+ .map((e) => queries.getNodeById(e.target))
508
+ .filter((n) => !!n && n.kind === 'method')
509
+ .map((n) => n.name));
510
+ const goStructs = queries.getNodesByKind('struct').filter((s) => s.language === 'go');
511
+ const structMethods = new Map();
512
+ for (const s of goStructs)
513
+ structMethods.set(s.id, methodNameSet(s.id));
514
+ for (const iface of queries.getNodesByKind('interface')) {
515
+ if (iface.language !== 'go')
516
+ continue;
517
+ const want = methodNameSet(iface.id);
518
+ if (want.size === 0)
519
+ continue; // empty interface (`any`) — would match everything
520
+ let added = 0;
521
+ for (const s of goStructs) {
522
+ if (added >= MAX_CALLBACKS_PER_CHANNEL)
523
+ break;
524
+ const have = structMethods.get(s.id);
525
+ if (!have || have.size < want.size)
526
+ continue;
527
+ let all = true;
528
+ for (const m of want) {
529
+ if (!have.has(m)) {
530
+ all = false;
531
+ break;
532
+ }
533
+ }
534
+ if (!all)
535
+ continue;
536
+ const key = `${s.id}>${iface.id}`;
537
+ if (seen.has(key))
538
+ continue;
539
+ seen.add(key);
540
+ edges.push({
541
+ source: s.id,
542
+ target: iface.id,
543
+ kind: 'implements',
544
+ line: s.startLine,
545
+ provenance: 'heuristic',
546
+ metadata: { synthesizedBy: 'go-implements', via: iface.name, registeredAt: `${s.filePath}:${s.startLine}` },
547
+ });
548
+ added++;
549
+ }
550
+ }
551
+ return edges;
552
+ }
553
+ /**
554
+ * Cross-file Go method → receiver-type `contains` edges. In Go a type's methods
555
+ * are commonly declared in a different file from the `type` declaration itself
556
+ * (`type User struct{…}` in `user.go`, `func (u *User) Save()` in
557
+ * `user_store.go`). Extraction attaches the struct→method `contains` edge only
558
+ * when the receiver type is in the SAME file — the owner lookup in
559
+ * `tree-sitter.ts` is scoped to the file being parsed — so a cross-file method
560
+ * is left orphaned from its type (it's still `contains`ed by its file, just not
561
+ * its struct). That breaks `codegraph_node` member outlines, any
562
+ * callers/callees/impact traversal that goes through the type's `contains`
563
+ * edges, and the {@link goImplementsEdges} method-set computation (which derives
564
+ * a struct's method set from those same edges, so it under-counts interfaces a
565
+ * cross-file struct satisfies).
566
+ *
567
+ * Go guarantees a method's receiver type is declared in the SAME PACKAGE as the
568
+ * method, and a Go package is a single directory — so this is a deterministic
569
+ * structural link, not a heuristic: find the same-named type in the method's own
570
+ * directory and add the missing `contains` edge (no `provenance: 'heuristic'`,
571
+ * matching the same-file edges extraction already emits). Skips methods that
572
+ * already have a type parent (the same-file case). (#583, cross-file half)
573
+ */
574
+ function goCrossFileMethodContainsEdges(queries) {
575
+ const edges = [];
576
+ const seen = new Set();
577
+ const TYPE_KINDS = new Set(['struct', 'class', 'interface', 'enum', 'type_alias']);
578
+ const dirOf = (p) => {
579
+ const i = p.replace(/\\/g, '/').lastIndexOf('/');
580
+ return i >= 0 ? p.slice(0, i) : '';
581
+ };
582
+ for (const method of queries.getNodesByKind('method')) {
583
+ if (method.language !== 'go')
584
+ continue;
585
+ // The receiver type is encoded in the method's qualifiedName as `Recv::name`
586
+ // (extraction sets `${receiverType}::${name}` for receiver methods).
587
+ const qn = method.qualifiedName;
588
+ if (!qn)
589
+ continue;
590
+ const sep = qn.lastIndexOf('::');
591
+ if (sep <= 0)
592
+ continue;
593
+ const receiver = qn.slice(0, sep);
594
+ if (!receiver)
595
+ continue;
596
+ // Already attached to its type (same-file case handled at extraction)?
597
+ const hasTypeParent = queries
598
+ .getIncomingEdges(method.id, ['contains'])
599
+ .some((e) => {
600
+ const src = queries.getNodeById(e.source);
601
+ return src != null && TYPE_KINDS.has(src.kind);
602
+ });
603
+ if (hasTypeParent)
604
+ continue;
605
+ // Find the receiver type in the SAME directory (= same Go package). Go forbids
606
+ // duplicate type names within a package, so a same-name same-dir match is
607
+ // unambiguous; scoping to the directory avoids linking to a same-named type
608
+ // in another package.
609
+ const dir = dirOf(method.filePath);
610
+ const owner = queries
611
+ .getNodesByName(receiver)
612
+ .find((n) => n.language === 'go' && TYPE_KINDS.has(n.kind) && dirOf(n.filePath) === dir);
613
+ if (!owner)
614
+ continue;
615
+ const key = `${owner.id}>${method.id}`;
616
+ if (seen.has(key))
617
+ continue;
618
+ seen.add(key);
619
+ edges.push({ source: owner.id, target: method.id, kind: 'contains', line: method.startLine });
620
+ }
621
+ return edges;
622
+ }
623
+ /**
624
+ * Kotlin Multiplatform `expect`/`actual` linking. A `common` source set declares
625
+ * `expect fun foo()` / `expect class Bar`; each platform source set (jvm, native,
626
+ * js, …) provides an `actual` implementation with the IDENTICAL fully-qualified
627
+ * name in a different file. Callers in common code resolve to the `expect`
628
+ * declaration, so every `actual` impl ends up with zero dependents — invisible to
629
+ * impact/affected even though editing it can break every caller of the API.
630
+ *
631
+ * Synthesize a `calls` edge from the common declaration to each platform `actual`
632
+ * (mirroring the interface-impl bridge: abstract → concrete), so editing a
633
+ * platform impl surfaces the common `expect` and its callers, and the impl file
634
+ * participates in the graph.
635
+ *
636
+ * `expect`/`actual` are captured onto the node's `decorators` list at extraction
637
+ * (kotlin.ts `extractModifiers`). Members of an `expect class` are NOT themselves
638
+ * keyword-marked, so the declaration side is matched as the same-FQN, same-kind
639
+ * node that is NOT marked `actual`. Requiring an `actual`-marked counterpart also
640
+ * gates out plain cross-file overloads (neither side is marked).
641
+ */
642
+ // Kinds that an `expect`/`actual` pair may legitimately straddle. `expect class`
643
+ // is routinely fulfilled by an `actual typealias` (e.g. `actual typealias
644
+ // CancellationException = …`, `actual typealias SchedulerTask = Task`), so a
645
+ // strict kind match would miss those one-line alias files. Same-FQN + the
646
+ // `actual` marker already gates out unrelated symbols, so widening to the
647
+ // type-like kinds is safe.
648
+ const KMP_TYPE_KINDS = new Set(['class', 'interface', 'struct', 'enum', 'type_alias']);
649
+ function kmpKindsCompatible(a, b) {
650
+ return a === b || (KMP_TYPE_KINDS.has(a) && KMP_TYPE_KINDS.has(b));
651
+ }
652
+ function kotlinExpectActualEdges(queries) {
653
+ const edges = [];
654
+ const seen = new Set();
655
+ const actuals = queries
656
+ .getAllNodes()
657
+ .filter((n) => n.language === 'kotlin' && !!n.decorators?.includes('actual'));
658
+ for (const act of actuals) {
659
+ let added = 0;
660
+ for (const cand of queries.getNodesByQualifiedNameExact(act.qualifiedName)) {
661
+ if (added >= MAX_CALLBACKS_PER_CHANNEL)
662
+ break;
663
+ // The declaration side: same FQN + compatible kind, a different file, NOT
664
+ // itself an `actual` (that would be a sibling platform impl, not the decl).
665
+ if (cand.language !== 'kotlin' || cand.id === act.id)
666
+ continue;
667
+ if (!kmpKindsCompatible(cand.kind, act.kind) || cand.filePath === act.filePath)
668
+ continue;
669
+ if (cand.decorators?.includes('actual'))
670
+ continue;
671
+ const key = `${cand.id}>${act.id}`;
672
+ if (seen.has(key))
673
+ continue;
674
+ seen.add(key);
675
+ edges.push({
676
+ source: cand.id,
677
+ target: act.id,
678
+ kind: 'calls',
679
+ line: cand.startLine,
680
+ provenance: 'heuristic',
681
+ metadata: {
682
+ synthesizedBy: 'kotlin-expect-actual',
683
+ via: act.name,
684
+ registeredAt: `${act.filePath}:${act.startLine}`,
685
+ },
686
+ });
687
+ added++;
688
+ }
689
+ }
690
+ return edges;
691
+ }
451
692
  function interfaceOverrideEdges(queries) {
452
693
  const edges = [];
453
694
  const seen = new Set();
@@ -689,6 +930,17 @@ function vueTemplateEdges(ctx) {
689
930
  // A composable's returned member may be a fn (`function close(){}`) or an
690
931
  // arrow assigned to a const (`const close = () => {}`).
691
932
  const RETURN_KINDS = new Set(['method', 'function', 'variable', 'constant']);
933
+ // Nuxt auto-imports nested components by a DIRECTORY-PREFIXED name —
934
+ // `components/media/Card.vue` is used as `<MediaCard/>`, not `<Card/>` — but
935
+ // the component node is named by basename (`Card`), so a direct tag match
936
+ // misses it (flat components match by basename and don't need this). Map each
937
+ // nested component's Nuxt name → node so those template usages resolve.
938
+ const nuxtComponents = new Map();
939
+ for (const c of ctx.getNodesByKind('component')) {
940
+ const nn = nuxtComponentName(c.filePath);
941
+ if (nn && !nuxtComponents.has(nn))
942
+ nuxtComponents.set(nn, c);
943
+ }
692
944
  for (const file of ctx.getAllFiles()) {
693
945
  if (!file.endsWith('.vue'))
694
946
  continue;
@@ -734,8 +986,18 @@ function vueTemplateEdges(ctx) {
734
986
  };
735
987
  let m;
736
988
  VUE_KEBAB_RE.lastIndex = 0;
737
- while ((m = VUE_KEBAB_RE.exec(tpl)))
738
- addEdge(resolve(kebabToPascal(m[1]), COMPONENT_KINDS), { synthesizedBy: 'jsx-render', via: m[1] });
989
+ while ((m = VUE_KEBAB_RE.exec(tpl))) {
990
+ const tag = kebabToPascal(m[1]);
991
+ addEdge(resolve(tag, COMPONENT_KINDS) ?? nuxtComponents.get(tag), { synthesizedBy: 'jsx-render', via: m[1] });
992
+ }
993
+ // PascalCase component tags. Try a direct name match first (flat components
994
+ // and explicit registrations), then the Nuxt dir-prefixed auto-import name
995
+ // (`<MediaCard>` → components/media/Card.vue). Built-ins match neither → no edge.
996
+ VUE_PASCAL_RE.lastIndex = 0;
997
+ while ((m = VUE_PASCAL_RE.exec(tpl))) {
998
+ const tag = m[1];
999
+ addEdge(resolve(tag, COMPONENT_KINDS) ?? nuxtComponents.get(tag), { synthesizedBy: 'jsx-render', via: tag });
1000
+ }
739
1001
  VUE_HANDLER_RE.lastIndex = 0;
740
1002
  while ((m = VUE_HANDLER_RE.exec(tpl))) {
741
1003
  const event = m[1];
@@ -805,6 +1067,14 @@ const RN_SWIFT_SEND_RE = /\bsendEvent\s*\(\s*withName\s*:\s*"([^"]+)"/g;
805
1067
  // JVM source files in the consumer so we don't re-process JS emits
806
1068
  // (which `eventEmitterEdges` already handles).
807
1069
  const RN_JVM_EMIT_RE = /\.emit\s*\(\s*"([^"]+)"\s*,/g;
1070
+ // Custom `sendEvent(reactContext, "X", body)` wrapper — extremely common
1071
+ // (react-native-device-info and many libs wrap `DeviceEventManagerModule…emit`
1072
+ // behind a helper whose `.emit(eventName, …)` uses a VARIABLE, so RN_JVM_EMIT_RE
1073
+ // misses it; the literal lives in the wrapper CALL instead). Captures the first
1074
+ // string literal inside a `sendEvent(...)` call. `[^;{}]*?` keeps it on one
1075
+ // statement and stops at a block boundary, so the wrapper DEFINITION (whose `(`
1076
+ // is followed by `… ) {`) never matches. Multi-line tolerant. (java/kotlin/swift)
1077
+ const RN_NATIVE_SENDEVENT_RE = /\bsendEvent\s*\([^;{}]*?"([^"]+)"/g;
808
1078
  function rnEventEdges(ctx) {
809
1079
  // Native dispatchers (source = the native method whose body sends the
810
1080
  // event) and JS handlers (target = the function/method registered as
@@ -843,17 +1113,28 @@ function rnEventEdges(ctx) {
843
1113
  if (m[1])
844
1114
  addDispatcher(m[1], lineOf(m.index));
845
1115
  }
1116
+ RN_NATIVE_SENDEVENT_RE.lastIndex = 0;
1117
+ while ((m = RN_NATIVE_SENDEVENT_RE.exec(content))) {
1118
+ if (m[1])
1119
+ addDispatcher(m[1], lineOf(m.index));
1120
+ }
846
1121
  }
847
- // JVM side: `.emit("X", …)` in Java/Kotlin. (We pattern-match
848
- // anywhere in the file; the JS in-language path uses a separate
849
- // emitter object pattern and is already handled by eventEmitterEdges.)
1122
+ // JVM side: `.emit("X", …)` in Java/Kotlin, plus the common
1123
+ // `sendEvent(ctx, "X", body)` wrapper. (We pattern-match anywhere in the
1124
+ // file; the JS in-language path uses a separate emitter object pattern and
1125
+ // is already handled by eventEmitterEdges.)
850
1126
  if (file.endsWith('.java') || file.endsWith('.kt')) {
851
- RN_JVM_EMIT_RE.lastIndex = 0;
852
1127
  let m;
1128
+ RN_JVM_EMIT_RE.lastIndex = 0;
853
1129
  while ((m = RN_JVM_EMIT_RE.exec(content))) {
854
1130
  if (m[1])
855
1131
  addDispatcher(m[1], lineOf(m.index));
856
1132
  }
1133
+ RN_NATIVE_SENDEVENT_RE.lastIndex = 0;
1134
+ while ((m = RN_NATIVE_SENDEVENT_RE.exec(content))) {
1135
+ if (m[1])
1136
+ addDispatcher(m[1], lineOf(m.index));
1137
+ }
857
1138
  }
858
1139
  // JS subscribers (.addListener("X", handler)). Restrict to JS-family
859
1140
  // files so a native file's `addListener:` (the ObjC method) doesn't
@@ -977,6 +1258,142 @@ function rnEventEdges(ctx) {
977
1258
  * conflict otherwise).
978
1259
  */
979
1260
  const FABRIC_NATIVE_SUFFIXES = ['', 'View', 'ViewManager', 'ComponentView', 'Manager'];
1261
+ /**
1262
+ * Expo Modules cross-platform pairing. An Expo Module exposes the SAME
1263
+ * JS-visible method (`AsyncFunction("getBatteryLevelAsync")`) from BOTH an iOS
1264
+ * (Swift) and an Android (Kotlin) implementation. A JS callsite name-resolves to
1265
+ * only ONE of them, so the other platform's impl looked like nothing called it
1266
+ * (and editing it showed no blast radius). Link the iOS and Android impls of the
1267
+ * same `<module>.<method>` to each other (both directions), so a JS call that
1268
+ * reaches one platform reaches the other, and editing either surfaces the JS
1269
+ * caller. The Expo method nodes are id-prefixed `expo-module:` and qualified
1270
+ * `<file>::<module>.<method>` by the framework extractor.
1271
+ */
1272
+ function expoCrossPlatformEdges(queries) {
1273
+ const edges = [];
1274
+ const seen = new Set();
1275
+ const byKey = new Map();
1276
+ for (const m of queries.getNodesByKind('method')) {
1277
+ if (!m.id.startsWith('expo-module:'))
1278
+ continue;
1279
+ const key = m.qualifiedName.split('::').pop(); // `<module>.<method>`
1280
+ if (!key)
1281
+ continue;
1282
+ const arr = byKey.get(key);
1283
+ if (arr)
1284
+ arr.push(m);
1285
+ else
1286
+ byKey.set(key, [m]);
1287
+ }
1288
+ for (const group of byKey.values()) {
1289
+ if (group.length < 2)
1290
+ continue;
1291
+ for (const a of group) {
1292
+ for (const b of group) {
1293
+ if (a.id === b.id || a.language === b.language)
1294
+ continue; // cross-platform only
1295
+ const key = `${a.id}>${b.id}`;
1296
+ if (seen.has(key))
1297
+ continue;
1298
+ seen.add(key);
1299
+ edges.push({
1300
+ source: a.id,
1301
+ target: b.id,
1302
+ kind: 'calls',
1303
+ line: a.startLine,
1304
+ provenance: 'heuristic',
1305
+ metadata: { synthesizedBy: 'expo-cross-platform', via: a.name },
1306
+ });
1307
+ }
1308
+ }
1309
+ }
1310
+ return edges;
1311
+ }
1312
+ /**
1313
+ * Classic React Native NativeModules cross-platform pairing. A native module
1314
+ * method (`@ReactMethod` on Android, `RCT_EXPORT_METHOD` on iOS) is implemented
1315
+ * on BOTH platforms, but a JS callsite name-resolves to only ONE — so the other
1316
+ * platform's impl looked like nothing called it. A native method that HAS a JS
1317
+ * caller is a confirmed bridge method; link it to the same-named native method
1318
+ * in another language (the other platform's impl) so a JS call reaching one
1319
+ * platform reaches the other, and editing either surfaces the JS caller.
1320
+ *
1321
+ * Names are normalized to the first selector keyword (`getFreeDiskStorage:` →
1322
+ * `getFreeDiskStorage`) — that's the JS-visible name, and how the iOS selector
1323
+ * lines up with the bare Android method name.
1324
+ */
1325
+ function rnCrossPlatformEdges(queries) {
1326
+ const edges = [];
1327
+ const seen = new Set();
1328
+ const NATIVE = new Set(['java', 'kotlin', 'objc', 'cpp']);
1329
+ const JS = new Set(['typescript', 'tsx', 'javascript', 'jsx']);
1330
+ // RN module INFRASTRUCTURE methods exist on every native module (called by the
1331
+ // RN runtime, not user JS), so pairing them by name would cross-link unrelated
1332
+ // modules in a multi-module repo. Skip them — they aren't user-facing methods.
1333
+ const RN_INFRA = new Set([
1334
+ 'addListener', 'removeListeners', 'getConstants', 'constantsToExport', 'getName',
1335
+ 'invalidate', 'initialize', 'getDefaultEventTypes', 'supportedEvents',
1336
+ 'requiresMainQueueSetup', 'methodQueue',
1337
+ ]);
1338
+ const norm = (name) => {
1339
+ const i = name.indexOf(':');
1340
+ return i >= 0 ? name.slice(0, i) : name;
1341
+ };
1342
+ // Index native methods by their JS-visible (normalized) name. Only names with
1343
+ // impls in ≥2 native languages can pair, so the per-method JS-caller check
1344
+ // below only runs for genuine cross-platform candidates.
1345
+ const byName = new Map();
1346
+ for (const m of queries.iterateNodesByKind('method')) {
1347
+ if (!NATIVE.has(m.language))
1348
+ continue;
1349
+ const key = norm(m.name);
1350
+ const arr = byName.get(key);
1351
+ if (arr)
1352
+ arr.push(m);
1353
+ else
1354
+ byName.set(key, [m]);
1355
+ }
1356
+ for (const [groupName, group] of byName) {
1357
+ if (RN_INFRA.has(groupName))
1358
+ continue;
1359
+ const langs = new Set(group.map((m) => m.language));
1360
+ if (langs.size < 2)
1361
+ continue; // single-platform — nothing to pair
1362
+ for (const m of group) {
1363
+ // Is m a bridge method? (a JS-language `calls` edge points at it)
1364
+ const incoming = queries.getIncomingEdges(m.id, ['calls']);
1365
+ if (incoming.length === 0)
1366
+ continue;
1367
+ const sources = queries.getNodesByIds(incoming.map((e) => e.source));
1368
+ const isBridge = incoming.some((e) => {
1369
+ const s = sources.get(e.source);
1370
+ return !!s && JS.has(s.language);
1371
+ });
1372
+ if (!isBridge)
1373
+ continue;
1374
+ // Link to the other-platform impls (both directions).
1375
+ for (const sib of group) {
1376
+ if (sib.id === m.id || sib.language === m.language)
1377
+ continue;
1378
+ for (const [a, b] of [[m, sib], [sib, m]]) {
1379
+ const key = `${a.id}>${b.id}`;
1380
+ if (seen.has(key))
1381
+ continue;
1382
+ seen.add(key);
1383
+ edges.push({
1384
+ source: a.id,
1385
+ target: b.id,
1386
+ kind: 'calls',
1387
+ line: a.startLine,
1388
+ provenance: 'heuristic',
1389
+ metadata: { synthesizedBy: 'rn-cross-platform', via: norm(m.name) },
1390
+ });
1391
+ }
1392
+ }
1393
+ }
1394
+ }
1395
+ return edges;
1396
+ }
980
1397
  function fabricNativeImplEdges(ctx) {
981
1398
  const edges = [];
982
1399
  const seen = new Set();
@@ -1045,7 +1462,7 @@ function mybatisJavaXmlEdges(queries) {
1045
1462
  const seen = new Set();
1046
1463
  // Index Java methods by `<ClassName>::<methodName>` for O(1) lookup.
1047
1464
  const javaIndex = new Map();
1048
- for (const m of queries.getNodesByKind('method')) {
1465
+ for (const m of queries.iterateNodesByKind('method')) {
1049
1466
  if (m.language !== 'java' && m.language !== 'kotlin')
1050
1467
  continue;
1051
1468
  const parts = m.qualifiedName.split('::');
@@ -1060,7 +1477,7 @@ function mybatisJavaXmlEdges(queries) {
1060
1477
  else
1061
1478
  javaIndex.set(key, [m]);
1062
1479
  }
1063
- for (const xml of queries.getNodesByKind('method')) {
1480
+ for (const xml of queries.iterateNodesByKind('method')) {
1064
1481
  if (xml.language !== 'xml')
1065
1482
  continue;
1066
1483
  // Qualified name: `<namespace>::<id>`. Extract the simple class name.
@@ -1176,13 +1593,15 @@ function goHandlerIdent(expr) {
1176
1593
  }
1177
1594
  function ginMiddlewareChainEdges(queries, ctx) {
1178
1595
  // 1. Find the chain dispatcher(s): a Go method that invokes a `handlers` slice by index.
1179
- const dispatchers = queries.getNodesByKind('method').filter((n) => {
1596
+ const dispatchers = [];
1597
+ for (const n of queries.iterateNodesByKind('method')) {
1180
1598
  if (n.language !== 'go')
1181
- return false;
1599
+ continue;
1182
1600
  const content = ctx.readFile(n.filePath);
1183
1601
  const src = content && sliceLines(content, n.startLine, n.endLine);
1184
- return !!src && GIN_DISPATCH_RE.test(src);
1185
- });
1602
+ if (src && GIN_DISPATCH_RE.test(src))
1603
+ dispatchers.push(n);
1604
+ }
1186
1605
  if (dispatchers.length === 0)
1187
1606
  return []; // not a gin repo — bail
1188
1607
  // 2. Collect handler identifiers registered via gin registration calls
@@ -1238,25 +1657,129 @@ function ginMiddlewareChainEdges(queries, ctx) {
1238
1657
  }
1239
1658
  return edges;
1240
1659
  }
1660
+ /**
1661
+ * Delphi form code-behind: a form unit `UFRMAbout.pas` owns its visual form
1662
+ * definition `UFRMAbout.dfm` (VCL) / `.fmx` (FireMonkey) — paired by basename in
1663
+ * the same directory, wired by the `{$R *.dfm}` directive rather than a `uses`
1664
+ * clause. Link the unit → its form so a `.dfm`/`.fmx` used only as a form
1665
+ * definition isn't orphaned, and editing the form surfaces its code-behind unit.
1666
+ */
1667
+ function pascalFormEdges(ctx) {
1668
+ const edges = [];
1669
+ const allFiles = new Set(ctx.getAllFiles());
1670
+ for (const file of allFiles) {
1671
+ if (!/\.(dfm|fmx)$/i.test(file))
1672
+ continue;
1673
+ const pasFile = file.replace(/\.(dfm|fmx)$/i, '.pas');
1674
+ if (!allFiles.has(pasFile))
1675
+ continue;
1676
+ const formNode = ctx.getNodesInFile(file).find((n) => n.kind === 'file');
1677
+ const unitNode = ctx.getNodesInFile(pasFile).find((n) => n.kind === 'file');
1678
+ if (!formNode || !unitNode)
1679
+ continue;
1680
+ edges.push({
1681
+ source: unitNode.id,
1682
+ target: formNode.id,
1683
+ kind: 'references',
1684
+ line: unitNode.startLine,
1685
+ provenance: 'heuristic',
1686
+ metadata: { synthesizedBy: 'pascal-form', registeredAt: pasFile },
1687
+ });
1688
+ }
1689
+ return edges;
1690
+ }
1691
+ /**
1692
+ * SvelteKit file-convention data flow. A route directory's `+page.svelte` (a
1693
+ * `component` node) receives its `data` from the sibling `+page.server.{ts,js}`
1694
+ * / `+page.{ts,js}` `load` function and posts forms to its `actions` — wired by
1695
+ * the framework BY FILE PATH, with no static import between them. So editing a
1696
+ * `load` shows no impact on the page it feeds, and the page looks like it has no
1697
+ * server-side dependency. Link the page component to its sibling loader's
1698
+ * `load` / `actions` (same for `+layout`). The pairing is path-deterministic
1699
+ * (same directory, matching `+page`/`+layout` prefix), so it's precise — but
1700
+ * it's a framework-convention edge, so provenance stays `heuristic`.
1701
+ *
1702
+ * Direction: page → load, so `getImpactRadius(load)` surfaces the page (editing
1703
+ * a loader's data shows the page it feeds) and the page's dependencies include
1704
+ * its loader.
1705
+ */
1706
+ function svelteKitLoadEdges(ctx) {
1707
+ const edges = [];
1708
+ const allFiles = new Set(ctx.getAllFiles());
1709
+ const HOOKS = new Set(['load', 'actions']);
1710
+ const HOOK_KINDS = new Set(['function', 'method', 'constant', 'variable']);
1711
+ for (const file of allFiles) {
1712
+ const m = file.match(/(.*\/)(\+(?:page|layout))\.svelte$/);
1713
+ if (!m)
1714
+ continue;
1715
+ const dir = m[1];
1716
+ const prefix = m[2];
1717
+ const page = ctx.getNodesInFile(file).find((n) => n.kind === 'component');
1718
+ if (!page)
1719
+ continue;
1720
+ for (const ext of ['.server.ts', '.server.js', '.ts', '.js']) {
1721
+ const loaderFile = `${dir}${prefix}${ext}`;
1722
+ if (!allFiles.has(loaderFile))
1723
+ continue;
1724
+ for (const hook of ctx.getNodesInFile(loaderFile)) {
1725
+ if (!HOOK_KINDS.has(hook.kind) || !HOOKS.has(hook.name))
1726
+ continue;
1727
+ edges.push({
1728
+ source: page.id,
1729
+ target: hook.id,
1730
+ kind: 'references',
1731
+ line: page.startLine,
1732
+ provenance: 'heuristic',
1733
+ metadata: {
1734
+ synthesizedBy: 'sveltekit-load',
1735
+ via: hook.name,
1736
+ registeredAt: `${loaderFile}:${hook.startLine ?? 0}`,
1737
+ },
1738
+ });
1739
+ }
1740
+ }
1741
+ }
1742
+ return edges;
1743
+ }
1241
1744
  /**
1242
1745
  * Synthesize dispatcher→callback edges (field observers + EventEmitters +
1243
- * React re-render + JSX children + Vue templates + RN event channel +
1244
- * Fabric native-impl + MyBatis Java↔XML + Gin middleware chain). Returns the
1245
- * count added. Never throws into indexing — callers wrap in try/catch.
1746
+ * React re-render + JSX children + Vue templates + SvelteKit load + RN event
1747
+ * channel + Fabric native-impl + MyBatis Java↔XML + Gin middleware chain).
1748
+ * Returns the count added. Never throws into indexing — callers wrap in try/catch.
1246
1749
  */
1247
1750
  function synthesizeCallbackEdges(queries, ctx) {
1751
+ // Cross-file Go method→type `contains` edges must be synthesized AND persisted
1752
+ // FIRST: a method declared in a different file from its receiver type is
1753
+ // otherwise orphaned from the struct, and goImplementsEdges (next) derives a
1754
+ // struct's method set from its `contains` edges — so without this it would
1755
+ // under-count the interfaces a cross-file struct satisfies. (#583)
1756
+ const goMethodContains = goCrossFileMethodContainsEdges(queries);
1757
+ if (goMethodContains.length > 0)
1758
+ queries.insertEdges(goMethodContains);
1759
+ // Go implicit `implements` edges must be synthesized AND persisted next: the
1760
+ // interface-dispatch bridge below reads `implements` edges from the DB, and
1761
+ // Go has none statically. (Other languages already have static implements
1762
+ // edges from extraction, so they don't need this pre-pass.)
1763
+ const goImpl = goImplementsEdges(queries);
1764
+ if (goImpl.length > 0)
1765
+ queries.insertEdges(goImpl);
1248
1766
  const fieldEdges = fieldChannelEdges(queries, ctx);
1249
1767
  const closureCollEdges = closureCollectionEdges(queries, ctx);
1250
1768
  const emitterEdges = eventEmitterEdges(ctx);
1251
1769
  const renderEdges = reactRenderEdges(queries, ctx);
1252
1770
  const jsxEdges = reactJsxChildEdges(ctx);
1253
1771
  const vueEdges = vueTemplateEdges(ctx);
1772
+ const svelteKitEdges = svelteKitLoadEdges(ctx);
1773
+ const pascalEdges = pascalFormEdges(ctx);
1254
1774
  const flutterEdges = flutterBuildEdges(queries, ctx);
1255
1775
  const cppEdges = cppOverrideEdges(queries);
1256
1776
  const ifaceEdges = interfaceOverrideEdges(queries);
1777
+ const kotlinExpectActual = kotlinExpectActualEdges(queries);
1257
1778
  const goGrpcEdges = goGrpcStubImplEdges(queries);
1258
1779
  const rnEventEdgesList = rnEventEdges(ctx);
1259
1780
  const fabricNativeEdges = fabricNativeImplEdges(ctx);
1781
+ const expoXPlatEdges = expoCrossPlatformEdges(queries);
1782
+ const rnXPlatEdges = rnCrossPlatformEdges(queries);
1260
1783
  const mybatisEdges = mybatisJavaXmlEdges(queries);
1261
1784
  const ginEdges = ginMiddlewareChainEdges(queries, ctx);
1262
1785
  const merged = [];
@@ -1268,12 +1791,17 @@ function synthesizeCallbackEdges(queries, ctx) {
1268
1791
  ...renderEdges,
1269
1792
  ...jsxEdges,
1270
1793
  ...vueEdges,
1794
+ ...svelteKitEdges,
1795
+ ...pascalEdges,
1271
1796
  ...flutterEdges,
1272
1797
  ...cppEdges,
1273
1798
  ...ifaceEdges,
1799
+ ...kotlinExpectActual,
1274
1800
  ...goGrpcEdges,
1275
1801
  ...rnEventEdgesList,
1276
1802
  ...fabricNativeEdges,
1803
+ ...expoXPlatEdges,
1804
+ ...rnXPlatEdges,
1277
1805
  ...mybatisEdges,
1278
1806
  ...ginEdges,
1279
1807
  ]) {
@@ -1285,6 +1813,6 @@ function synthesizeCallbackEdges(queries, ctx) {
1285
1813
  }
1286
1814
  if (merged.length > 0)
1287
1815
  queries.insertEdges(merged);
1288
- return merged.length;
1816
+ return merged.length + goImpl.length + goMethodContains.length;
1289
1817
  }
1290
1818
  //# sourceMappingURL=callback-synthesizer.js.map