@quolu/lattice 0.70.0 → 0.71.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 (602) hide show
  1. package/package.json +1 -1
  2. package/sensor/dist/bin/lattice-sensor.js +746 -323
  3. package/sensor/dist/bin/viewer-gate.d.ts +17 -0
  4. package/sensor/dist/bin/viewer-gate.d.ts.map +1 -0
  5. package/sensor/dist/bin/viewer-gate.js +31 -0
  6. package/sensor/dist/bin/viewer-gate.js.map +1 -0
  7. package/sensor/dist/context/formatter.d.ts +7 -1
  8. package/sensor/dist/context/formatter.d.ts.map +1 -1
  9. package/sensor/dist/context/formatter.js +12 -6
  10. package/sensor/dist/context/formatter.js.map +1 -1
  11. package/sensor/dist/context/index.d.ts +12 -0
  12. package/sensor/dist/context/index.d.ts.map +1 -1
  13. package/sensor/dist/context/index.js +103 -16
  14. package/sensor/dist/context/index.js.map +1 -1
  15. package/sensor/dist/db/index.d.ts +23 -2
  16. package/sensor/dist/db/index.d.ts.map +1 -1
  17. package/sensor/dist/db/index.js +109 -31
  18. package/sensor/dist/db/index.js.map +1 -1
  19. package/sensor/dist/db/migrations.d.ts +1 -1
  20. package/sensor/dist/db/migrations.d.ts.map +1 -1
  21. package/sensor/dist/db/migrations.js +68 -1
  22. package/sensor/dist/db/migrations.js.map +1 -1
  23. package/sensor/dist/db/queries.d.ts +583 -6
  24. package/sensor/dist/db/queries.d.ts.map +1 -1
  25. package/sensor/dist/db/queries.js +1425 -57
  26. package/sensor/dist/db/queries.js.map +1 -1
  27. package/sensor/dist/db/schema.sql +27 -4
  28. package/sensor/dist/db/sqlite-adapter.d.ts +2 -0
  29. package/sensor/dist/db/sqlite-adapter.d.ts.map +1 -1
  30. package/sensor/dist/db/sqlite-adapter.js +56 -7
  31. package/sensor/dist/db/sqlite-adapter.js.map +1 -1
  32. package/sensor/dist/db/synthesis-stage.d.ts +12 -0
  33. package/sensor/dist/db/synthesis-stage.d.ts.map +1 -0
  34. package/sensor/dist/db/synthesis-stage.js +93 -0
  35. package/sensor/dist/db/synthesis-stage.js.map +1 -0
  36. package/sensor/dist/db/wal-valve.d.ts +32 -12
  37. package/sensor/dist/db/wal-valve.d.ts.map +1 -1
  38. package/sensor/dist/db/wal-valve.js +65 -22
  39. package/sensor/dist/db/wal-valve.js.map +1 -1
  40. package/sensor/dist/db/wsl-shared-index.d.ts +53 -0
  41. package/sensor/dist/db/wsl-shared-index.d.ts.map +1 -0
  42. package/sensor/dist/db/wsl-shared-index.js +121 -0
  43. package/sensor/dist/db/wsl-shared-index.js.map +1 -0
  44. package/sensor/dist/directory.d.ts +126 -2
  45. package/sensor/dist/directory.d.ts.map +1 -1
  46. package/sensor/dist/directory.js +285 -16
  47. package/sensor/dist/directory.js.map +1 -1
  48. package/sensor/dist/errors.d.ts +14 -0
  49. package/sensor/dist/errors.d.ts.map +1 -1
  50. package/sensor/dist/errors.js +16 -1
  51. package/sensor/dist/errors.js.map +1 -1
  52. package/sensor/dist/extraction/cfml-extractor.d.ts +1 -0
  53. package/sensor/dist/extraction/cfml-extractor.d.ts.map +1 -1
  54. package/sensor/dist/extraction/cfml-extractor.js +15 -3
  55. package/sensor/dist/extraction/cfml-extractor.js.map +1 -1
  56. package/sensor/dist/extraction/dfm-extractor.d.ts.map +1 -1
  57. package/sensor/dist/extraction/dfm-extractor.js +14 -10
  58. package/sensor/dist/extraction/dfm-extractor.js.map +1 -1
  59. package/sensor/dist/extraction/extraction-version.d.ts +1 -1
  60. package/sensor/dist/extraction/extraction-version.d.ts.map +1 -1
  61. package/sensor/dist/extraction/extraction-version.js +3 -1
  62. package/sensor/dist/extraction/extraction-version.js.map +1 -1
  63. package/sensor/dist/extraction/function-ref.d.ts +6 -4
  64. package/sensor/dist/extraction/function-ref.d.ts.map +1 -1
  65. package/sensor/dist/extraction/function-ref.js +35 -14
  66. package/sensor/dist/extraction/function-ref.js.map +1 -1
  67. package/sensor/dist/extraction/generated-detection.d.ts +48 -12
  68. package/sensor/dist/extraction/generated-detection.d.ts.map +1 -1
  69. package/sensor/dist/extraction/generated-detection.js +176 -11
  70. package/sensor/dist/extraction/generated-detection.js.map +1 -1
  71. package/sensor/dist/extraction/grammars.d.ts +38 -0
  72. package/sensor/dist/extraction/grammars.d.ts.map +1 -1
  73. package/sensor/dist/extraction/grammars.js +115 -3
  74. package/sensor/dist/extraction/grammars.js.map +1 -1
  75. package/sensor/dist/extraction/index.d.ts +218 -14
  76. package/sensor/dist/extraction/index.d.ts.map +1 -1
  77. package/sensor/dist/extraction/index.js +887 -175
  78. package/sensor/dist/extraction/index.js.map +1 -1
  79. package/sensor/dist/extraction/languages/c-cpp.d.ts +58 -0
  80. package/sensor/dist/extraction/languages/c-cpp.d.ts.map +1 -1
  81. package/sensor/dist/extraction/languages/c-cpp.js +310 -19
  82. package/sensor/dist/extraction/languages/c-cpp.js.map +1 -1
  83. package/sensor/dist/extraction/languages/dart.d.ts.map +1 -1
  84. package/sensor/dist/extraction/languages/dart.js +8 -3
  85. package/sensor/dist/extraction/languages/dart.js.map +1 -1
  86. package/sensor/dist/extraction/languages/erlang.d.ts.map +1 -1
  87. package/sensor/dist/extraction/languages/erlang.js +58 -22
  88. package/sensor/dist/extraction/languages/erlang.js.map +1 -1
  89. package/sensor/dist/extraction/languages/javascript.js +1 -1
  90. package/sensor/dist/extraction/languages/javascript.js.map +1 -1
  91. package/sensor/dist/extraction/languages/kotlin.d.ts.map +1 -1
  92. package/sensor/dist/extraction/languages/kotlin.js +169 -36
  93. package/sensor/dist/extraction/languages/kotlin.js.map +1 -1
  94. package/sensor/dist/extraction/languages/lua.d.ts.map +1 -1
  95. package/sensor/dist/extraction/languages/lua.js +4 -1
  96. package/sensor/dist/extraction/languages/lua.js.map +1 -1
  97. package/sensor/dist/extraction/languages/objc.d.ts.map +1 -1
  98. package/sensor/dist/extraction/languages/objc.js +4 -0
  99. package/sensor/dist/extraction/languages/objc.js.map +1 -1
  100. package/sensor/dist/extraction/languages/python.d.ts.map +1 -1
  101. package/sensor/dist/extraction/languages/python.js +71 -0
  102. package/sensor/dist/extraction/languages/python.js.map +1 -1
  103. package/sensor/dist/extraction/languages/rust.d.ts +20 -0
  104. package/sensor/dist/extraction/languages/rust.d.ts.map +1 -1
  105. package/sensor/dist/extraction/languages/rust.js +50 -20
  106. package/sensor/dist/extraction/languages/rust.js.map +1 -1
  107. package/sensor/dist/extraction/languages/scala.d.ts.map +1 -1
  108. package/sensor/dist/extraction/languages/scala.js +18 -3
  109. package/sensor/dist/extraction/languages/scala.js.map +1 -1
  110. package/sensor/dist/extraction/languages/swift.d.ts +15 -0
  111. package/sensor/dist/extraction/languages/swift.d.ts.map +1 -1
  112. package/sensor/dist/extraction/languages/swift.js +50 -0
  113. package/sensor/dist/extraction/languages/swift.js.map +1 -1
  114. package/sensor/dist/extraction/languages/typescript.d.ts.map +1 -1
  115. package/sensor/dist/extraction/languages/typescript.js +9 -2
  116. package/sensor/dist/extraction/languages/typescript.js.map +1 -1
  117. package/sensor/dist/extraction/liquid-extractor.d.ts +18 -0
  118. package/sensor/dist/extraction/liquid-extractor.d.ts.map +1 -1
  119. package/sensor/dist/extraction/liquid-extractor.js +98 -22
  120. package/sensor/dist/extraction/liquid-extractor.js.map +1 -1
  121. package/sensor/dist/extraction/mybatis-extractor.d.ts.map +1 -1
  122. package/sensor/dist/extraction/mybatis-extractor.js +6 -4
  123. package/sensor/dist/extraction/mybatis-extractor.js.map +1 -1
  124. package/sensor/dist/extraction/parse-pool.d.ts +7 -0
  125. package/sensor/dist/extraction/parse-pool.d.ts.map +1 -1
  126. package/sensor/dist/extraction/parse-pool.js +20 -1
  127. package/sensor/dist/extraction/parse-pool.js.map +1 -1
  128. package/sensor/dist/extraction/syntax-tokens.d.ts +103 -0
  129. package/sensor/dist/extraction/syntax-tokens.d.ts.map +1 -0
  130. package/sensor/dist/extraction/syntax-tokens.js +381 -0
  131. package/sensor/dist/extraction/syntax-tokens.js.map +1 -0
  132. package/sensor/dist/extraction/tree-sitter-helpers.d.ts +5 -0
  133. package/sensor/dist/extraction/tree-sitter-helpers.d.ts.map +1 -1
  134. package/sensor/dist/extraction/tree-sitter-helpers.js +17 -0
  135. package/sensor/dist/extraction/tree-sitter-helpers.js.map +1 -1
  136. package/sensor/dist/extraction/tree-sitter-types.d.ts +31 -3
  137. package/sensor/dist/extraction/tree-sitter-types.d.ts.map +1 -1
  138. package/sensor/dist/extraction/tree-sitter.d.ts +101 -18
  139. package/sensor/dist/extraction/tree-sitter.d.ts.map +1 -1
  140. package/sensor/dist/extraction/tree-sitter.js +928 -134
  141. package/sensor/dist/extraction/tree-sitter.js.map +1 -1
  142. package/sensor/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
  143. package/sensor/dist/file-limits.d.ts +40 -0
  144. package/sensor/dist/file-limits.d.ts.map +1 -0
  145. package/sensor/dist/file-limits.js +147 -0
  146. package/sensor/dist/file-limits.js.map +1 -0
  147. package/sensor/dist/graph/branch-guards.d.ts +265 -0
  148. package/sensor/dist/graph/branch-guards.d.ts.map +1 -0
  149. package/sensor/dist/graph/branch-guards.js +2325 -0
  150. package/sensor/dist/graph/branch-guards.js.map +1 -0
  151. package/sensor/dist/graph/dead-code.d.ts +251 -0
  152. package/sensor/dist/graph/dead-code.d.ts.map +1 -0
  153. package/sensor/dist/graph/dead-code.js +776 -0
  154. package/sensor/dist/graph/dead-code.js.map +1 -0
  155. package/sensor/dist/graph/dynamic-boundary-report.d.ts +121 -0
  156. package/sensor/dist/graph/dynamic-boundary-report.d.ts.map +1 -0
  157. package/sensor/dist/graph/dynamic-boundary-report.js +283 -0
  158. package/sensor/dist/graph/dynamic-boundary-report.js.map +1 -0
  159. package/sensor/dist/graph/index.d.ts +4 -0
  160. package/sensor/dist/graph/index.d.ts.map +1 -1
  161. package/sensor/dist/graph/index.js +16 -1
  162. package/sensor/dist/graph/index.js.map +1 -1
  163. package/sensor/dist/graph/named-symbol-flow.d.ts +125 -0
  164. package/sensor/dist/graph/named-symbol-flow.d.ts.map +1 -0
  165. package/sensor/dist/graph/named-symbol-flow.js +552 -0
  166. package/sensor/dist/graph/named-symbol-flow.js.map +1 -0
  167. package/sensor/dist/graph/queries.d.ts.map +1 -1
  168. package/sensor/dist/graph/queries.js +2 -0
  169. package/sensor/dist/graph/queries.js.map +1 -1
  170. package/sensor/dist/graph/symbol-lookup.d.ts +83 -0
  171. package/sensor/dist/graph/symbol-lookup.d.ts.map +1 -0
  172. package/sensor/dist/graph/symbol-lookup.js +181 -0
  173. package/sensor/dist/graph/symbol-lookup.js.map +1 -0
  174. package/sensor/dist/graph/traversal.d.ts +9 -0
  175. package/sensor/dist/graph/traversal.d.ts.map +1 -1
  176. package/sensor/dist/graph/traversal.js +62 -36
  177. package/sensor/dist/graph/traversal.js.map +1 -1
  178. package/sensor/dist/graph/type-hierarchy.d.ts +155 -0
  179. package/sensor/dist/graph/type-hierarchy.d.ts.map +1 -0
  180. package/sensor/dist/graph/type-hierarchy.js +382 -0
  181. package/sensor/dist/graph/type-hierarchy.js.map +1 -0
  182. package/sensor/dist/index.d.ts +272 -10
  183. package/sensor/dist/index.d.ts.map +1 -1
  184. package/sensor/dist/index.js +510 -34
  185. package/sensor/dist/index.js.map +1 -1
  186. package/sensor/dist/mcp/answer-freshness.d.ts +10 -0
  187. package/sensor/dist/mcp/answer-freshness.d.ts.map +1 -0
  188. package/sensor/dist/mcp/answer-freshness.js +68 -0
  189. package/sensor/dist/mcp/answer-freshness.js.map +1 -0
  190. package/sensor/dist/mcp/daemon-manager.d.ts +1 -1
  191. package/sensor/dist/mcp/daemon-manager.d.ts.map +1 -1
  192. package/sensor/dist/mcp/daemon-manager.js +17 -1
  193. package/sensor/dist/mcp/daemon-manager.js.map +1 -1
  194. package/sensor/dist/mcp/daemon-paths.d.ts +8 -0
  195. package/sensor/dist/mcp/daemon-paths.d.ts.map +1 -1
  196. package/sensor/dist/mcp/daemon-paths.js +65 -2
  197. package/sensor/dist/mcp/daemon-paths.js.map +1 -1
  198. package/sensor/dist/mcp/daemon-registry.d.ts +17 -4
  199. package/sensor/dist/mcp/daemon-registry.d.ts.map +1 -1
  200. package/sensor/dist/mcp/daemon-registry.js +133 -23
  201. package/sensor/dist/mcp/daemon-registry.js.map +1 -1
  202. package/sensor/dist/mcp/daemon.d.ts +26 -13
  203. package/sensor/dist/mcp/daemon.d.ts.map +1 -1
  204. package/sensor/dist/mcp/daemon.js +126 -32
  205. package/sensor/dist/mcp/daemon.js.map +1 -1
  206. package/sensor/dist/mcp/dynamic-boundaries.d.ts +1 -10
  207. package/sensor/dist/mcp/dynamic-boundaries.d.ts.map +1 -1
  208. package/sensor/dist/mcp/dynamic-boundaries.js +46 -42
  209. package/sensor/dist/mcp/dynamic-boundaries.js.map +1 -1
  210. package/sensor/dist/mcp/engine.d.ts +31 -9
  211. package/sensor/dist/mcp/engine.d.ts.map +1 -1
  212. package/sensor/dist/mcp/engine.js +190 -79
  213. package/sensor/dist/mcp/engine.js.map +1 -1
  214. package/sensor/dist/mcp/explore-dedup.d.ts +140 -0
  215. package/sensor/dist/mcp/explore-dedup.d.ts.map +1 -0
  216. package/sensor/dist/mcp/explore-dedup.js +239 -0
  217. package/sensor/dist/mcp/explore-dedup.js.map +1 -0
  218. package/sensor/dist/mcp/explore-diagnostics.d.ts +289 -0
  219. package/sensor/dist/mcp/explore-diagnostics.d.ts.map +1 -0
  220. package/sensor/dist/mcp/explore-diagnostics.js +558 -0
  221. package/sensor/dist/mcp/explore-diagnostics.js.map +1 -0
  222. package/sensor/dist/mcp/explore-session-state.d.ts +217 -0
  223. package/sensor/dist/mcp/explore-session-state.d.ts.map +1 -0
  224. package/sensor/dist/mcp/explore-session-state.js +322 -0
  225. package/sensor/dist/mcp/explore-session-state.js.map +1 -0
  226. package/sensor/dist/mcp/index-freshness-worker.d.ts +2 -0
  227. package/sensor/dist/mcp/index-freshness-worker.d.ts.map +1 -0
  228. package/sensor/dist/mcp/index-freshness-worker.js +30 -0
  229. package/sensor/dist/mcp/index-freshness-worker.js.map +1 -0
  230. package/sensor/dist/mcp/index-freshness.d.ts +7 -0
  231. package/sensor/dist/mcp/index-freshness.d.ts.map +1 -0
  232. package/sensor/dist/mcp/index-freshness.js +94 -0
  233. package/sensor/dist/mcp/index-freshness.js.map +1 -0
  234. package/sensor/dist/mcp/index.d.ts +3 -1
  235. package/sensor/dist/mcp/index.d.ts.map +1 -1
  236. package/sensor/dist/mcp/index.js +109 -12
  237. package/sensor/dist/mcp/index.js.map +1 -1
  238. package/sensor/dist/mcp/liveness-watchdog.d.ts +4 -1
  239. package/sensor/dist/mcp/liveness-watchdog.d.ts.map +1 -1
  240. package/sensor/dist/mcp/liveness-watchdog.js +11 -2
  241. package/sensor/dist/mcp/liveness-watchdog.js.map +1 -1
  242. package/sensor/dist/mcp/project-lifecycle.d.ts +9 -0
  243. package/sensor/dist/mcp/project-lifecycle.d.ts.map +1 -0
  244. package/sensor/dist/mcp/project-lifecycle.js +182 -0
  245. package/sensor/dist/mcp/project-lifecycle.js.map +1 -0
  246. package/sensor/dist/mcp/proxy.d.ts.map +1 -1
  247. package/sensor/dist/mcp/proxy.js +15 -1
  248. package/sensor/dist/mcp/proxy.js.map +1 -1
  249. package/sensor/dist/mcp/query-pool.d.ts +2 -2
  250. package/sensor/dist/mcp/query-pool.d.ts.map +1 -1
  251. package/sensor/dist/mcp/query-pool.js +9 -7
  252. package/sensor/dist/mcp/query-pool.js.map +1 -1
  253. package/sensor/dist/mcp/query-worker.js +2 -2
  254. package/sensor/dist/mcp/query-worker.js.map +1 -1
  255. package/sensor/dist/mcp/server-instructions.d.ts +2 -2
  256. package/sensor/dist/mcp/server-instructions.d.ts.map +1 -1
  257. package/sensor/dist/mcp/server-instructions.js +13 -6
  258. package/sensor/dist/mcp/server-instructions.js.map +1 -1
  259. package/sensor/dist/mcp/session.d.ts +16 -0
  260. package/sensor/dist/mcp/session.d.ts.map +1 -1
  261. package/sensor/dist/mcp/session.js +36 -13
  262. package/sensor/dist/mcp/session.js.map +1 -1
  263. package/sensor/dist/mcp/startup-handshake.d.ts +3 -1
  264. package/sensor/dist/mcp/startup-handshake.d.ts.map +1 -1
  265. package/sensor/dist/mcp/startup-handshake.js +10 -2
  266. package/sensor/dist/mcp/startup-handshake.js.map +1 -1
  267. package/sensor/dist/mcp/tools.d.ts +496 -38
  268. package/sensor/dist/mcp/tools.d.ts.map +1 -1
  269. package/sensor/dist/mcp/tools.js +4021 -772
  270. package/sensor/dist/mcp/tools.js.map +1 -1
  271. package/sensor/dist/mcp/writer-lock.d.ts +61 -0
  272. package/sensor/dist/mcp/writer-lock.d.ts.map +1 -0
  273. package/sensor/dist/mcp/writer-lock.js +260 -0
  274. package/sensor/dist/mcp/writer-lock.js.map +1 -0
  275. package/sensor/dist/project-config.d.ts +23 -0
  276. package/sensor/dist/project-config.d.ts.map +1 -1
  277. package/sensor/dist/project-config.js +39 -2
  278. package/sensor/dist/project-config.js.map +1 -1
  279. package/sensor/dist/resolution/alias-binding.d.ts +56 -0
  280. package/sensor/dist/resolution/alias-binding.d.ts.map +1 -0
  281. package/sensor/dist/resolution/alias-binding.js +127 -0
  282. package/sensor/dist/resolution/alias-binding.js.map +1 -0
  283. package/sensor/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -1
  284. package/sensor/dist/resolution/c-fnptr-synthesizer.js +21 -19
  285. package/sensor/dist/resolution/c-fnptr-synthesizer.js.map +1 -1
  286. package/sensor/dist/resolution/callback-synthesizer.d.ts +23 -1
  287. package/sensor/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  288. package/sensor/dist/resolution/callback-synthesizer.js +356 -155
  289. package/sensor/dist/resolution/callback-synthesizer.js.map +1 -1
  290. package/sensor/dist/resolution/cpp-constructor.d.ts +4 -0
  291. package/sensor/dist/resolution/cpp-constructor.d.ts.map +1 -0
  292. package/sensor/dist/resolution/cpp-constructor.js +123 -0
  293. package/sensor/dist/resolution/cpp-constructor.js.map +1 -0
  294. package/sensor/dist/resolution/cpp-macro-visibility.d.ts +12 -0
  295. package/sensor/dist/resolution/cpp-macro-visibility.d.ts.map +1 -0
  296. package/sensor/dist/resolution/cpp-macro-visibility.js +342 -0
  297. package/sensor/dist/resolution/cpp-macro-visibility.js.map +1 -0
  298. package/sensor/dist/resolution/expo-router-synthesizer.d.ts +32 -0
  299. package/sensor/dist/resolution/expo-router-synthesizer.d.ts.map +1 -0
  300. package/sensor/dist/resolution/expo-router-synthesizer.js +189 -0
  301. package/sensor/dist/resolution/expo-router-synthesizer.js.map +1 -0
  302. package/sensor/dist/resolution/frameworks/csharp.d.ts.map +1 -1
  303. package/sensor/dist/resolution/frameworks/csharp.js +96 -2
  304. package/sensor/dist/resolution/frameworks/csharp.js.map +1 -1
  305. package/sensor/dist/resolution/frameworks/expo-router.d.ts +189 -0
  306. package/sensor/dist/resolution/frameworks/expo-router.d.ts.map +1 -0
  307. package/sensor/dist/resolution/frameworks/expo-router.js +785 -0
  308. package/sensor/dist/resolution/frameworks/expo-router.js.map +1 -0
  309. package/sensor/dist/resolution/frameworks/express.d.ts.map +1 -1
  310. package/sensor/dist/resolution/frameworks/express.js +251 -16
  311. package/sensor/dist/resolution/frameworks/express.js.map +1 -1
  312. package/sensor/dist/resolution/frameworks/index.d.ts +5 -0
  313. package/sensor/dist/resolution/frameworks/index.d.ts.map +1 -1
  314. package/sensor/dist/resolution/frameworks/index.js +29 -1
  315. package/sensor/dist/resolution/frameworks/index.js.map +1 -1
  316. package/sensor/dist/resolution/frameworks/java.d.ts.map +1 -1
  317. package/sensor/dist/resolution/frameworks/java.js +79 -47
  318. package/sensor/dist/resolution/frameworks/java.js.map +1 -1
  319. package/sensor/dist/resolution/frameworks/laravel.d.ts.map +1 -1
  320. package/sensor/dist/resolution/frameworks/laravel.js +294 -4
  321. package/sensor/dist/resolution/frameworks/laravel.js.map +1 -1
  322. package/sensor/dist/resolution/frameworks/nestjs.d.ts.map +1 -1
  323. package/sensor/dist/resolution/frameworks/nestjs.js +6 -14
  324. package/sensor/dist/resolution/frameworks/nestjs.js.map +1 -1
  325. package/sensor/dist/resolution/frameworks/nextjs.d.ts +76 -0
  326. package/sensor/dist/resolution/frameworks/nextjs.d.ts.map +1 -0
  327. package/sensor/dist/resolution/frameworks/nextjs.js +328 -0
  328. package/sensor/dist/resolution/frameworks/nextjs.js.map +1 -0
  329. package/sensor/dist/resolution/frameworks/object-literal.d.ts +32 -0
  330. package/sensor/dist/resolution/frameworks/object-literal.d.ts.map +1 -0
  331. package/sensor/dist/resolution/frameworks/object-literal.js +139 -0
  332. package/sensor/dist/resolution/frameworks/object-literal.js.map +1 -0
  333. package/sensor/dist/resolution/frameworks/package-deps.d.ts +14 -0
  334. package/sensor/dist/resolution/frameworks/package-deps.d.ts.map +1 -0
  335. package/sensor/dist/resolution/frameworks/package-deps.js +78 -0
  336. package/sensor/dist/resolution/frameworks/package-deps.js.map +1 -0
  337. package/sensor/dist/resolution/frameworks/python.d.ts.map +1 -1
  338. package/sensor/dist/resolution/frameworks/python.js +166 -0
  339. package/sensor/dist/resolution/frameworks/python.js.map +1 -1
  340. package/sensor/dist/resolution/frameworks/react-native.d.ts +26 -0
  341. package/sensor/dist/resolution/frameworks/react-native.d.ts.map +1 -1
  342. package/sensor/dist/resolution/frameworks/react-native.js +153 -14
  343. package/sensor/dist/resolution/frameworks/react-native.js.map +1 -1
  344. package/sensor/dist/resolution/frameworks/react-router.d.ts +46 -0
  345. package/sensor/dist/resolution/frameworks/react-router.d.ts.map +1 -0
  346. package/sensor/dist/resolution/frameworks/react-router.js +187 -0
  347. package/sensor/dist/resolution/frameworks/react-router.js.map +1 -0
  348. package/sensor/dist/resolution/frameworks/react.d.ts +2 -1
  349. package/sensor/dist/resolution/frameworks/react.d.ts.map +1 -1
  350. package/sensor/dist/resolution/frameworks/react.js +188 -139
  351. package/sensor/dist/resolution/frameworks/react.js.map +1 -1
  352. package/sensor/dist/resolution/frameworks/svelte.d.ts.map +1 -1
  353. package/sensor/dist/resolution/frameworks/svelte.js +6 -1
  354. package/sensor/dist/resolution/frameworks/svelte.js.map +1 -1
  355. package/sensor/dist/resolution/frameworks/sveltekit-router.d.ts +36 -0
  356. package/sensor/dist/resolution/frameworks/sveltekit-router.d.ts.map +1 -0
  357. package/sensor/dist/resolution/frameworks/sveltekit-router.js +156 -0
  358. package/sensor/dist/resolution/frameworks/sveltekit-router.js.map +1 -0
  359. package/sensor/dist/resolution/frameworks/swift.d.ts.map +1 -1
  360. package/sensor/dist/resolution/frameworks/swift.js +140 -99
  361. package/sensor/dist/resolution/frameworks/swift.js.map +1 -1
  362. package/sensor/dist/resolution/frameworks/tanstack-router.d.ts +84 -0
  363. package/sensor/dist/resolution/frameworks/tanstack-router.d.ts.map +1 -0
  364. package/sensor/dist/resolution/frameworks/tanstack-router.js +437 -0
  365. package/sensor/dist/resolution/frameworks/tanstack-router.js.map +1 -0
  366. package/sensor/dist/resolution/frameworks/vue-router.d.ts +82 -0
  367. package/sensor/dist/resolution/frameworks/vue-router.d.ts.map +1 -0
  368. package/sensor/dist/resolution/frameworks/vue-router.js +377 -0
  369. package/sensor/dist/resolution/frameworks/vue-router.js.map +1 -0
  370. package/sensor/dist/resolution/import-resolver.d.ts +38 -0
  371. package/sensor/dist/resolution/import-resolver.d.ts.map +1 -1
  372. package/sensor/dist/resolution/import-resolver.js +394 -28
  373. package/sensor/dist/resolution/import-resolver.js.map +1 -1
  374. package/sensor/dist/resolution/index.d.ts +89 -25
  375. package/sensor/dist/resolution/index.d.ts.map +1 -1
  376. package/sensor/dist/resolution/index.js +573 -148
  377. package/sensor/dist/resolution/index.js.map +1 -1
  378. package/sensor/dist/resolution/js-builtins.d.ts +13 -0
  379. package/sensor/dist/resolution/js-builtins.d.ts.map +1 -0
  380. package/sensor/dist/resolution/js-builtins.js +46 -0
  381. package/sensor/dist/resolution/js-builtins.js.map +1 -0
  382. package/sensor/dist/resolution/name-matcher.d.ts +99 -22
  383. package/sensor/dist/resolution/name-matcher.d.ts.map +1 -1
  384. package/sensor/dist/resolution/name-matcher.js +2220 -86
  385. package/sensor/dist/resolution/name-matcher.js.map +1 -1
  386. package/sensor/dist/resolution/next-router-synthesizer.d.ts +27 -0
  387. package/sensor/dist/resolution/next-router-synthesizer.d.ts.map +1 -0
  388. package/sensor/dist/resolution/next-router-synthesizer.js +113 -0
  389. package/sensor/dist/resolution/next-router-synthesizer.js.map +1 -0
  390. package/sensor/dist/resolution/path-aliases.d.ts +6 -4
  391. package/sensor/dist/resolution/path-aliases.d.ts.map +1 -1
  392. package/sensor/dist/resolution/path-aliases.js +125 -17
  393. package/sensor/dist/resolution/path-aliases.js.map +1 -1
  394. package/sensor/dist/resolution/react-router-synthesizer.d.ts +33 -0
  395. package/sensor/dist/resolution/react-router-synthesizer.d.ts.map +1 -0
  396. package/sensor/dist/resolution/react-router-synthesizer.js +126 -0
  397. package/sensor/dist/resolution/react-router-synthesizer.js.map +1 -0
  398. package/sensor/dist/resolution/resolver-pool.d.ts +2 -0
  399. package/sensor/dist/resolution/resolver-pool.d.ts.map +1 -1
  400. package/sensor/dist/resolution/resolver-pool.js +4 -0
  401. package/sensor/dist/resolution/resolver-pool.js.map +1 -1
  402. package/sensor/dist/resolution/strip-comments.d.ts +6 -0
  403. package/sensor/dist/resolution/strip-comments.d.ts.map +1 -1
  404. package/sensor/dist/resolution/strip-comments.js +58 -0
  405. package/sensor/dist/resolution/strip-comments.js.map +1 -1
  406. package/sensor/dist/resolution/sveltekit-synthesizer.d.ts +58 -0
  407. package/sensor/dist/resolution/sveltekit-synthesizer.d.ts.map +1 -0
  408. package/sensor/dist/resolution/sveltekit-synthesizer.js +173 -0
  409. package/sensor/dist/resolution/sveltekit-synthesizer.js.map +1 -0
  410. package/sensor/dist/resolution/swift-type-visibility.d.ts +52 -0
  411. package/sensor/dist/resolution/swift-type-visibility.d.ts.map +1 -0
  412. package/sensor/dist/resolution/swift-type-visibility.js +328 -0
  413. package/sensor/dist/resolution/swift-type-visibility.js.map +1 -0
  414. package/sensor/dist/resolution/synth-utils.d.ts +26 -0
  415. package/sensor/dist/resolution/synth-utils.d.ts.map +1 -0
  416. package/sensor/dist/resolution/synth-utils.js +75 -0
  417. package/sensor/dist/resolution/synth-utils.js.map +1 -0
  418. package/sensor/dist/resolution/tanstack-router-synthesizer.d.ts +31 -0
  419. package/sensor/dist/resolution/tanstack-router-synthesizer.d.ts.map +1 -0
  420. package/sensor/dist/resolution/tanstack-router-synthesizer.js +114 -0
  421. package/sensor/dist/resolution/tanstack-router-synthesizer.js.map +1 -0
  422. package/sensor/dist/resolution/tier-synthesizer.d.ts +54 -0
  423. package/sensor/dist/resolution/tier-synthesizer.d.ts.map +1 -0
  424. package/sensor/dist/resolution/tier-synthesizer.js +1294 -0
  425. package/sensor/dist/resolution/tier-synthesizer.js.map +1 -0
  426. package/sensor/dist/resolution/types.d.ts +78 -2
  427. package/sensor/dist/resolution/types.d.ts.map +1 -1
  428. package/sensor/dist/resolution/types.js +64 -0
  429. package/sensor/dist/resolution/types.js.map +1 -1
  430. package/sensor/dist/resolution/vue-router-synthesizer.d.ts +30 -0
  431. package/sensor/dist/resolution/vue-router-synthesizer.d.ts.map +1 -0
  432. package/sensor/dist/resolution/vue-router-synthesizer.js +115 -0
  433. package/sensor/dist/resolution/vue-router-synthesizer.js.map +1 -0
  434. package/sensor/dist/search/identifier-segments.d.ts +10 -0
  435. package/sensor/dist/search/identifier-segments.d.ts.map +1 -1
  436. package/sensor/dist/search/identifier-segments.js +29 -0
  437. package/sensor/dist/search/identifier-segments.js.map +1 -1
  438. package/sensor/dist/search/query-paths.d.ts +106 -0
  439. package/sensor/dist/search/query-paths.d.ts.map +1 -0
  440. package/sensor/dist/search/query-paths.js +513 -0
  441. package/sensor/dist/search/query-paths.js.map +1 -0
  442. package/sensor/dist/search/query-utils.d.ts +22 -2
  443. package/sensor/dist/search/query-utils.d.ts.map +1 -1
  444. package/sensor/dist/search/query-utils.js +56 -16
  445. package/sensor/dist/search/query-utils.js.map +1 -1
  446. package/sensor/dist/sync/watch-policy.d.ts +14 -0
  447. package/sensor/dist/sync/watch-policy.d.ts.map +1 -1
  448. package/sensor/dist/sync/watch-policy.js +19 -0
  449. package/sensor/dist/sync/watch-policy.js.map +1 -1
  450. package/sensor/dist/sync/watcher.d.ts +74 -35
  451. package/sensor/dist/sync/watcher.d.ts.map +1 -1
  452. package/sensor/dist/sync/watcher.js +281 -99
  453. package/sensor/dist/sync/watcher.js.map +1 -1
  454. package/sensor/dist/telemetry/index.d.ts +4 -2
  455. package/sensor/dist/telemetry/index.d.ts.map +1 -1
  456. package/sensor/dist/telemetry/index.js +92 -41
  457. package/sensor/dist/telemetry/index.js.map +1 -1
  458. package/sensor/dist/types.d.ts +18 -2
  459. package/sensor/dist/types.d.ts.map +1 -1
  460. package/sensor/dist/types.js +2 -0
  461. package/sensor/dist/types.js.map +1 -1
  462. package/sensor/dist/ui-server/api/deadcode.d.ts +84 -0
  463. package/sensor/dist/ui-server/api/deadcode.d.ts.map +1 -0
  464. package/sensor/dist/ui-server/api/deadcode.js +136 -0
  465. package/sensor/dist/ui-server/api/deadcode.js.map +1 -0
  466. package/sensor/dist/ui-server/api/effects.d.ts +94 -0
  467. package/sensor/dist/ui-server/api/effects.d.ts.map +1 -0
  468. package/sensor/dist/ui-server/api/effects.js +527 -0
  469. package/sensor/dist/ui-server/api/effects.js.map +1 -0
  470. package/sensor/dist/ui-server/api/entrypoints.d.ts +92 -0
  471. package/sensor/dist/ui-server/api/entrypoints.d.ts.map +1 -0
  472. package/sensor/dist/ui-server/api/entrypoints.js +361 -0
  473. package/sensor/dist/ui-server/api/entrypoints.js.map +1 -0
  474. package/sensor/dist/ui-server/api/events.d.ts +183 -0
  475. package/sensor/dist/ui-server/api/events.d.ts.map +1 -0
  476. package/sensor/dist/ui-server/api/events.js +430 -0
  477. package/sensor/dist/ui-server/api/events.js.map +1 -0
  478. package/sensor/dist/ui-server/api/file.d.ts +66 -0
  479. package/sensor/dist/ui-server/api/file.d.ts.map +1 -0
  480. package/sensor/dist/ui-server/api/file.js +235 -0
  481. package/sensor/dist/ui-server/api/file.js.map +1 -0
  482. package/sensor/dist/ui-server/api/filecode.d.ts +126 -0
  483. package/sensor/dist/ui-server/api/filecode.d.ts.map +1 -0
  484. package/sensor/dist/ui-server/api/filecode.js +205 -0
  485. package/sensor/dist/ui-server/api/filecode.js.map +1 -0
  486. package/sensor/dist/ui-server/api/flow.d.ts +226 -0
  487. package/sensor/dist/ui-server/api/flow.d.ts.map +1 -0
  488. package/sensor/dist/ui-server/api/flow.js +612 -0
  489. package/sensor/dist/ui-server/api/flow.js.map +1 -0
  490. package/sensor/dist/ui-server/api/hierarchy.d.ts +81 -0
  491. package/sensor/dist/ui-server/api/hierarchy.d.ts.map +1 -0
  492. package/sensor/dist/ui-server/api/hierarchy.js +92 -0
  493. package/sensor/dist/ui-server/api/hierarchy.js.map +1 -0
  494. package/sensor/dist/ui-server/api/index.d.ts +90 -0
  495. package/sensor/dist/ui-server/api/index.d.ts.map +1 -0
  496. package/sensor/dist/ui-server/api/index.js +332 -0
  497. package/sensor/dist/ui-server/api/index.js.map +1 -0
  498. package/sensor/dist/ui-server/api/map.d.ts +256 -0
  499. package/sensor/dist/ui-server/api/map.d.ts.map +1 -0
  500. package/sensor/dist/ui-server/api/map.js +760 -0
  501. package/sensor/dist/ui-server/api/map.js.map +1 -0
  502. package/sensor/dist/ui-server/api/node.d.ts +81 -0
  503. package/sensor/dist/ui-server/api/node.d.ts.map +1 -0
  504. package/sensor/dist/ui-server/api/node.js +388 -0
  505. package/sensor/dist/ui-server/api/node.js.map +1 -0
  506. package/sensor/dist/ui-server/api/nodes.d.ts +25 -0
  507. package/sensor/dist/ui-server/api/nodes.d.ts.map +1 -0
  508. package/sensor/dist/ui-server/api/nodes.js +45 -0
  509. package/sensor/dist/ui-server/api/nodes.js.map +1 -0
  510. package/sensor/dist/ui-server/api/program.d.ts +139 -0
  511. package/sensor/dist/ui-server/api/program.d.ts.map +1 -0
  512. package/sensor/dist/ui-server/api/program.js +315 -0
  513. package/sensor/dist/ui-server/api/program.js.map +1 -0
  514. package/sensor/dist/ui-server/api/respond.d.ts +76 -0
  515. package/sensor/dist/ui-server/api/respond.d.ts.map +1 -0
  516. package/sensor/dist/ui-server/api/respond.js +186 -0
  517. package/sensor/dist/ui-server/api/respond.js.map +1 -0
  518. package/sensor/dist/ui-server/api/route-roots.d.ts +39 -0
  519. package/sensor/dist/ui-server/api/route-roots.d.ts.map +1 -0
  520. package/sensor/dist/ui-server/api/route-roots.js +96 -0
  521. package/sensor/dist/ui-server/api/route-roots.js.map +1 -0
  522. package/sensor/dist/ui-server/api/routes.d.ts +59 -0
  523. package/sensor/dist/ui-server/api/routes.d.ts.map +1 -0
  524. package/sensor/dist/ui-server/api/routes.js +108 -0
  525. package/sensor/dist/ui-server/api/routes.js.map +1 -0
  526. package/sensor/dist/ui-server/api/screens.d.ts +117 -0
  527. package/sensor/dist/ui-server/api/screens.d.ts.map +1 -0
  528. package/sensor/dist/ui-server/api/screens.js +611 -0
  529. package/sensor/dist/ui-server/api/screens.js.map +1 -0
  530. package/sensor/dist/ui-server/api/search.d.ts +29 -0
  531. package/sensor/dist/ui-server/api/search.d.ts.map +1 -0
  532. package/sensor/dist/ui-server/api/search.js +232 -0
  533. package/sensor/dist/ui-server/api/search.js.map +1 -0
  534. package/sensor/dist/ui-server/api/session.d.ts +52 -0
  535. package/sensor/dist/ui-server/api/session.d.ts.map +1 -0
  536. package/sensor/dist/ui-server/api/session.js +196 -0
  537. package/sensor/dist/ui-server/api/session.js.map +1 -0
  538. package/sensor/dist/ui-server/api/source.d.ts +200 -0
  539. package/sensor/dist/ui-server/api/source.d.ts.map +1 -0
  540. package/sensor/dist/ui-server/api/source.js +422 -0
  541. package/sensor/dist/ui-server/api/source.js.map +1 -0
  542. package/sensor/dist/ui-server/api/stats.d.ts +27 -0
  543. package/sensor/dist/ui-server/api/stats.d.ts.map +1 -0
  544. package/sensor/dist/ui-server/api/stats.js +164 -0
  545. package/sensor/dist/ui-server/api/stats.js.map +1 -0
  546. package/sensor/dist/ui-server/api/steps.d.ts +274 -0
  547. package/sensor/dist/ui-server/api/steps.d.ts.map +1 -0
  548. package/sensor/dist/ui-server/api/steps.js +1544 -0
  549. package/sensor/dist/ui-server/api/steps.js.map +1 -0
  550. package/sensor/dist/ui-server/api/trail-store.d.ts +142 -0
  551. package/sensor/dist/ui-server/api/trail-store.d.ts.map +1 -0
  552. package/sensor/dist/ui-server/api/trail-store.js +325 -0
  553. package/sensor/dist/ui-server/api/trail-store.js.map +1 -0
  554. package/sensor/dist/ui-server/api/trails.d.ts +163 -0
  555. package/sensor/dist/ui-server/api/trails.d.ts.map +1 -0
  556. package/sensor/dist/ui-server/api/trails.js +369 -0
  557. package/sensor/dist/ui-server/api/trails.js.map +1 -0
  558. package/sensor/dist/ui-server/api/when.d.ts +133 -0
  559. package/sensor/dist/ui-server/api/when.d.ts.map +1 -0
  560. package/sensor/dist/ui-server/api/when.js +275 -0
  561. package/sensor/dist/ui-server/api/when.js.map +1 -0
  562. package/sensor/dist/ui-server/api/wire.d.ts +179 -0
  563. package/sensor/dist/ui-server/api/wire.d.ts.map +1 -0
  564. package/sensor/dist/ui-server/api/wire.js +231 -0
  565. package/sensor/dist/ui-server/api/wire.js.map +1 -0
  566. package/sensor/dist/ui-server/assets.d.ts +40 -0
  567. package/sensor/dist/ui-server/assets.d.ts.map +1 -0
  568. package/sensor/dist/ui-server/assets.js +110 -0
  569. package/sensor/dist/ui-server/assets.js.map +1 -0
  570. package/sensor/dist/ui-server/constants.d.ts +32 -0
  571. package/sensor/dist/ui-server/constants.d.ts.map +1 -0
  572. package/sensor/dist/ui-server/constants.js +35 -0
  573. package/sensor/dist/ui-server/constants.js.map +1 -0
  574. package/sensor/dist/ui-server/highlight/index.d.ts +113 -0
  575. package/sensor/dist/ui-server/highlight/index.d.ts.map +1 -0
  576. package/sensor/dist/ui-server/highlight/index.js +307 -0
  577. package/sensor/dist/ui-server/highlight/index.js.map +1 -0
  578. package/sensor/dist/ui-server/highlight/languages.d.ts +36 -0
  579. package/sensor/dist/ui-server/highlight/languages.d.ts.map +1 -0
  580. package/sensor/dist/ui-server/highlight/languages.js +50 -0
  581. package/sensor/dist/ui-server/highlight/languages.js.map +1 -0
  582. package/sensor/dist/ui-server/index.d.ts +92 -0
  583. package/sensor/dist/ui-server/index.d.ts.map +1 -0
  584. package/sensor/dist/ui-server/index.js +399 -0
  585. package/sensor/dist/ui-server/index.js.map +1 -0
  586. package/sensor/dist/ui-server/open-browser.d.ts +31 -0
  587. package/sensor/dist/ui-server/open-browser.d.ts.map +1 -0
  588. package/sensor/dist/ui-server/open-browser.js +79 -0
  589. package/sensor/dist/ui-server/open-browser.js.map +1 -0
  590. package/sensor/dist/ui-server/security.d.ts +127 -0
  591. package/sensor/dist/ui-server/security.d.ts.map +1 -0
  592. package/sensor/dist/ui-server/security.js +332 -0
  593. package/sensor/dist/ui-server/security.js.map +1 -0
  594. package/sensor/dist/ui-server/static.d.ts +45 -0
  595. package/sensor/dist/ui-server/static.d.ts.map +1 -0
  596. package/sensor/dist/ui-server/static.js +165 -0
  597. package/sensor/dist/ui-server/static.js.map +1 -0
  598. package/sensor/dist/utils.d.ts +14 -2
  599. package/sensor/dist/utils.d.ts.map +1 -1
  600. package/sensor/dist/utils.js +25 -10
  601. package/sensor/dist/utils.js.map +1 -1
  602. package/src/sensor-adapter.mjs +5 -1
@@ -1,12 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SYNTH_PROGRESS_STEPS = exports.SYNTH_PASSES = void 0;
4
+ exports.hasSynthesisPattern = hasSynthesisPattern;
4
5
  exports.synthesizeCallbackEdges = synthesizeCallbackEdges;
5
6
  const generated_detection_1 = require("../extraction/generated-detection");
6
7
  const strip_comments_1 = require("./strip-comments");
7
8
  const c_fnptr_synthesizer_1 = require("./c-fnptr-synthesizer");
8
9
  const goframe_synthesizer_1 = require("./goframe-synthesizer");
10
+ const expo_router_synthesizer_1 = require("./expo-router-synthesizer");
11
+ const next_router_synthesizer_1 = require("./next-router-synthesizer");
12
+ const react_router_synthesizer_1 = require("./react-router-synthesizer");
13
+ const tanstack_router_synthesizer_1 = require("./tanstack-router-synthesizer");
14
+ const vue_router_synthesizer_1 = require("./vue-router-synthesizer");
15
+ const sveltekit_synthesizer_1 = require("./sveltekit-synthesizer");
9
16
  const cooperative_yield_1 = require("./cooperative-yield");
17
+ const tier_synthesizer_1 = require("./tier-synthesizer");
18
+ const synth_utils_1 = require("./synth-utils");
19
+ const import_resolver_1 = require("./import-resolver");
20
+ const name_matcher_1 = require("./name-matcher");
10
21
  const REGISTRAR_NAME = /^(on[A-Z]\w*|subscribe|addListener|addEventListener|register|watch|listen|addCallback)$/;
11
22
  const DISPATCHER_NAME = /(emit|trigger|notify|dispatch|fire|publish|flush)/i;
12
23
  const MAX_CALLBACKS_PER_CHANNEL = 40;
@@ -15,6 +26,7 @@ const ON_RE = /\.(?:on|once|addListener)\(\s*['"]([^'"]+)['"]\s*,\s*(?:function\
15
26
  const EMIT_RE = /\.(?:emit|fire|dispatchEvent)\(\s*['"]([^'"]+)['"]/g;
16
27
  const SETSTATE_RE = /this\.setState\s*\(/;
17
28
  const FLUTTER_SETSTATE_RE = /\bsetState\s*\(/; // Flutter: setState((){…}) / this.setState
29
+ const JS_FAMILY = ['typescript', 'javascript', 'tsx', 'jsx'];
18
30
  const JSX_TAG_RE = /<([A-Z][A-Za-z0-9_]*)[\s/>]/g;
19
31
  const MAX_JSX_CHILDREN = 30;
20
32
  // Vue SFC templates: kebab-case child components (<el-button> → ElButton) and
@@ -85,35 +97,6 @@ function sliceLines(content, startLine, endLine) {
85
97
  return null;
86
98
  return content.split('\n').slice(startLine - 1, endLine).join('\n');
87
99
  }
88
- /**
89
- * Per-match line resolver over `src`, 1-based at `baseLine`. The inline
90
- * `src.slice(0, idx).split('\n').length` idiom is O(source-length) PER MATCH,
91
- * which goes quadratic on a match-dense source (a generated function full of
92
- * `.push(` calls re-scanned tens of thousands of times was most of the #1235
93
- * indexing wedge). Builds the newline index once — lazily, since most sources
94
- * never produce a match — then answers each call with a binary search.
95
- */
96
- function makeLineAt(src, baseLine) {
97
- let nl = null;
98
- return (idx) => {
99
- if (!nl) {
100
- nl = [];
101
- for (let i = src.indexOf('\n'); i !== -1; i = src.indexOf('\n', i + 1))
102
- nl.push(i);
103
- }
104
- // Count newlines strictly before idx.
105
- let lo = 0;
106
- let hi = nl.length;
107
- while (lo < hi) {
108
- const mid = (lo + hi) >> 1;
109
- if (nl[mid] < idx)
110
- lo = mid + 1;
111
- else
112
- hi = mid;
113
- }
114
- return baseLine + lo;
115
- };
116
- }
117
100
  function registrarField(src) {
118
101
  const m = src.match(/this\.(\w+)\.(?:add|push|set)\(/);
119
102
  return m ? m[1] : null;
@@ -127,21 +110,6 @@ function dispatcherField(src) {
127
110
  return forEach[1];
128
111
  return null;
129
112
  }
130
- const FN_KINDS = new Set(['method', 'function', 'component']);
131
- /** Innermost function/method node whose line range contains `line`. */
132
- function enclosingFn(nodesInFile, line) {
133
- let best = null;
134
- for (const n of nodesInFile) {
135
- if (!FN_KINDS.has(n.kind))
136
- continue;
137
- const end = n.endLine ?? n.startLine;
138
- if (n.startLine <= line && end >= line) {
139
- if (!best || n.startLine >= best.startLine)
140
- best = n; // prefer the tightest (latest-starting) encloser
141
- }
142
- }
143
- return best;
144
- }
145
113
  /**
146
114
  * Stream method + function nodes lazily. The synthesizers only scan-and-filter
147
115
  * down to a tiny matched subset, so materializing every function/method (which
@@ -185,7 +153,7 @@ async function fieldChannelEdges(queries, ctx, onYield) {
185
153
  const chDispatchers = dispatchers.filter((d) => d.node.filePath === reg.node.filePath && d.field === reg.field);
186
154
  if (chDispatchers.length === 0)
187
155
  continue;
188
- const argRe = new RegExp(`${reg.node.name}\\s*\\(\\s*(?:this\\.)?(\\w+)`);
156
+ const argRe = new RegExp(`${reg.node.name}\\s*\\(\\s*(this\\.\\w+|\\w+)\\s*(?=[,)])`);
189
157
  let added = 0;
190
158
  for (const e of queries.getIncomingEdges(reg.node.id, ['calls'])) {
191
159
  if (added >= MAX_CALLBACKS_PER_CHANNEL)
@@ -199,8 +167,15 @@ async function fieldChannelEdges(queries, ctx, onYield) {
199
167
  const am = line?.match(argRe);
200
168
  if (!am)
201
169
  continue;
202
- const fn = ctx.getNodesByName(am[1]).find((n) => n.kind === 'method' || n.kind === 'function');
203
- if (!fn)
170
+ // Reuse the resolved value at this registration site: it retains the
171
+ // receiver's class/inheritance and import binding, unlike a name lookup.
172
+ const refs = queries.getOutgoingEdges(caller.id, ['references']).filter((r) => r.line === e.line && r.metadata?.fnRef === true && r.metadata.refName === am[1]);
173
+ if (refs.length !== 1)
174
+ continue;
175
+ const fn = queries.getNodeById(refs[0].target);
176
+ if (!fn || (fn.kind !== 'method' && fn.kind !== 'function'))
177
+ continue;
178
+ if (!am[1].startsWith('this.') && fn.filePath !== caller.filePath && refs[0].metadata?.resolvedBy !== 'import')
204
179
  continue;
205
180
  for (const disp of chDispatchers) {
206
181
  if (disp.node.id === fn.id)
@@ -271,7 +246,7 @@ async function closureCollectionEdges(queries, ctx, onYield) {
271
246
  const hasAppend = src.includes('.append(') || src.includes('.add(') || src.includes('.push(') || src.includes('.insert(');
272
247
  if (!hasForEach && !hasAppend)
273
248
  continue;
274
- const lineAt = makeLineAt(src, m.startLine ?? 1);
249
+ const lineAt = (0, synth_utils_1.makeLineAt)(src, m.startLine ?? 1);
275
250
  if (hasForEach) {
276
251
  CC_DISPATCH_RE.lastIndex = 0;
277
252
  let d;
@@ -345,12 +320,12 @@ async function eventEmitterEdges(ctx, onYield) {
345
320
  if (!hasEmit && !hasOn)
346
321
  continue;
347
322
  const nodesInFile = ctx.getNodesInFile(file);
348
- const lineOf = makeLineAt(content, 1);
323
+ const lineOf = (0, synth_utils_1.makeLineAt)(content, 1);
349
324
  if (hasEmit) {
350
325
  EMIT_RE.lastIndex = 0;
351
326
  let m;
352
327
  while ((m = EMIT_RE.exec(content))) {
353
- const disp = enclosingFn(nodesInFile, lineOf(m.index));
328
+ const disp = (0, synth_utils_1.enclosingFn)(nodesInFile, lineOf(m.index));
354
329
  if (!disp)
355
330
  continue;
356
331
  const set = emitsByEvent.get(m[1]) ?? new Set();
@@ -542,11 +517,9 @@ async function arkuiStateBuildEdges(queries, ctx, onYield) {
542
517
  let scanned255 = 0;
543
518
  const edges = [];
544
519
  const seen = new Set();
545
- for (const struct of queries.iterateNodesByKind('struct')) {
520
+ for (const struct of queries.iterateNodesByKindIn('struct', ['arkts'])) {
546
521
  if ((++scanned255 & 63) === 0)
547
522
  await onYield();
548
- if (struct.language !== 'arkts')
549
- continue;
550
523
  const children = queries.getOutgoingEdges(struct.id, ['contains'])
551
524
  .map((e) => queries.getNodeById(e.target))
552
525
  .filter((n) => !!n);
@@ -884,22 +857,17 @@ async function goImplementsEdges(queries, onYield) {
884
857
  // never the whole struct kind — that array is O(nodes) on struct-heavy
885
858
  // repos like the Linux kernel (#1212).
886
859
  const goStructs = [];
887
- for (const s of queries.iterateNodesByKind('struct')) {
860
+ for (const s of queries.iterateNodesByKindIn('struct', ['go'])) {
888
861
  if ((++scanned255 & 63) === 0)
889
862
  await onYield();
890
- if (s.language === 'go')
891
- goStructs.push(s);
863
+ goStructs.push(s);
892
864
  }
893
865
  const structMethods = new Map();
894
866
  for (const s of goStructs)
895
867
  structMethods.set(s.id, methodNameSet(s.id));
896
- for (const iface of queries.iterateNodesByKind('interface')) {
868
+ for (const iface of queries.iterateNodesByKindIn('interface', ['go'])) {
897
869
  if ((++scanned255 & 63) === 0)
898
870
  await onYield();
899
- if ((++scanned255 & 63) === 0)
900
- await onYield();
901
- if (iface.language !== 'go')
902
- continue;
903
871
  const want = methodNameSet(iface.id);
904
872
  if (want.size === 0)
905
873
  continue; // empty interface (`any`) — would match everything
@@ -952,9 +920,10 @@ async function goImplementsEdges(queries, onYield) {
952
920
  *
953
921
  * Go guarantees a method's receiver type is declared in the SAME PACKAGE as the
954
922
  * method, and a Go package is a single directory — so this is a deterministic
955
- * structural link, not a heuristic: find the same-named type in the method's own
956
- * directory and add the missing `contains` edge (no `provenance: 'heuristic'`,
957
- * matching the same-file edges extraction already emits). Skips methods that
923
+ * structural link, not a heuristic: find the same-named type in the method's
924
+ * own directory and add the missing `contains` edge with no provenance, matching
925
+ * same-file extraction. Tag synthesis ownership so an incremental
926
+ * refresh can replace it alongside implicit `implements`. Skips methods that
958
927
  * already have a type parent (the same-file case). (#583, cross-file half)
959
928
  */
960
929
  async function goCrossFileMethodContainsEdges(queries, onYield) {
@@ -966,13 +935,9 @@ async function goCrossFileMethodContainsEdges(queries, onYield) {
966
935
  const i = p.replace(/\\/g, '/').lastIndexOf('/');
967
936
  return i >= 0 ? p.slice(0, i) : '';
968
937
  };
969
- for (const method of queries.iterateNodesByKind('method')) {
970
- if ((++scanned255 & 63) === 0)
971
- await onYield();
938
+ for (const method of queries.iterateNodesByKindIn('method', ['go'])) {
972
939
  if ((++scanned255 & 63) === 0)
973
940
  await onYield();
974
- if (method.language !== 'go')
975
- continue;
976
941
  // The receiver type is encoded in the method's qualifiedName as `Recv::name`
977
942
  // (extraction sets `${receiverType}::${name}` for receiver methods).
978
943
  const qn = method.qualifiedName;
@@ -1007,7 +972,8 @@ async function goCrossFileMethodContainsEdges(queries, onYield) {
1007
972
  if (seen.has(key))
1008
973
  continue;
1009
974
  seen.add(key);
1010
- edges.push({ source: owner.id, target: method.id, kind: 'contains', line: method.startLine });
975
+ edges.push({ source: owner.id, target: method.id, kind: 'contains', line: method.startLine,
976
+ metadata: { synthesizedBy: 'go-method-contains' } });
1011
977
  }
1012
978
  return edges;
1013
979
  }
@@ -1106,10 +1072,31 @@ async function interfaceOverrideEdges(queries, onYield) {
1106
1072
  methodsMemo.set(classId, methods);
1107
1073
  return methods;
1108
1074
  };
1075
+ // A Swift protocol's methods live in its extensions: requirements are not
1076
+ // extracted as methods, and `extension EventMonitor { func request(…) }` is
1077
+ // where the default implementations a conformer overrides are. A class-kind
1078
+ // node sharing a protocol's name is one of its extensions.
1079
+ const protocolMemo = new Map();
1080
+ const baseMethodsOf = (base) => {
1081
+ if (base.language !== 'swift' || base.kind !== 'interface')
1082
+ return methodsOf(base.id);
1083
+ const hit = protocolMemo.get(base.id);
1084
+ if (hit)
1085
+ return hit;
1086
+ const methods = [
1087
+ ...methodsOf(base.id),
1088
+ ...queries
1089
+ .getNodesByName(base.name)
1090
+ .filter((n) => n.language === 'swift' && n.kind === 'class')
1091
+ .flatMap((n) => methodsOf(n.id)),
1092
+ ];
1093
+ protocolMemo.set(base.id, methods);
1094
+ return methods;
1095
+ };
1109
1096
  // Concrete-side kinds vary by language: `class` covers Java / Kotlin /
1110
1097
  // C# / TS / Swift-classes / Scala-classes; `struct` covers Swift value
1111
1098
  // types that conform to protocols. Iterate both.
1112
- const concreteKinds = ['class', 'struct'];
1099
+ const concreteKinds = ['class', 'struct', 'union'];
1113
1100
  for (const kind of concreteKinds) {
1114
1101
  for (const cls of queries.iterateNodesByKind(kind)) {
1115
1102
  if ((++scanned255 & 63) === 0)
@@ -1140,7 +1127,7 @@ async function interfaceOverrideEdges(queries, onYield) {
1140
1127
  implByName.set(m.name, [m]);
1141
1128
  }
1142
1129
  let added = 0;
1143
- for (const bm of methodsOf(base.id)) {
1130
+ for (const bm of baseMethodsOf(base)) {
1144
1131
  if (added >= MAX_CALLBACKS_PER_CHANNEL)
1145
1132
  break;
1146
1133
  for (const m of implByName.get(bm.name) ?? []) {
@@ -1206,11 +1193,9 @@ async function goGrpcStubImplEdges(queries, onYield) {
1206
1193
  const methodNamesByStruct = new Map();
1207
1194
  const methodNodesByStruct = new Map();
1208
1195
  const goStructs = [];
1209
- for (const s of queries.iterateNodesByKind('struct')) {
1196
+ for (const s of queries.iterateNodesByKindIn('struct', ['go'])) {
1210
1197
  if ((++scanned255 & 63) === 0)
1211
1198
  await onYield();
1212
- if (s.language !== 'go')
1213
- continue;
1214
1199
  goStructs.push(s);
1215
1200
  const ms = queries
1216
1201
  .getOutgoingEdges(s.id, ['contains'])
@@ -1282,6 +1267,60 @@ async function goGrpcStubImplEdges(queries, onYield) {
1282
1267
  }
1283
1268
  return edges;
1284
1269
  }
1270
+ /** Kinds a JSX tag can name. A tag that resolves only to a type is markup we drop. */
1271
+ const JSX_CHILD_KINDS = new Set(['component', 'function', 'class']);
1272
+ /**
1273
+ * The languages a JSX tag can plausibly name a component in. Preferred over a
1274
+ * same-named symbol in another language, never required — a React Native tag
1275
+ * whose only match is the native class it bridges to (`requireNativeComponent`)
1276
+ * still links there.
1277
+ */
1278
+ const JSX_CHILD_LANGUAGES = [...JS_FAMILY, 'vue', 'svelte'];
1279
+ /** `localName` → the project file it is imported from, for one file's imports. */
1280
+ function importedFrom(ctx, file, language) {
1281
+ const out = new Map();
1282
+ for (const im of ctx.getImportMappings(file, language)) {
1283
+ // The mappings name the module as written; the file it is comes from the
1284
+ // same resolution the import resolver uses (aliases, extensions, index files).
1285
+ const resolved = im.resolvedPath ?? (0, import_resolver_1.resolveImportPath)(im.source, file, language, ctx);
1286
+ if (resolved)
1287
+ out.set(im.localName, resolved);
1288
+ }
1289
+ return out;
1290
+ }
1291
+ /**
1292
+ * The component a JSX tag names, among every node that shares the name.
1293
+ *
1294
+ * A tag is written in one file, and that file already says which `FrameCard` it
1295
+ * means: the one it declares, or the one it imports. Taking the FIRST node of
1296
+ * that name — which is what this did — is a coin flip once a name repeats, and
1297
+ * it costs twice over. The parent gets an edge to a component it never renders,
1298
+ * and the component it does render is left with no caller at all, so every walk
1299
+ * back from that subtree dead-ends: on an Expo app whose `<FrameCard/>` was one
1300
+ * of two, the folder sheet's `openBackgroundNoiseDetail` stood alone on Screens
1301
+ * with no screen behind it, while the edge pointed at an unrelated card in
1302
+ * another sheet.
1303
+ *
1304
+ * Same file first — a small component declared beside its use is the commonest
1305
+ * shape, and the one an import can never disambiguate. Then the file the name
1306
+ * is imported from. Then the language, which only decides a tie: a `.tsx` tag
1307
+ * naming both a TS component and a same-named Swift class means the TS one.
1308
+ */
1309
+ function jsxChild(ctx, name, file, importsOf) {
1310
+ const candidates = ctx.getNodesByName(name).filter((n) => JSX_CHILD_KINDS.has(n.kind));
1311
+ if (candidates.length <= 1)
1312
+ return candidates[0];
1313
+ const local = candidates.find((n) => n.filePath === file);
1314
+ if (local)
1315
+ return local;
1316
+ const from = importsOf().get(name);
1317
+ if (from) {
1318
+ const imported = candidates.find((n) => n.filePath === from);
1319
+ if (imported)
1320
+ return imported;
1321
+ }
1322
+ return candidates.find((n) => JSX_CHILD_LANGUAGES.includes(n.language)) ?? candidates[0];
1323
+ }
1285
1324
  /**
1286
1325
  * Phase 5: React JSX child rendering. A component that returns `<Child .../>`
1287
1326
  * mounts Child — React calls it — but JSX instantiation isn't a static call edge,
@@ -1305,7 +1344,14 @@ async function reactJsxChildEdges(ctx, onYield) {
1305
1344
  const content = ctx.readFile(file);
1306
1345
  if (!content || (!content.includes('</') && !content.includes('/>')))
1307
1346
  continue; // JSX-file gate
1308
- const parents = ctx.getNodesInFile(file).filter((n) => PARENT_KINDS.has(n.kind));
1347
+ // File-level language gate, not merely a project-level one: mixed C/JS
1348
+ // monorepos must not interpret `"<Foo/>"` inside C as JSX (#1560).
1349
+ const parents = ctx.getNodesInFile(file).filter((n) => PARENT_KINDS.has(n.kind) && JS_FAMILY.includes(n.language));
1350
+ if (parents.length === 0)
1351
+ continue;
1352
+ // Read once per file, and only when a name actually turns out ambiguous.
1353
+ let imports = null;
1354
+ const importsOf = () => (imports ??= importedFrom(ctx, file, parents[0].language));
1309
1355
  for (const parent of parents) {
1310
1356
  const src = sliceLines(content, parent.startLine, parent.endLine);
1311
1357
  if (!src || (!src.includes('</') && !src.includes('/>')))
@@ -1319,8 +1365,8 @@ async function reactJsxChildEdges(ctx, onYield) {
1319
1365
  for (const name of names) {
1320
1366
  if (added >= MAX_JSX_CHILDREN)
1321
1367
  break;
1322
- const child = ctx.getNodesByName(name).find((n) => n.kind === 'component' || n.kind === 'function' || n.kind === 'class');
1323
- if (!child || child.id === parent.id)
1368
+ const child = jsxChild(ctx, name, file, importsOf);
1369
+ if (!child || child.id === parent.id || (0, name_matcher_1.crossesCodeBoundary)(parent.language, child.language))
1324
1370
  continue;
1325
1371
  const key = `${parent.id}>${child.id}`;
1326
1372
  if (seen.has(key))
@@ -1402,7 +1448,8 @@ async function vueTemplateEdges(ctx, onYield) {
1402
1448
  }
1403
1449
  let added = 0;
1404
1450
  const addEdge = (target, meta) => {
1405
- if (added >= MAX_JSX_CHILDREN || !target || target.id === comp.id)
1451
+ if (added >= MAX_JSX_CHILDREN || !target || target.id === comp.id ||
1452
+ (0, name_matcher_1.crossesCodeBoundary)(comp.language, target.language))
1406
1453
  return;
1407
1454
  const k = `${comp.id}>${target.id}>${meta.synthesizedBy}`;
1408
1455
  if (seen.has(k))
@@ -1479,10 +1526,13 @@ async function vueTemplateEdges(ctx, onYield) {
1479
1526
  * DeviceEventEmitter.addListener("locationUpdate", handler);
1480
1527
  *
1481
1528
  * Synthesize: native dispatch site → JS handler, keyed by the literal
1482
- * event name. Only matches NAMED handlers (the existing `ON_RE` named-
1483
- * capture form). Inline arrow handlers like `addListener('x', d => …)`
1484
- * aren't named at extraction time and would need link-through-body
1485
- * support; matches the deliberate scope of the in-language synthesizer.
1529
+ * event name. A NAMED handler (`addListener('x', handleX)`) is the target
1530
+ * when it is a node; an unnamed one — a parameter passed through, or an
1531
+ * inline `(data) => {…}` written in a `useEffect` — is attributed to the
1532
+ * enclosing function, where the event demonstrably lands. (The in-language
1533
+ * synthesizer stays named-only; this channel pairs across a language
1534
+ * boundary on a literal, which is the evidence that makes the wider
1535
+ * attribution safe.)
1486
1536
  *
1487
1537
  * Provenance `'heuristic'`, synthesizedBy `'rn-event-channel'`.
1488
1538
  */
@@ -1522,9 +1572,9 @@ async function rnEventEdges(ctx, onYield) {
1522
1572
  if (!content)
1523
1573
  continue;
1524
1574
  const nodesInFile = ctx.getNodesInFile(file);
1525
- const lineOf = makeLineAt(content, 1);
1575
+ const lineOf = (0, synth_utils_1.makeLineAt)(content, 1);
1526
1576
  const addDispatcher = (event, line) => {
1527
- const disp = enclosingFn(nodesInFile, line);
1577
+ const disp = (0, synth_utils_1.enclosingFn)(nodesInFile, line);
1528
1578
  if (!disp)
1529
1579
  return;
1530
1580
  const set = nativeDispatchersByEvent.get(event) ?? new Set();
@@ -1590,6 +1640,8 @@ async function rnEventEdges(ctx, onYield) {
1590
1640
  // function (the abstraction layer), giving a reachability-correct
1591
1641
  // hop even when the actual user-side handler lives one call up.
1592
1642
  const ADDLISTENER_ANY = /\.(?:on|once|addListener)\(\s*['"]([^'"]+)['"]\s*,\s*([A-Za-z_][\w.]*)/g;
1643
+ // The inline form: `.addListener('x', (data) => {…})` / `function () {…}`.
1644
+ const ADDLISTENER_INLINE = /\.(?:on|once|addListener)\(\s*['"]([^'"]+)['"]\s*,\s*(?:async\s*)?(?:\([^)]*\)\s*=>|[A-Za-z_$][\w$]*\s*=>|function\s*\()/g;
1593
1645
  ADDLISTENER_ANY.lastIndex = 0;
1594
1646
  let m;
1595
1647
  while ((m = ADDLISTENER_ANY.exec(content))) {
@@ -1607,7 +1659,7 @@ async function rnEventEdges(ctx, onYield) {
1607
1659
  // Fall back to the enclosing function — the subscribe-wrapper
1608
1660
  // pattern means the event fires THROUGH this function on its
1609
1661
  // way to user code. Reachability-correct attribution.
1610
- const enclosing = enclosingFn(nodesInFile, lineOf(m.index));
1662
+ const enclosing = (0, synth_utils_1.enclosingFn)(nodesInFile, lineOf(m.index));
1611
1663
  targetId = enclosing?.id ?? null;
1612
1664
  }
1613
1665
  if (!targetId) {
@@ -1637,6 +1689,26 @@ async function rnEventEdges(ctx, onYield) {
1637
1689
  map.set(targetId, `${file}:${lineOf(m.index)}`);
1638
1690
  jsHandlersByEvent.set(event, map);
1639
1691
  }
1692
+ // Inline listeners — the shape a React component registers inside a
1693
+ // `useEffect`: `nativeEmitter.addListener('onCaptureComplete', (data) =>
1694
+ // { … router.push('/review') })`. There is no handler symbol to name,
1695
+ // so the subscription is attributed to the enclosing function exactly
1696
+ // as the unnamed-argument form above is: the native event lands in that
1697
+ // component, and its body is where a reader looks next. This channel
1698
+ // only — the in-language synthesizer keeps its named-handler policy.
1699
+ ADDLISTENER_INLINE.lastIndex = 0;
1700
+ while ((m = ADDLISTENER_INLINE.exec(content))) {
1701
+ const event = m[1];
1702
+ if (!event)
1703
+ continue;
1704
+ const enclosing = (0, synth_utils_1.enclosingFn)(nodesInFile, lineOf(m.index));
1705
+ if (!enclosing)
1706
+ continue;
1707
+ const map = jsHandlersByEvent.get(event) ?? new Map();
1708
+ if (!map.has(enclosing.id))
1709
+ map.set(enclosing.id, `${file}:${lineOf(m.index)}`);
1710
+ jsHandlersByEvent.set(event, map);
1711
+ }
1640
1712
  }
1641
1713
  }
1642
1714
  const edges = [];
@@ -1783,11 +1855,9 @@ async function rnCrossPlatformEdges(queries, onYield) {
1783
1855
  // impls in ≥2 native languages can pair, so the per-method JS-caller check
1784
1856
  // below only runs for genuine cross-platform candidates.
1785
1857
  const byName = new Map();
1786
- for (const m of queries.iterateNodesByKind('method')) {
1858
+ for (const m of queries.iterateNodesByKindIn('method', [...NATIVE])) {
1787
1859
  if ((++scanned255 & 63) === 0)
1788
1860
  await onYield();
1789
- if (!NATIVE.has(m.language))
1790
- continue;
1791
1861
  const key = norm(m.name);
1792
1862
  const arr = byName.get(key);
1793
1863
  if (arr)
@@ -1919,21 +1989,18 @@ async function mybatisJavaXmlEdges(queries, onYield) {
1919
1989
  // stream below. Same rowid stream order as matching inline, so the edge
1920
1990
  // output is byte-identical when mappers do exist.
1921
1991
  const xmlMethods = [];
1922
- for (const m of queries.iterateNodesByKind('method')) {
1992
+ for (const m of queries.iterateNodesByKindIn('method', ['xml'])) {
1923
1993
  if ((++scanned255 & 63) === 0)
1924
1994
  await onYield();
1925
- if (m.language === 'xml')
1926
- xmlMethods.push(m);
1995
+ xmlMethods.push(m);
1927
1996
  }
1928
1997
  if (xmlMethods.length === 0)
1929
1998
  return edges;
1930
1999
  // Index Java methods by `<ClassName>::<methodName>` for O(1) lookup.
1931
2000
  const javaIndex = new Map();
1932
- for (const m of queries.iterateNodesByKind('method')) {
2001
+ for (const m of queries.iterateNodesByKindIn('method', ['java', 'kotlin'])) {
1933
2002
  if ((++scanned255 & 63) === 0)
1934
2003
  await onYield();
1935
- if (m.language !== 'java' && m.language !== 'kotlin')
1936
- continue;
1937
2004
  const parts = m.qualifiedName.split('::');
1938
2005
  const last = parts[parts.length - 1];
1939
2006
  const cls = parts[parts.length - 2];
@@ -2065,11 +2132,9 @@ async function ginMiddlewareChainEdges(queries, ctx, onYield) {
2065
2132
  let scannedFiles = 0;
2066
2133
  // 1. Find the chain dispatcher(s): a Go method that invokes a `handlers` slice by index.
2067
2134
  const dispatchers = [];
2068
- for (const n of queries.iterateNodesByKind('method')) {
2135
+ for (const n of queries.iterateNodesByKindIn('method', ['go'])) {
2069
2136
  if ((++scanned255 & 63) === 0)
2070
2137
  await onYield();
2071
- if (n.language !== 'go')
2072
- continue;
2073
2138
  const content = ctx.readFile(n.filePath);
2074
2139
  const src = content && sliceLines(content, n.startLine, n.endLine);
2075
2140
  if (src && GIN_DISPATCH_RE.test(src))
@@ -2203,12 +2268,17 @@ async function svelteKitLoadEdges(ctx, onYield) {
2203
2268
  if (!allFiles.has(loaderFile))
2204
2269
  continue;
2205
2270
  for (const hook of ctx.getNodesInFile(loaderFile)) {
2206
- if (!HOOK_KINDS.has(hook.kind) || !HOOKS.has(hook.name))
2271
+ // `load` and `actions` by name, and every function the loader file
2272
+ // declares — a form action is an arrow inside `actions`, and it is a
2273
+ // node of its own (`default`, `logout`), where the redirect that ends
2274
+ // the submission is actually written.
2275
+ const named = HOOK_KINDS.has(hook.kind) && HOOKS.has(hook.name);
2276
+ if (!named && hook.kind !== 'function' && hook.kind !== 'method')
2207
2277
  continue;
2208
2278
  edges.push({
2209
2279
  source: page.id,
2210
2280
  target: hook.id,
2211
- kind: 'references',
2281
+ kind: 'calls',
2212
2282
  line: page.startLine,
2213
2283
  provenance: 'heuristic',
2214
2284
  metadata: {
@@ -2306,12 +2376,38 @@ async function reduxThunkEdges(queries, ctx, onYield) {
2306
2376
  // the SAME file (the cross-file barrel-namespace variant, e.g. trezor's getMethod, is
2307
2377
  // deferred). Gated on a real object literal with ≥2 entries that RESOLVE to callables (a
2308
2378
  // `{ width: 5 }` literal resolves to nothing → no edges); fan-out capped.
2309
- const REGISTRY_ASSIGN_RE = /(?:(?:const|let|var)\s+([A-Za-z_$][\w$]*)|((?:this\.)?[A-Za-z_$][\w$]*))\s*=\s*\{/g;
2310
- const REGISTRY_DISPATCH_RE = /(?:\bnew\s+)?((?:this\.)?[A-Za-z_$][\w$]*)\s*\[\s*([A-Za-z_$][\w$.]*)\s*\]\s*(?:\(|\.[A-Za-z_$])/g;
2379
+ // Both scans only START a name at an identifier's first character (or at a `this.`): the
2380
+ // engine otherwise retries the greedy name at every later character of every identifier in
2381
+ // the file. A match found from inside an identifier always has one from the identifier's
2382
+ // start (same name tail, same continuation), so the result is unchanged — except where the
2383
+ // scan itself resumes mid-identifier after a dispatch ending in `.method`, which
2384
+ // `nextRegistryDispatch` covers with the unguarded pattern.
2385
+ const REGISTRY_NAME_START = String.raw `(?:(?<![A-Za-z_$])(?<![A-Za-z_$][\w$]+)|(?=this\.))`;
2386
+ const REGISTRY_ASSIGN_RE = new RegExp(String.raw `(?:(?:const|let|var)\s+([A-Za-z_$][\w$]*)|${REGISTRY_NAME_START}((?:this\.)?[A-Za-z_$][\w$]*))\s*=\s*\{`, 'g');
2387
+ const REGISTRY_DISPATCH_SRC = String.raw `(?:\bnew\s+)?((?:this\.)?[A-Za-z_$][\w$]*)\s*\[\s*([A-Za-z_$][\w$.]*)\s*\]\s*(?:\(|\.[A-Za-z_$])`;
2388
+ const REGISTRY_DISPATCH_AT = new RegExp(REGISTRY_DISPATCH_SRC, 'y');
2389
+ // `\bnew` can follow a `$` inside an identifier run, hence the extra `(?<!\w)` start.
2390
+ const REGISTRY_DISPATCH_RE = new RegExp(String.raw `(?:(?<!\w)|${REGISTRY_NAME_START})${REGISTRY_DISPATCH_SRC}`, 'g');
2391
+ const IDENT_RUN_CHAR = /[\w$]/;
2311
2392
  const REGISTRY_MIN_ENTRIES = 2;
2312
2393
  const REGISTRY_FANOUT_CAP = 40;
2313
2394
  const REGISTRY_CLASS_ENTRY = new Set(['execute', 'run', 'handle', 'perform', 'process', 'call', 'apply', 'dispatch']);
2314
2395
  const REGISTRY_JS_EXT = /\.(?:ts|tsx|js|jsx|mjs|cjs)$/;
2396
+ /** The first registry dispatch at or after `from`, exactly as a `g` scan of the unguarded
2397
+ * pattern resuming at `from` would find it. */
2398
+ function nextRegistryDispatch(src, from) {
2399
+ let at = from;
2400
+ if (at > 0 && at < src.length && IDENT_RUN_CHAR.test(src[at - 1]) && IDENT_RUN_CHAR.test(src[at])) {
2401
+ for (; at < src.length && IDENT_RUN_CHAR.test(src[at]); at++) {
2402
+ REGISTRY_DISPATCH_AT.lastIndex = at;
2403
+ const m = REGISTRY_DISPATCH_AT.exec(src);
2404
+ if (m)
2405
+ return m;
2406
+ }
2407
+ }
2408
+ REGISTRY_DISPATCH_RE.lastIndex = at;
2409
+ return REGISTRY_DISPATCH_RE.exec(src);
2410
+ }
2315
2411
  /** From the index of an opening `{`, return the brace-balanced body up to its matching `}`. */
2316
2412
  function braceBody(src, openIdx) {
2317
2413
  let depth = 0;
@@ -2400,13 +2496,12 @@ async function objectRegistryEdges(ctx, onYield) {
2400
2496
  const safe = (0, strip_comments_1.stripCommentsForRegex)(content, /\.(?:jsx?|mjs|cjs)$/.test(file) ? 'javascript' : 'typescript');
2401
2497
  // 1. Dispatch sites: `(new )?<ref>[<ident-key>]` followed by a call or a chained method.
2402
2498
  // A quoted-string key (`['save']`) does NOT match — that's a static access, not dispatch.
2403
- REGISTRY_DISPATCH_RE.lastIndex = 0;
2499
+ const lineOf = (0, synth_utils_1.makeLineAt)(safe, 1);
2404
2500
  const dispatches = [];
2405
- let dm;
2406
- while ((dm = REGISTRY_DISPATCH_RE.exec(safe))) {
2501
+ for (let dm = nextRegistryDispatch(safe, 0); dm; dm = nextRegistryDispatch(safe, dm.index + dm[0].length)) {
2407
2502
  const win = safe.slice(dm.index, dm.index + 160);
2408
2503
  const cm = /\]\s*\([^)]*\)\s*\.\s*([A-Za-z_$][\w$]*)/.exec(win) || /\]\s*\.\s*([A-Za-z_$][\w$]*)/.exec(win);
2409
- dispatches.push({ ref: dm[1], line: safe.slice(0, dm.index).split('\n').length, chained: cm ? cm[1] : null });
2504
+ dispatches.push({ ref: dm[1], line: lineOf(dm.index), chained: cm ? cm[1] : null });
2410
2505
  }
2411
2506
  if (!dispatches.length)
2412
2507
  continue;
@@ -2427,7 +2522,7 @@ async function objectRegistryEdges(ctx, onYield) {
2427
2522
  continue;
2428
2523
  const names = registryEntryNames(body); // depth-0 `key: Identifier` entries only
2429
2524
  if (names.length >= REGISTRY_MIN_ENTRIES) {
2430
- registries.set(lhs, { names, line: safe.slice(0, am.index).split('\n').length });
2525
+ registries.set(lhs, { names, line: lineOf(am.index) });
2431
2526
  }
2432
2527
  }
2433
2528
  if (!registries.size)
@@ -2438,7 +2533,7 @@ async function objectRegistryEdges(ctx, onYield) {
2438
2533
  const reg = registries.get(norm(d.ref));
2439
2534
  if (!reg)
2440
2535
  continue;
2441
- const disp = enclosingFn(nodesInFile, d.line);
2536
+ const disp = (0, synth_utils_1.enclosingFn)(nodesInFile, d.line);
2442
2537
  if (!disp)
2443
2538
  continue;
2444
2539
  let added = 0;
@@ -2597,7 +2692,7 @@ async function piniaStoreEdges(ctx, onYield) {
2597
2692
  continue;
2598
2693
  const method = cm[2];
2599
2694
  const line = safe.slice(0, cm.index).split('\n').length;
2600
- const disp = enclosingFn(nodesInFile, line) ?? fallbackDispatcher;
2695
+ const disp = (0, synth_utils_1.enclosingFn)(nodesInFile, line) ?? fallbackDispatcher;
2601
2696
  if (!disp)
2602
2697
  continue;
2603
2698
  const target = ctx
@@ -2695,7 +2790,7 @@ async function vuexDispatchEdges(ctx, onYield) {
2695
2790
  while ((m = VUEX_DISPATCH_RE.exec(safe)) && added < VUEX_FANOUT_CAP) {
2696
2791
  const key = m[1];
2697
2792
  const line = safe.slice(0, m.index).split('\n').length;
2698
- const disp = enclosingFn(nodesInFile, line) ?? fallback;
2793
+ const disp = (0, synth_utils_1.enclosingFn)(nodesInFile, line) ?? fallback;
2699
2794
  if (!disp)
2700
2795
  continue;
2701
2796
  const target = resolve(key, file);
@@ -2800,7 +2895,7 @@ async function celeryDispatchEdges(ctx, onYield) {
2800
2895
  while ((m = CELERY_DISPATCH_RE.exec(safe)) && added < CELERY_FANOUT_CAP) {
2801
2896
  const name = m[1];
2802
2897
  const line = safe.slice(0, m.index).split('\n').length;
2803
- const disp = enclosingFn(nodesInFile, line);
2898
+ const disp = (0, synth_utils_1.enclosingFn)(nodesInFile, line);
2804
2899
  if (!disp)
2805
2900
  continue; // module-level dispatch — no source symbol to attribute
2806
2901
  const target = resolve(name, file);
@@ -2942,7 +3037,7 @@ async function springEventEdges(ctx, onYield) {
2942
3037
  if (!targets || !targets.length)
2943
3038
  continue;
2944
3039
  const line = safe.slice(0, m.index).split('\n').length;
2945
- const disp = enclosingFn(nodesInFile, line);
3040
+ const disp = (0, synth_utils_1.enclosingFn)(nodesInFile, line);
2946
3041
  if (!disp)
2947
3042
  continue;
2948
3043
  for (const target of targets) {
@@ -3070,7 +3165,7 @@ async function mediatrDispatchEdges(ctx, onYield) {
3070
3165
  if (!MEDIATR_RECEIVER_RE.test(m[1]))
3071
3166
  continue; // not a mediator (MessagingCenter, HttpClient, …)
3072
3167
  const line = safe.slice(0, m.index).split('\n').length;
3073
- const disp = enclosingFn(nodesInFile, line);
3168
+ const disp = (0, synth_utils_1.enclosingFn)(nodesInFile, line);
3074
3169
  if (!disp)
3075
3170
  continue;
3076
3171
  const type = resolveMediatrArgType(m[2], safeLines, disp.startLine, line);
@@ -3170,7 +3265,7 @@ async function sidekiqDispatchEdges(ctx, onYield) {
3170
3265
  let added = 0;
3171
3266
  while ((m = SIDEKIQ_DISPATCH_RE.exec(safe)) && added < SIDEKIQ_FANOUT_CAP) {
3172
3267
  const line = safe.slice(0, m.index).split('\n').length;
3173
- const disp = enclosingFn(nodesInFile, line);
3268
+ const disp = (0, synth_utils_1.enclosingFn)(nodesInFile, line);
3174
3269
  if (!disp)
3175
3270
  continue;
3176
3271
  const target = resolve(m[1]);
@@ -3230,6 +3325,10 @@ const ERLANG_BEHAVIOUR_FANOUT_CAP = 24;
3230
3325
  */
3231
3326
  function erlangArityAt(src, openIdx) {
3232
3327
  let depth = 1;
3328
+ // `<<1,2,3>>` binary literals: commas inside are element separators, not
3329
+ // argument separators. Tracked separately from bracket depth because the
3330
+ // single-char `<`/`>` comparison operators must stay inert (#1358).
3331
+ let binDepth = 0;
3233
3332
  let commas = 0;
3234
3333
  let sawArg = false;
3235
3334
  const limit = Math.min(src.length, openIdx + 4000);
@@ -3252,6 +3351,17 @@ function erlangArityAt(src, openIdx) {
3252
3351
  sawArg = true;
3253
3352
  continue;
3254
3353
  }
3354
+ if (ch === '<' && src[i + 1] === '<') {
3355
+ binDepth++;
3356
+ i++;
3357
+ sawArg = true;
3358
+ continue;
3359
+ }
3360
+ if (ch === '>' && src[i + 1] === '>' && binDepth > 0) {
3361
+ binDepth--;
3362
+ i++;
3363
+ continue;
3364
+ }
3255
3365
  if (ch === '(' || ch === '[' || ch === '{') {
3256
3366
  depth++;
3257
3367
  sawArg = true;
@@ -3263,7 +3373,7 @@ function erlangArityAt(src, openIdx) {
3263
3373
  return sawArg ? commas + 1 : 0;
3264
3374
  continue;
3265
3375
  }
3266
- if (ch === ',' && depth === 1) {
3376
+ if (ch === ',' && depth === 1 && binDepth === 0) {
3267
3377
  commas++;
3268
3378
  continue;
3269
3379
  }
@@ -3347,13 +3457,11 @@ async function nixOptionPathEdges(queries, onYield) {
3347
3457
  const byFile = new Map();
3348
3458
  let scanned = 0;
3349
3459
  for (const kind of ['variable', 'function']) {
3350
- for (const node of queries.iterateNodesByKind(kind)) {
3460
+ for (const node of queries.iterateNodesByKindIn(kind, ['nix'])) {
3351
3461
  if ((++scanned255 & 63) === 0)
3352
3462
  await onYield();
3353
3463
  if ((++scanned & 0x3fff) === 0 && onYield)
3354
3464
  await onYield();
3355
- if (node.language !== 'nix')
3356
- continue;
3357
3465
  const segs = nixLeadingPlainSegments(node.name);
3358
3466
  if (segs.length === 0)
3359
3467
  continue;
@@ -3378,7 +3486,7 @@ async function nixOptionPathEdges(queries, onYield) {
3378
3486
  // own namespace (`attrsOf (submodule { options = ...; })`) — its internals
3379
3487
  // are not globally addressable, so the sentinel blocks registration below it
3380
3488
  // while still excluding the region from write candidates.
3381
- const SUBMODULE = 'submodule';
3489
+ const SUBMODULE = '\u0000submodule';
3382
3490
  const decls = new Map();
3383
3491
  const writes = [];
3384
3492
  const register = (path, rec) => {
@@ -3468,11 +3576,10 @@ async function erlangBehaviourDispatchEdges(queries, ctx, onYield) {
3468
3576
  // Cheap language gate: no Erlang modules → no cost beyond one streamed
3469
3577
  // kind scan (never a materialized array of every namespace — #1212).
3470
3578
  const erlangModules = [];
3471
- for (const n of queries.iterateNodesByKind('namespace')) {
3579
+ for (const n of queries.iterateNodesByKindIn('namespace', ['erlang'])) {
3472
3580
  if ((++scanned255 & 63) === 0)
3473
3581
  await onYield();
3474
- if (n.language === 'erlang')
3475
- erlangModules.push(n);
3582
+ erlangModules.push(n);
3476
3583
  }
3477
3584
  if (erlangModules.length === 0)
3478
3585
  return [];
@@ -3519,12 +3626,18 @@ async function erlangBehaviourDispatchEdges(queries, ctx, onYield) {
3519
3626
  }
3520
3627
  if (declaringBehaviours.size === 0)
3521
3628
  return [];
3522
- // Implementer target lookup, lazy per (behaviour, fn): implementers come
3523
- // from the `implements` edges extraction resolved, and the target is the
3524
- // implementer module's own exported `fn` function node.
3629
+ // Implementer target lookup, lazy per (behaviour, fn, arity): implementers
3630
+ // come from the `implements` edges extraction resolved, and the target is
3631
+ // the implementer module's own exported `fn` node OF THE SITE'S ARITY —
3632
+ // function qualifiedNames carry arity (`mod::fn/2`, #1610), so the arity the
3633
+ // dispatch site used selects among same-named definitions.
3525
3634
  const targetCache = new Map();
3526
- const targetsOf = (behaviour, fn) => {
3527
- const cacheKey = `${behaviour.id}#${fn}`;
3635
+ const qnArity = (qn) => {
3636
+ const m = /\/(\d{1,3})$/.exec(qn);
3637
+ return m ? Number(m[1]) : -1;
3638
+ };
3639
+ const targetsOf = (behaviour, fn, arity) => {
3640
+ const cacheKey = `${behaviour.id}#${fn}/${arity}`;
3528
3641
  let targets = targetCache.get(cacheKey);
3529
3642
  if (targets)
3530
3643
  return targets;
@@ -3535,7 +3648,10 @@ async function erlangBehaviourDispatchEdges(queries, ctx, onYield) {
3535
3648
  continue;
3536
3649
  const fnNode = ctx
3537
3650
  .getNodesInFile(impl.filePath)
3538
- .find((n) => n.kind === 'function' && n.name === fn && n.isExported !== false);
3651
+ .find((n) => n.kind === 'function' &&
3652
+ n.name === fn &&
3653
+ qnArity(n.qualifiedName) === arity &&
3654
+ n.isExported !== false);
3539
3655
  if (fnNode)
3540
3656
  targets.push(fnNode);
3541
3657
  }
@@ -3570,11 +3686,11 @@ async function erlangBehaviourDispatchEdges(queries, ctx, onYield) {
3570
3686
  if (!behaviours || behaviours.length !== 1)
3571
3687
  continue; // unknown or ambiguous
3572
3688
  const behaviour = behaviours[0];
3573
- const targets = targetsOf(behaviour, fn);
3689
+ const targets = targetsOf(behaviour, fn, arity);
3574
3690
  if (targets.length === 0 || targets.length > ERLANG_BEHAVIOUR_FANOUT_CAP)
3575
3691
  continue;
3576
3692
  const line = safe.slice(0, m.index).split('\n').length;
3577
- const disp = enclosingFn(nodesInFile, line);
3693
+ const disp = (0, synth_utils_1.enclosingFn)(nodesInFile, line);
3578
3694
  if (!disp)
3579
3695
  continue;
3580
3696
  for (const target of targets) {
@@ -3729,7 +3845,7 @@ async function laravelEventEdges(ctx, onYield) {
3729
3845
  if (!targets)
3730
3846
  continue;
3731
3847
  const line = safe.slice(0, m.index).split('\n').length;
3732
- const disp = enclosingFn(nodesInFile, line);
3848
+ const disp = (0, synth_utils_1.enclosingFn)(nodesInFile, line);
3733
3849
  if (!disp)
3734
3850
  continue;
3735
3851
  for (const target of targets.values()) {
@@ -3753,27 +3869,43 @@ async function laravelEventEdges(ctx, onYield) {
3753
3869
  }
3754
3870
  return edges;
3755
3871
  }
3756
- /**
3757
- * Synthesize dispatcher→callback edges (field observers + EventEmitters +
3758
- * React re-render + JSX children + Vue templates + SvelteKit load + RN event
3759
- * channel + Fabric native-impl + MyBatis Java↔XML + Gin middleware chain +
3760
- * Redux-thunk dispatch chain + object-literal registry dispatch + RTK Query
3761
- * generated-hook → endpoint + Pinia useStore().action() + Vuex string dispatch +
3762
- * Celery task .delay()/.apply_async() → task body + Spring publishEvent → @EventListener +
3763
- * MediatR Send/Publish → IRequestHandler/INotificationHandler +
3764
- * Sidekiq Worker.perform_async → #perform + Laravel event(new X) → listener handle).
3765
- * Returns the count added. Never throws into indexing — callers wrap in try/catch.
3766
- */
3767
- /**
3768
- * Number of progress steps synthesizeCallbackEdges reports: one per `__mark()`
3769
- * call (every synthesis pass, plus the dedupe-merge and edge-insert steps).
3770
- * Cosmetic only — drift just makes the progress bar end early or jump — and a
3771
- * test pins it to the actual step count (registry passes + the fixed
3772
- * pre/post marks) so adding a pass without bumping this fails loudly instead
3773
- * of silently skewing the bar.
3774
- */
3775
- const JS_FAMILY = ['typescript', 'javascript', 'tsx', 'jsx'];
3776
3872
  const ALWAYS = () => true;
3873
+ /** Conservative input gates for SYNTH_PASSES; keep these in sync when adding a pass. */
3874
+ function hasSynthesisPattern(filePath, content) {
3875
+ // These passes consume declarations/layouts as well as dispatch sites. A
3876
+ // header or markup edit can change a channel whose endpoints live elsewhere.
3877
+ if (/\.(?:vue|svelte|dfm|fmx|nix|xml)$/.test(filePath))
3878
+ return true;
3879
+ if (/\.(?:c|h|cc|cpp|cxx|hpp|hh|hxx|cppm|ipp|inl|tcc|def|inc|tbl)$/i.test(filePath) &&
3880
+ /\b(?:struct|union|typedef|virtual|override)\b|#\s*(?:include|define|if)|=|->|\[/.test(content))
3881
+ return true;
3882
+ if (/\b(?:class|interface|protocol|trait|impl|extends|implements|expect|actual)\b/.test(content))
3883
+ return true;
3884
+ if (/\.go$/.test(filePath) && /\b(?:struct|interface)\b|\bfunc\s*\(/.test(content))
3885
+ return true;
3886
+ if ((0, tier_synthesizer_1.hasCrossTierPattern)(content) || (0, tier_synthesizer_1.hasTestRequestPattern)(filePath, content))
3887
+ return true;
3888
+ if (/\b(?:render|build|setState|defineStore|createStore|createApi|Store|href|sendEvent|sendEventWithName)\b|<\/|\/>/.test(content))
3889
+ return true;
3890
+ if (/\.(?:forEach|append|add|push|insert|fire|dispatchEvent|addListener|Use|GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD|Any|Handle)\s*\(/.test(content))
3891
+ return true;
3892
+ if (/[\w$]\s*\[\s*[A-Za-z_$]/.test(content) || /\b(?:dispatch|commit)\s*\(/.test(content))
3893
+ return true;
3894
+ const patterns = [THUNK_DECL_RE, CELERY_TASK_DECORATOR_RE, CELERY_DISPATCH_RE,
3895
+ SPRING_LISTENER_ANNO_RE, SPRING_APP_LISTENER_RE, SPRING_PUBLISH_RE,
3896
+ MEDIATR_HANDLER_BASE_RE, MEDIATR_DISPATCH_RE, SIDEKIQ_WORKER_RE, SIDEKIQ_DISPATCH_RE,
3897
+ ERLANG_CALLBACK_DECL_RE, ERLANG_DISPATCH_RE, LARAVEL_DISPATCH_RE, ARKUI_EMITTER_CALL_RE,
3898
+ ARKUI_ROUTER_RE];
3899
+ for (const re of patterns) {
3900
+ re.lastIndex = 0;
3901
+ const matches = re.test(content);
3902
+ re.lastIndex = 0;
3903
+ if (matches)
3904
+ return true;
3905
+ }
3906
+ // Field-backed observers use method-name gates rather than fixed call names.
3907
+ return (content.match(/[A-Za-z_$][\w$]*/g) ?? []).some((name) => REGISTRAR_NAME.test(name) || DISPATCHER_NAME.test(name));
3908
+ }
3777
3909
  /**
3778
3910
  * The independent passes, in MERGE ORDER — the first-seen dedup in
3779
3911
  * synthesizeCallbackEdges follows this array, so reordering entries changes
@@ -3784,9 +3916,18 @@ const ALWAYS = () => true;
3784
3916
  exports.SYNTH_PASSES = [
3785
3917
  { name: 'fieldEdges', gate: ALWAYS, run: (q, c, y) => fieldChannelEdges(q, c, y) },
3786
3918
  { name: 'closureCollEdges', gate: ALWAYS, run: (q, c, y) => closureCollectionEdges(q, c, y) },
3919
+ // Cross-tier channels — a client's `fetch('/api/x')` onto its own route,
3920
+ // a queue job onto its consumer, a bus / socket event onto its handler.
3921
+ // Before the in-process emitter pass: the same (source, target) pair
3922
+ // keeps the more specific edge — the one that says which tier it crosses.
3923
+ { name: 'tierEdges', gate: (has) => has(...JS_FAMILY), run: (_q, c, y) => (0, tier_synthesizer_1.crossTierEdges)(c, y) },
3924
+ // A Spring / Laravel test's request (`mockMvc.perform(post("/x"))`,
3925
+ // `$this->postJson('api/x')`) onto the route it reaches, so the handler
3926
+ // counts as tested.
3927
+ { name: 'testRequestEdges', gate: (has) => has('java', 'kotlin', 'php'), run: (_q, c, y) => (0, tier_synthesizer_1.testRequestEdges)(c, y) },
3787
3928
  { name: 'emitterEdges', gate: ALWAYS, run: (_q, c, y) => eventEmitterEdges(c, y) },
3788
3929
  { name: 'renderEdges', gate: ALWAYS, run: (q, c, y) => reactRenderEdges(q, c, y) },
3789
- { name: 'jsxEdges', gate: ALWAYS, run: (_q, c, y) => reactJsxChildEdges(c, y) },
3930
+ { name: 'jsxEdges', gate: (has) => has(...JS_FAMILY), run: (_q, c, y) => reactJsxChildEdges(c, y) },
3790
3931
  { name: 'vueEdges', gate: (has) => has('vue'), run: (_q, c, y) => vueTemplateEdges(c, y) },
3791
3932
  { name: 'svelteKitEdges', gate: (has) => has('svelte'), run: (_q, c, y) => svelteKitLoadEdges(c, y) },
3792
3933
  { name: 'pascalEdges', gate: ALWAYS, run: (_q, c, y) => pascalFormEdges(c, y) },
@@ -3840,8 +3981,31 @@ exports.SYNTH_PASSES = [
3840
3981
  run: (q, c, y, sub) => (0, c_fnptr_synthesizer_1.cFnPointerDispatchEdges)(q, c, y, sub),
3841
3982
  },
3842
3983
  { name: 'goframeEdges', gate: (has) => has('go'), run: (_q, c, y) => (0, goframe_synthesizer_1.goframeRouteEdges)(c, y) },
3984
+ // `router.push(await helper())` — the helper's return literals are the screens.
3985
+ { name: 'expoRouterReturnEdges', gate: (has) => has(...JS_FAMILY), run: (_q, c, y) => (0, expo_router_synthesizer_1.expoRouterReturnEdges)(c, y) },
3986
+ // `<Link href="/x">` / an internal `<a href>` — markup, not a call; the component navigates.
3987
+ { name: 'nextLinkEdges', gate: (has) => has(...JS_FAMILY), run: (_q, c, y) => (0, next_router_synthesizer_1.nextLinkEdges)(c, y) },
3988
+ { name: 'reactRouterLinkEdges', gate: (has) => has(...JS_FAMILY), run: (_q, c, y) => (0, react_router_synthesizer_1.reactRouterLinkEdges)(c, y) },
3989
+ { name: 'tanstackLinkEdges', gate: (has) => has(...JS_FAMILY), run: (_q, c, y) => (0, tanstack_router_synthesizer_1.tanstackLinkEdges)(c, y) },
3990
+ { name: 'vueRouterLinkEdges', gate: (has) => has('vue', ...JS_FAMILY), run: (_q, c, y) => (0, vue_router_synthesizer_1.vueRouterLinkEdges)(c, y) },
3991
+ { name: 'svelteKitPageEdges', gate: (has) => has('svelte'), run: (_q, c, y) => (0, sveltekit_synthesizer_1.svelteKitPageComponentEdges)(c, y) },
3992
+ { name: 'svelteKitLinkEdges', gate: (has) => has('svelte'), run: (_q, c, y) => (0, sveltekit_synthesizer_1.svelteKitLinkEdges)(c, y) },
3843
3993
  { name: 'nixOptionEdges', gate: (has) => has('nix'), run: (q, _c, y) => nixOptionPathEdges(q, y) },
3844
3994
  ];
3995
+ /**
3996
+ * Rough relative cost of the passes that run longest on large repos. Only
3997
+ * the pooled dispatch ORDER reads it — heaviest first, so the longest passes
3998
+ * start before the short ones fill the workers; unlisted passes keep registry
3999
+ * order after these. A wrong hint costs wall time, never edges.
4000
+ */
4001
+ const SYNTH_PASS_COST_HINT = {
4002
+ cFnPtrEdges: 100, registryEdges: 60, tierEdges: 55, jsxEdges: 25, rnEventEdgesList: 24,
4003
+ ifaceEdges: 18, flutterEdges: 16, cppEdges: 15, emitterEdges: 12, fieldEdges: 11,
4004
+ mybatisEdges: 10, vuexEdges: 8, closureCollEdges: 6, piniaEdges: 5, renderEdges: 4,
4005
+ };
4006
+ function synthPassCostHint(name) {
4007
+ return SYNTH_PASS_COST_HINT[name] ?? 0;
4008
+ }
3845
4009
  /** Fixed non-registry steps: goMethodContains, goImplements, dedupe-merge, insertMergedEdges. */
3846
4010
  const FIXED_SYNTH_STEPS = 4;
3847
4011
  exports.SYNTH_PROGRESS_STEPS = exports.SYNTH_PASSES.length + FIXED_SYNTH_STEPS;
@@ -3853,7 +4017,10 @@ pool,
3853
4017
  // WAL-valve writer backstop (WalCheckpointValve.backpressure), called at
3854
4018
  // pool-idle points in the edge-insert loops below — the passes themselves
3855
4019
  // only read; every write in this function happens with the pool idle.
3856
- backpressure) {
4020
+ backpressure,
4021
+ // Main-thread work to run while the pool computes the passes (the main
4022
+ // thread otherwise only waits there) — e.g. building a deferred index.
4023
+ whilePoolBusy) {
3857
4024
  // Each sub-pass below is a whole-graph scan, and there are ~30 of them, all
3858
4025
  // running synchronously on the indexer's main thread. Their AGGREGATE can run
3859
4026
  // for well over a minute on a large repo — long enough for the #850 liveness
@@ -3979,8 +4146,23 @@ backpressure) {
3979
4146
  // synthesized edges; the index still completes.
3980
4147
  const MAIN_RETRY_MAX_NODES = 1_500_000;
3981
4148
  const graphNodes = queries.getNodeAndEdgeCount().nodes;
4149
+ // Files whose text could feed a pass (inputs that produce NO edges included,
4150
+ // e.g. an over-cap channel: deleting one may make the full pass viable). It
4151
+ // depends only on the files, so with a pool the main thread reads them while
4152
+ // the workers run the passes instead of after.
4153
+ const collectInputs = async () => {
4154
+ const found = [];
4155
+ for (const file of ctx.getAllFiles()) {
4156
+ const content = ctx.readFile(file);
4157
+ if (content !== null && hasSynthesisPattern(file, content))
4158
+ found.push(file);
4159
+ await yieldToLoop();
4160
+ }
4161
+ return found;
4162
+ };
4163
+ let inputs = null;
3982
4164
  if (pool && gatedIn.length > 1) {
3983
- await Promise.all(gatedIn.map(async (i) => {
4165
+ const runPooled = async (i) => {
3984
4166
  const pass = exports.SYNTH_PASSES[i];
3985
4167
  try {
3986
4168
  const out = await pool.runSynthPass(pass.name);
@@ -3999,7 +4181,25 @@ backpressure) {
3999
4181
  // mismatch): retry this one pass on the main thread.
4000
4182
  await runPassOnMain(i);
4001
4183
  }
4002
- }));
4184
+ };
4185
+ // One pass per worker at a time, heaviest first. Handing every pass out
4186
+ // at once split them by count, and a worker interleaves what it holds, so
4187
+ // a heavy pass finished only with its worker's whole share: on vscode the
4188
+ // 6s registry pass ended ~16s in. Pulling keeps workers busy until the
4189
+ // queue drains. Edges merge by registry index below, so order is free.
4190
+ const lanes = Math.min(pool.size ?? gatedIn.length, gatedIn.length);
4191
+ const queue = [...gatedIn].sort((a, b) => synthPassCostHint(exports.SYNTH_PASSES[b].name) - synthPassCostHint(exports.SYNTH_PASSES[a].name) || a - b);
4192
+ const lane = async () => {
4193
+ for (let i = queue.shift(); i !== undefined; i = queue.shift())
4194
+ await runPooled(i);
4195
+ };
4196
+ const fanOut = Promise.all(Array.from({ length: lanes }, lane));
4197
+ // Observed now, awaited below: a rejection while the scan runs must not
4198
+ // surface as an unhandled one.
4199
+ fanOut.catch(() => undefined);
4200
+ whilePoolBusy?.();
4201
+ inputs = await collectInputs();
4202
+ await fanOut;
4003
4203
  }
4004
4204
  else {
4005
4205
  for (const i of gatedIn) {
@@ -4024,6 +4224,7 @@ backpressure) {
4024
4224
  await yieldToLoop();
4025
4225
  await foldIfOver();
4026
4226
  }
4227
+ queries.replaceSynthesisInputs(inputs ?? await collectInputs());
4027
4228
  __mark('insertMergedEdges');
4028
4229
  return merged.length + goImpl.length + goMethodContains.length;
4029
4230
  }