@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
@@ -0,0 +1,1294 @@
1
+ "use strict";
2
+ /**
3
+ * Cross-tier channels — the web's equivalent of the React Native bridge.
4
+ *
5
+ * A web app is two programs that talk over a wire the graph cannot see: the
6
+ * page calls `fetch('/api/users', { method: 'POST' })` and the API's
7
+ * `app.post('/api/users', createUser)` answers; a service puts `'welcome'` on
8
+ * the `email` queue and a `@Process('welcome')` method picks it up; a
9
+ * gateway's `this.server.emit('message')` lands in the component that wrote
10
+ * `socket.on('message', …)`. Each hop is a string on both sides, which is the
11
+ * evidence that lets a synthesizer close it — exactly as the RN event channel
12
+ * pairs `sendEvent(withName: "x")` with `addListener('x')`.
13
+ *
14
+ * Three channels, one scan:
15
+ *
16
+ * 1. **`http-client`** — a literal path in a client call (`fetch`, `axios.post`,
17
+ * `ky`, `got`, `$fetch`, `useFetch`, `useSWR`, or a project instance made by
18
+ * `axios.create(…)` / `ky.extend(…)`) → the ONE route node `METHOD path` it
19
+ * denotes. Template holes match a `:param`; a hole in front of the path
20
+ * (`${API_URL}/users`) matches a route by its tail; a variable url, a path
21
+ * no route serves, or a path two routes serve alike produce nothing.
22
+ * Edge: enclosing function → route, `tier: 'client→server'`.
23
+ * 2. **`queue-job`** — `queue.add('job', …)` where the queue is named (`new
24
+ * Queue('email')`, `@InjectQueue('email')`) → the `@Process('job')` method
25
+ * of the `@Processor('email')` class, a WorkerHost's `process`, a
26
+ * `new Worker('email', handler)`, or Bull's `queue.process('job', handler)`.
27
+ * 3. **`event-bus`** — `eventEmitter.emit('user.created')` → `@OnEvent('user.created')`
28
+ * (globs honoured); and sockets in both directions: a client's
29
+ * `socket.emit('x')` → the server's `@SubscribeMessage('x')` / `socket.on('x')`
30
+ * (`tier: 'client→server'`), the server's `server.emit('x')` → the client's
31
+ * `socket.on('x', …)` (`tier: 'server→client'`). The in-process
32
+ * `.on('x', fn)` ↔ `.emit('x')` pairing stays the emitter pass's.
33
+ *
34
+ * Every edge is `kind: 'calls'`, `provenance: 'heuristic'`, and carries
35
+ * `synthesizedBy`, `channel` (`http` | `queue` | `event` | `socket`), the
36
+ * `tier` when the direction is known, the `event` / `queue` / `method` /
37
+ * `href` it was paired on, and `registeredAt` — the route registration, the
38
+ * decorator, the `.on` — so a reader can check the pairing. Fan-out is capped
39
+ * per event as the emitter pass caps it; an HTTP pairing needs no cap because
40
+ * it is exact. Test suites and generated files are never sources of these
41
+ * passes: a supertest call is the test's story, and forty of them would make
42
+ * the route a hub. The one exception is section 4, a Spring / Laravel test's
43
+ * request onto its route, whose edges are kept out of fan-in for that reason.
44
+ */
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.TIER_SERVER_TO_CLIENT = exports.TIER_CLIENT_TO_SERVER = void 0;
47
+ exports.hasTestRequestPattern = hasTestRequestPattern;
48
+ exports.testRequestEdges = testRequestEdges;
49
+ exports.hasCrossTierPattern = hasCrossTierPattern;
50
+ exports.crossTierEdges = crossTierEdges;
51
+ const strip_comments_1 = require("./strip-comments");
52
+ const import_resolver_1 = require("./import-resolver");
53
+ const generated_detection_1 = require("../extraction/generated-detection");
54
+ const query_utils_1 = require("../search/query-utils");
55
+ const expo_router_1 = require("./frameworks/expo-router");
56
+ const synth_utils_1 = require("./synth-utils");
57
+ const JS_FILE = /\.(?:[cm]?[jt]sx?)$/;
58
+ /** Events with more handlers or dispatchers than this are too generic to pair without type information. */
59
+ const EVENT_FANOUT_CAP = 6;
60
+ const HTTP_VERBS = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS', 'ALL', 'ANY']);
61
+ exports.TIER_CLIENT_TO_SERVER = 'client→server';
62
+ exports.TIER_SERVER_TO_CLIENT = 'server→client';
63
+ // =============================================================================
64
+ // Source reading
65
+ // =============================================================================
66
+ /** Index just past the `)` that closes the `(` at `open`, skipping strings; -1 if unbalanced. */
67
+ function closeParen(s, open) {
68
+ let depth = 0;
69
+ for (let i = open; i < s.length; i++) {
70
+ const ch = s[i];
71
+ if (ch === '"' || ch === "'") {
72
+ const q = ch;
73
+ i++;
74
+ while (i < s.length && s[i] !== q) {
75
+ if (s[i] === '\\')
76
+ i++;
77
+ i++;
78
+ }
79
+ continue;
80
+ }
81
+ if (ch === '`') {
82
+ i = templateEnd(s, i);
83
+ continue;
84
+ }
85
+ if (ch === '(')
86
+ depth++;
87
+ else if (ch === ')') {
88
+ depth--;
89
+ if (depth === 0)
90
+ return i;
91
+ }
92
+ }
93
+ return -1;
94
+ }
95
+ /** Index of the backtick closing the template opening at `open`. */
96
+ function templateEnd(s, open) {
97
+ let i = open + 1;
98
+ while (i < s.length) {
99
+ const ch = s[i];
100
+ if (ch === '\\') {
101
+ i += 2;
102
+ continue;
103
+ }
104
+ if (ch === '`')
105
+ return i;
106
+ if (ch === '$' && s[i + 1] === '{') {
107
+ let depth = 0;
108
+ for (i = i + 1; i < s.length; i++) {
109
+ if (s[i] === '{')
110
+ depth++;
111
+ else if (s[i] === '}') {
112
+ depth--;
113
+ if (depth === 0)
114
+ break;
115
+ }
116
+ else if (s[i] === '`')
117
+ i = templateEnd(s, i);
118
+ }
119
+ }
120
+ i++;
121
+ }
122
+ return s.length;
123
+ }
124
+ /** The arguments of the call whose `(` is at `open`, split at depth-0 commas. */
125
+ function argumentsAt(s, open) {
126
+ const close = closeParen(s, open);
127
+ if (close < 0)
128
+ return null;
129
+ const inner = s.slice(open + 1, close);
130
+ const out = [];
131
+ let depth = 0;
132
+ let start = 0;
133
+ for (let i = 0; i < inner.length; i++) {
134
+ const c = inner[i];
135
+ if (c === '"' || c === "'") {
136
+ const q = c;
137
+ i++;
138
+ while (i < inner.length && inner[i] !== q) {
139
+ if (inner[i] === '\\')
140
+ i++;
141
+ i++;
142
+ }
143
+ continue;
144
+ }
145
+ if (c === '`') {
146
+ i = templateEnd(inner, i);
147
+ continue;
148
+ }
149
+ if (c === '(' || c === '[' || c === '{')
150
+ depth++;
151
+ else if (c === ')' || c === ']' || c === '}')
152
+ depth--;
153
+ else if (c === ',' && depth === 0) {
154
+ out.push(inner.slice(start, i));
155
+ start = i + 1;
156
+ }
157
+ }
158
+ out.push(inner.slice(start));
159
+ return out.map((a) => a.trim()).filter((a, i) => a.length > 0 || i === 0);
160
+ }
161
+ /** The first string literal in `text`, holes kept; null when it opens with anything else. */
162
+ function leadingString(text) {
163
+ const t = text.trim().replace(/^\(\s*/, '');
164
+ // `new URL('/x', base)` — the path is the first argument of the URL.
165
+ const url = /^new\s+URL\s*\(/.exec(t);
166
+ if (url) {
167
+ const args = argumentsAt(t, url[0].length - 1);
168
+ return args && args[0] ? leadingString(args[0]) : null;
169
+ }
170
+ if (t[0] === '"' || t[0] === "'" || t[0] === '`')
171
+ return (0, expo_router_1.readStringAt)(t, 0);
172
+ return null;
173
+ }
174
+ /** `method: 'POST'` inside an options object / config, upper-cased; null when absent or computed. */
175
+ function methodIn(text) {
176
+ const m = /\bmethod\s*:\s*(['"`])([A-Za-z]+)\1/.exec(text);
177
+ return m ? m[2].toUpperCase() : null;
178
+ }
179
+ /** The first string literal anywhere in a decorator's arguments (`'x'`, `{ name: 'x' }`). */
180
+ function firstLiteral(args) {
181
+ const m = /(['"`])([^'"`]+)\1/.exec(args);
182
+ return m ? m[2] : null;
183
+ }
184
+ /** Every string literal in a decorator's arguments, for `@OnEvent(['a', 'b'])`. */
185
+ function allLiterals(args) {
186
+ const out = [];
187
+ const re = /(['"`])([^'"`]+)\1/g;
188
+ let m;
189
+ while ((m = re.exec(args)) !== null)
190
+ out.push(m[2]);
191
+ return out;
192
+ }
193
+ /**
194
+ * The name of the method a decorator sits on: skip further stacked decorators
195
+ * and modifiers after the decorator's `)`, then take the identifier before `(`.
196
+ */
197
+ function methodNameAfter(safe, from) {
198
+ let i = from;
199
+ const ws = /\s*/y;
200
+ const deco = /@[\w.]+/y;
201
+ const modifier = /(?:public|private|protected|async|static|readonly|override)\b/y;
202
+ const ident = /([A-Za-z_$][\w$]*)\s*[<(]/y;
203
+ const eat = () => {
204
+ ws.lastIndex = i;
205
+ if (ws.exec(safe))
206
+ i = ws.lastIndex;
207
+ };
208
+ for (;;) {
209
+ eat();
210
+ if (safe[i] !== '@')
211
+ break;
212
+ deco.lastIndex = i;
213
+ if (!deco.exec(safe))
214
+ break;
215
+ i = deco.lastIndex;
216
+ eat();
217
+ if (safe[i] === '(') {
218
+ const close = closeParen(safe, i);
219
+ if (close < 0)
220
+ return null;
221
+ i = close + 1;
222
+ }
223
+ }
224
+ for (;;) {
225
+ eat();
226
+ modifier.lastIndex = i;
227
+ if (modifier.exec(safe) && modifier.lastIndex > i) {
228
+ i = modifier.lastIndex;
229
+ continue;
230
+ }
231
+ break;
232
+ }
233
+ eat();
234
+ ident.lastIndex = i;
235
+ const m = ident.exec(safe);
236
+ return m ? { name: m[1], index: i } : null;
237
+ }
238
+ /** Every `@Name(` decorator in `safe` with its arguments and where it ends. */
239
+ function decorators(safe, name) {
240
+ const out = [];
241
+ const re = new RegExp(`@${name}\\s*\\(`, 'g');
242
+ let m;
243
+ while ((m = re.exec(safe)) !== null) {
244
+ const open = m.index + m[0].length - 1;
245
+ const close = closeParen(safe, open);
246
+ if (close < 0)
247
+ continue;
248
+ out.push({ args: safe.slice(open + 1, close), index: m.index, end: close + 1 });
249
+ re.lastIndex = close + 1;
250
+ }
251
+ return out;
252
+ }
253
+ const CLIENT_FACTORY = /\b(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*(?::[^=;]*?)?=\s*(?:await\s+)?(?:(?:axios|ky|got|ofetch|\$fetch|wretch|redaxios)\s*\.\s*(?:create|extend)|new\s+Axios|wretch)\s*\(/g;
254
+ const DEFAULT_CLIENT_FACTORY = /\bexport\s+default\s+(?:(?:axios|ky|got|ofetch|\$fetch|wretch|redaxios)\s*\.\s*(?:create|extend)|new\s+Axios|wretch)\s*\(/;
255
+ const QUEUE_BINDING = /\b(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*(?::[^=;]*?)?=\s*(?:new\s+)?(?:Queue|Bull|BullQueue)\s*(?:<[^>]*>)?\s*\(\s*(['"`])([^'"`]+)\2/g;
256
+ const INJECT_QUEUE = /@InjectQueue\s*\(\s*(['"`])([^'"`]+)\1\s*\)\s*(?:(?:private|public|protected|readonly)\s+)*([A-Za-z_$][\w$]*)/g;
257
+ const SOCKET_SERVER_FILE = /@WebSocketGateway\s*\(|@SubscribeMessage\s*\(|@WebSocketServer\s*\(|\bnew\s+(?:Server|SocketIOServer|WebSocketServer|WebSocket\.Server|WSServer)\b|\.on\s*\(\s*['"]connection['"]/;
258
+ function baseUrlIn(safe, open) {
259
+ const args = argumentsAt(safe, open);
260
+ const config = args?.[0] ?? '';
261
+ const m = /\b(?:baseURL|baseUrl|prefixUrl|baseURI)\s*:\s*/.exec(config);
262
+ if (!m)
263
+ return null;
264
+ return (0, expo_router_1.readStringAt)(config, m.index + m[0].length);
265
+ }
266
+ function readFacts(ctx, file) {
267
+ const content = ctx.readFile(file);
268
+ if (!content)
269
+ return null;
270
+ const safe = (0, strip_comments_1.stripCommentsForRegex)(content, 'typescript');
271
+ let nodes = null;
272
+ let routeLines = null;
273
+ const clients = new Map();
274
+ CLIENT_FACTORY.lastIndex = 0;
275
+ let m;
276
+ while ((m = CLIENT_FACTORY.exec(safe)) !== null) {
277
+ clients.set(m[1], { baseURL: baseUrlIn(safe, m.index + m[0].length - 1) });
278
+ }
279
+ const dm = DEFAULT_CLIENT_FACTORY.exec(safe);
280
+ const defaultClient = dm ? { baseURL: baseUrlIn(safe, dm.index + dm[0].length - 1) } : null;
281
+ const queues = new Map();
282
+ QUEUE_BINDING.lastIndex = 0;
283
+ while ((m = QUEUE_BINDING.exec(safe)) !== null)
284
+ queues.set(m[1], m[3]);
285
+ INJECT_QUEUE.lastIndex = 0;
286
+ while ((m = INJECT_QUEUE.exec(safe)) !== null)
287
+ queues.set(m[3], m[2]);
288
+ return {
289
+ file,
290
+ safe,
291
+ get nodes() {
292
+ return (nodes ??= ctx.getNodesInFile(file));
293
+ },
294
+ lineOf: (0, synth_utils_1.makeLineAt)(safe, 1),
295
+ columnOf: (idx) => idx - (safe.lastIndexOf('\n', idx - 1) + 1),
296
+ get routeLines() {
297
+ if (!routeLines) {
298
+ routeLines = new Set();
299
+ for (const n of this.nodes)
300
+ if (n.kind === 'route')
301
+ routeLines.add(n.startLine);
302
+ }
303
+ return routeLines;
304
+ },
305
+ clients,
306
+ defaultClient,
307
+ queues,
308
+ socketServer: SOCKET_SERVER_FILE.test(safe),
309
+ };
310
+ }
311
+ /** Facts for the file a local name is imported from, when the import resolves to a project file. */
312
+ function importedFacts(ctx, facts, localName, cache) {
313
+ const lang = facts.file.endsWith('x') ? 'tsx' : 'typescript';
314
+ const im = ctx.getImportMappings(facts.file, lang).find((i) => i.localName === localName);
315
+ if (!im)
316
+ return null;
317
+ // The mappings name the module as written; the file it is comes from the
318
+ // same resolution the import resolver uses (aliases, extensions, index files).
319
+ const resolved = im.resolvedPath ?? (0, import_resolver_1.resolveImportPath)(im.source, facts.file, lang, ctx);
320
+ if (!resolved)
321
+ return null;
322
+ let target = cache.get(resolved);
323
+ if (target === undefined) {
324
+ target = JS_FILE.test(resolved) ? readFacts(ctx, resolved) : null;
325
+ cache.set(resolved, target);
326
+ }
327
+ return target ? { facts: target, exportedName: im.exportedName, isDefault: im.isDefault } : null;
328
+ }
329
+ // =============================================================================
330
+ // 1. HTTP client → route
331
+ // =============================================================================
332
+ /** A receiver that is an HTTP client by name alone. */
333
+ const CLIENT_NAMES = /^(?:axios|ky|got|superagent|http|https|httpClient|httpService|api|apiClient|client|restClient|request|agent|fetcher|instance|\$api|\$http|\$axios|axiosInstance|Axios|HttpClient|backend|server)$/;
334
+ /** A receiver that registers routes, never a client — unless it was made by a client factory. */
335
+ const SERVER_NAMES = /^(?:app|router|route|routes|express|fastify|koa|hono|elysia|apiRouter|v1|v2|r)$/;
336
+ /** A type argument between the callee and its `(` — `useSWR<TeamData>('/api/team')`, `ky.get<User>('/x')`. */
337
+ const GENERIC = String.raw `(?:<[^()<>]*(?:<[^()<>]*>[^()<>]*)*>)?`;
338
+ /**
339
+ * Where a receiver chain (`this.api.client`) may begin: not after an earlier
340
+ * identifier-start character of the same word (the second lookbehind is the
341
+ * whole rule; the first is its one-character fast path). A match from the
342
+ * middle of a word implies one from its first letter — the chain regexes read
343
+ * the rest of the word either way — and each of their matches ends on a
344
+ * non-word character, so a scan never resumes mid-word: this only skips starts
345
+ * bound to fail. Without it every letter of every identifier re-read the
346
+ * dotted chain behind it, which on vscode made this the slowest pass.
347
+ */
348
+ const CHAIN_START = String.raw `(?<![A-Za-z_$])(?<![A-Za-z_$][\w$]+)`;
349
+ const BARE_CLIENT_CALL = new RegExp(String.raw `(?:(?:window|globalThis|global)\s*\.\s*)?\b(fetch|\$fetch|ofetch|axios|ky|got|useFetch|useSWR)\s*${GENERIC}\s*\(`, 'g');
350
+ const MEMBER_CLIENT_CALL = new RegExp(String.raw `${CHAIN_START}((?:this\s*\.\s*)?[A-Za-z_$][\w$]*(?:\s*\.\s*[A-Za-z_$][\w$]*)*)\s*\.\s*(get|post|put|patch|delete|head|options|request|\$get|\$post|\$put|\$patch|\$delete)\s*${GENERIC}\s*\(`, 'g');
351
+ function httpRoutes(ctx) {
352
+ const out = [];
353
+ for (const node of ctx.getNodesByKind('route')) {
354
+ const space = node.name.indexOf(' ');
355
+ if (space <= 0)
356
+ continue;
357
+ const method = node.name.slice(0, space).toUpperCase();
358
+ if (!HTTP_VERBS.has(method))
359
+ continue;
360
+ const path = node.name.slice(space + 1).trim();
361
+ if (!path.startsWith('/'))
362
+ continue;
363
+ out.push({ node, method, segs: path.split('/').filter((s) => s.length > 0) });
364
+ }
365
+ return out;
366
+ }
367
+ const PARAM_SEG = /^(?::|\{|\[|<|\*)|\?$/;
368
+ const CATCH_ALL = /^(?:\*|\[\.\.\.|\{\*|:[\w$]+\*$|\{[\w$]+:\*\}|\*[\w$]*$)/;
369
+ /** How well the client's segments match a route's; null when they do not. A literal match beats a parameter's. */
370
+ function scorePath(client, route) {
371
+ let score = 0;
372
+ let i = 0;
373
+ for (let r = 0; r < route.length; r++) {
374
+ const seg = route[r];
375
+ if (CATCH_ALL.test(seg)) {
376
+ if (i >= client.length)
377
+ return null;
378
+ score += client.length - i;
379
+ i = client.length;
380
+ continue;
381
+ }
382
+ if (i >= client.length)
383
+ return null;
384
+ const c = client[i];
385
+ // A hole (`${id}`) fills a route's parameter; it never stands in for a
386
+ // literal segment — `/api/products/${id}` is not `/api/products/top`.
387
+ if (PARAM_SEG.test(seg))
388
+ score += 2;
389
+ else if (c === seg)
390
+ score += 3;
391
+ else
392
+ return null;
393
+ i++;
394
+ }
395
+ return i === client.length ? score : null;
396
+ }
397
+ function matchHttp(site, routes) {
398
+ let best = null;
399
+ let bestScore = -1;
400
+ let tied = false;
401
+ for (const r of routes) {
402
+ if (r.method !== 'ALL' && r.method !== 'ANY' && r.method !== site.method)
403
+ continue;
404
+ let score;
405
+ if (site.suffix) {
406
+ // A base URL hides a prefix the route may spell out (`${API}/users` for
407
+ // `GET /api/users`) — but a one-segment tail names half the routes in
408
+ // an index, and a long hidden prefix is a different API. Two segments
409
+ // of tail at least, two of prefix at most.
410
+ if (site.segs.length < 2 || r.segs.length < site.segs.length || r.segs.length - site.segs.length > 2)
411
+ continue;
412
+ score = scorePath(site.segs, r.segs.slice(r.segs.length - site.segs.length));
413
+ }
414
+ else
415
+ score = scorePath(site.segs, r.segs);
416
+ if (score === null)
417
+ continue;
418
+ if (score > bestScore) {
419
+ best = r;
420
+ bestScore = score;
421
+ tied = false;
422
+ }
423
+ else if (score === bestScore)
424
+ tied = true;
425
+ }
426
+ return tied ? null : best;
427
+ }
428
+ /**
429
+ * The path a client call names, as segments, with `${…}` as `*`; `suffix`
430
+ * when a base URL came first. Null when the path is not literal enough: a
431
+ * relative path with no base, or nothing but holes.
432
+ */
433
+ function clientPath(raw, baseURL) {
434
+ let p = raw;
435
+ const cut = p.search(/[?#]/);
436
+ if (cut >= 0)
437
+ p = p.slice(0, cut);
438
+ let suffix = false;
439
+ const absolute = /^(?:[a-z][a-z0-9+.-]*:)?\/\/[^/]*(\/.*)?$/i.exec(p);
440
+ if (absolute)
441
+ p = absolute[1] ?? '/';
442
+ else if (!p.startsWith('/')) {
443
+ if (p.startsWith(expo_router_1.HOLE)) {
444
+ const rest = p.slice(1);
445
+ if (!rest.startsWith('/'))
446
+ return null;
447
+ p = rest;
448
+ suffix = true;
449
+ }
450
+ else if (baseURL !== null) {
451
+ const base = clientPath(baseURL, null);
452
+ if (!base) {
453
+ // A base that is itself a hole: match by the tail.
454
+ if (!baseURL.includes(expo_router_1.HOLE))
455
+ return null;
456
+ suffix = true;
457
+ p = '/' + p;
458
+ }
459
+ else {
460
+ suffix = base.suffix;
461
+ p = '/' + [...base.segs, ...p.split('/')].filter(Boolean).join('/');
462
+ }
463
+ }
464
+ else
465
+ return null;
466
+ }
467
+ else if (baseURL !== null) {
468
+ // An instance with a literal path base: axios joins `baseURL + url`.
469
+ const base = clientPath(baseURL, null);
470
+ if (base && base.segs.length > 0) {
471
+ suffix = base.suffix;
472
+ p = '/' + [...base.segs, ...p.split('/')].filter(Boolean).join('/');
473
+ }
474
+ else if (!base && baseURL.includes(expo_router_1.HOLE))
475
+ suffix = true;
476
+ }
477
+ const segs = p
478
+ .split('/')
479
+ .filter((s) => s.length > 0)
480
+ .map((s) => (s.includes(expo_router_1.HOLE) ? '*' : s));
481
+ if (segs.length > 0 && segs.every((s) => s === '*'))
482
+ return null;
483
+ return { segs, suffix, display: '/' + segs.map((s) => (s === '*' ? '${…}' : s)).join('/') };
484
+ }
485
+ /** What a member-call receiver is: a client (with its base URL), or nothing. */
486
+ function clientFor(ctx, facts, receiver, cache) {
487
+ const chain = receiver.replace(/\s+/g, '').replace(/^this\./, '').split('.');
488
+ const head = chain[0];
489
+ const last = chain[chain.length - 1];
490
+ const local = facts.clients.get(head);
491
+ if (local)
492
+ return local;
493
+ const imported = importedFacts(ctx, facts, head, cache);
494
+ if (imported) {
495
+ const bound = imported.isDefault ? imported.facts.defaultClient : imported.facts.clients.get(imported.exportedName) ?? null;
496
+ if (bound)
497
+ return bound;
498
+ }
499
+ if (SERVER_NAMES.test(last) || SERVER_NAMES.test(head))
500
+ return null;
501
+ if (CLIENT_NAMES.test(last))
502
+ return { baseURL: null };
503
+ return null;
504
+ }
505
+ function collectHttpSites(ctx, facts, sites, cache) {
506
+ const { safe, lineOf } = facts;
507
+ const add = (index, open, verb, baseURL) => {
508
+ const line = lineOf(index);
509
+ const callee = safe.slice(index, open).replace(/\s+/g, '').replace(/<.*>$/, '');
510
+ if (facts.routeLines.has(line))
511
+ return; // a registration the resolver already read
512
+ const fn = (0, synth_utils_1.enclosingFn)(facts.nodes, line);
513
+ if (!fn)
514
+ return;
515
+ const args = argumentsAt(safe, open);
516
+ if (!args || !args[0])
517
+ return;
518
+ let first = args[0];
519
+ let method = verb;
520
+ // `axios({ url, method })`, `.request({ url, method })`, `ky(url, { method })`.
521
+ if (first.trimStart().startsWith('{')) {
522
+ const url = /\burl\s*:\s*/.exec(first);
523
+ if (!url)
524
+ return;
525
+ method = method ?? methodIn(first) ?? 'GET';
526
+ first = first.slice(url.index + url[0].length);
527
+ }
528
+ else if (method === null) {
529
+ method = methodIn(args.slice(1).join(',')) ?? 'GET';
530
+ }
531
+ const literal = leadingString(first);
532
+ if (literal === null)
533
+ return;
534
+ const path = clientPath(literal, baseURL);
535
+ if (!path)
536
+ return;
537
+ sites.push({ fn, file: facts.file, line, column: facts.columnOf(index), callee, method, segs: path.segs, suffix: path.suffix, display: path.display });
538
+ };
539
+ BARE_CLIENT_CALL.lastIndex = 0;
540
+ let m;
541
+ while ((m = BARE_CLIENT_CALL.exec(safe)) !== null) {
542
+ // `this.fetch(…)` / `repo.fetch(…)` is a project method, not the platform's.
543
+ const before = safe[m.index - 1];
544
+ if (before === '.' && !/^(?:window|globalThis|global)\s*\./.test(m[0]))
545
+ continue;
546
+ add(m.index, m.index + m[0].length - 1, null, null);
547
+ }
548
+ MEMBER_CLIENT_CALL.lastIndex = 0;
549
+ while ((m = MEMBER_CLIENT_CALL.exec(safe)) !== null) {
550
+ const client = clientFor(ctx, facts, m[1], cache);
551
+ if (!client)
552
+ continue;
553
+ const verb = m[2].replace(/^\$/, '').toUpperCase();
554
+ add(m.index, m.index + m[0].length - 1, verb === 'REQUEST' ? null : verb, client.baseURL);
555
+ }
556
+ }
557
+ // =============================================================================
558
+ // 2. Queue job → consumer
559
+ // =============================================================================
560
+ const QUEUE_ADD = new RegExp(CHAIN_START + /((?:this\s*\.\s*)?[A-Za-z_$][\w$]*(?:\s*\.\s*[A-Za-z_$][\w$]*)*)\s*\.\s*add\s*\(\s*(['"`])([^'"`]+)\2/.source, 'g');
561
+ const QUEUE_SHAPED = /queue|jobs?$|worker|bull|flow|producer/i;
562
+ const NEW_WORKER = /\bnew\s+Worker\s*(?:<[^>]*>)?\s*\(\s*(['"`])([^'"`]+)\1\s*,\s*/g;
563
+ const QUEUE_PROCESS = new RegExp(CHAIN_START + /((?:this\s*\.\s*)?[A-Za-z_$][\w$]*(?:\s*\.\s*[A-Za-z_$][\w$]*)*)\s*\.\s*process\s*\(\s*(?:(['"`])([^'"`]+)\2\s*,\s*)?(?:\d+\s*,\s*)?/.source, 'g');
564
+ /** A handler argument: a named function (group 1), or an inline function. */
565
+ const HANDLER_ARG = /^(?:(?:async\s+)?([A-Za-z_$][\w$.]*)\s*(?:[,)]|$)|(?:async\s*)?(?:\(|function\b|[A-Za-z_$][\w$]*\s*=>))/;
566
+ /** The queue a receiver is bound to, by its binding in this file or the file it is imported from. */
567
+ function queueFor(ctx, facts, receiver, cache) {
568
+ const chain = receiver.replace(/\s+/g, '').replace(/^this\./, '').split('.');
569
+ const head = chain[0];
570
+ const own = facts.queues.get(head);
571
+ if (own)
572
+ return own;
573
+ const imported = importedFacts(ctx, facts, head, cache);
574
+ if (imported) {
575
+ const bound = imported.facts.queues.get(imported.exportedName);
576
+ if (bound)
577
+ return bound;
578
+ }
579
+ return null;
580
+ }
581
+ /** The function a handler argument names or encloses; null when it is neither. */
582
+ function handlerNode(ctx, facts, text, line, cache) {
583
+ const m = HANDLER_ARG.exec(text.trimStart());
584
+ if (!m)
585
+ return null;
586
+ if (m[1]) {
587
+ const name = m[1].split('.').pop();
588
+ const candidates = ctx.getNodesByName(name).filter((n) => n.kind === 'function' || n.kind === 'method');
589
+ const local = candidates.filter((n) => n.filePath === facts.file);
590
+ if (local.length === 1)
591
+ return local[0];
592
+ if (local.length > 1)
593
+ return null;
594
+ const imported = importedFacts(ctx, facts, m[1].split('.')[0], cache);
595
+ if (imported) {
596
+ const viaImport = candidates.filter((n) => n.filePath === imported.facts.file);
597
+ if (viaImport.length === 1)
598
+ return viaImport[0];
599
+ }
600
+ return candidates.length === 1 ? candidates[0] : null;
601
+ }
602
+ return (0, synth_utils_1.enclosingFn)(facts.nodes, line) ?? (0, synth_utils_1.enclosingValue)(facts.nodes, line);
603
+ }
604
+ /** The nearest class declared at or after `index`, as its node. */
605
+ function classAfter(facts, index) {
606
+ const m = /\bclass\s+([A-Za-z_$][\w$]*)/g;
607
+ m.lastIndex = index;
608
+ const hit = m.exec(facts.safe);
609
+ if (!hit)
610
+ return null;
611
+ const line = facts.lineOf(hit.index);
612
+ return facts.nodes.find((n) => n.kind === 'class' && n.name === hit[1] && n.startLine >= line - 2) ?? null;
613
+ }
614
+ /** The method node a decorator at `end` sits on, inside `cls` when given. */
615
+ function decoratedMethod(facts, end, cls) {
616
+ const named = methodNameAfter(facts.safe, end);
617
+ if (!named)
618
+ return null;
619
+ const line = facts.lineOf(named.index);
620
+ return (facts.nodes.find((n) => (n.kind === 'method' || n.kind === 'function') &&
621
+ n.name === named.name &&
622
+ n.startLine >= line - 1 &&
623
+ n.startLine <= line + 1 &&
624
+ (!cls || (n.startLine >= cls.startLine && n.endLine <= cls.endLine))) ?? null);
625
+ }
626
+ function collectQueue(ctx, facts, producers, consumers, cache) {
627
+ const { safe, lineOf } = facts;
628
+ let m;
629
+ QUEUE_ADD.lastIndex = 0;
630
+ while ((m = QUEUE_ADD.exec(safe)) !== null) {
631
+ const receiver = m[1];
632
+ const queue = queueFor(ctx, facts, receiver, cache);
633
+ const last = receiver.replace(/\s+/g, '').split('.').pop();
634
+ if (queue === null && !QUEUE_SHAPED.test(last))
635
+ continue;
636
+ const line = lineOf(m.index);
637
+ const fn = (0, synth_utils_1.enclosingFn)(facts.nodes, line);
638
+ if (!fn)
639
+ continue;
640
+ producers.push({ fn, file: facts.file, line, column: facts.columnOf(m.index), callee: `${receiver.replace(/\s+/g, '')}.add`, queue, job: m[3] });
641
+ }
642
+ // Nest: `@Processor('email')` on a class; `@Process('welcome')` on its methods,
643
+ // or a WorkerHost's `process(job)`.
644
+ for (const proc of decorators(safe, 'Processor')) {
645
+ const queue = firstLiteral(proc.args);
646
+ const cls = classAfter(facts, proc.end);
647
+ if (!cls)
648
+ continue;
649
+ let any = false;
650
+ for (const job of decorators(safe, 'Process')) {
651
+ const line = lineOf(job.index);
652
+ if (line < cls.startLine || line > cls.endLine)
653
+ continue;
654
+ const method = decoratedMethod(facts, job.end, cls);
655
+ if (!method)
656
+ continue;
657
+ any = true;
658
+ consumers.push({ node: method, file: facts.file, line, queue, job: firstLiteral(job.args) });
659
+ }
660
+ if (!any) {
661
+ const process = facts.nodes.find((n) => n.kind === 'method' && n.name === 'process' && n.startLine >= cls.startLine && n.endLine <= cls.endLine);
662
+ if (process)
663
+ consumers.push({ node: process, file: facts.file, line: process.startLine, queue, job: null });
664
+ }
665
+ }
666
+ // BullMQ: `new Worker('email', handler)`.
667
+ NEW_WORKER.lastIndex = 0;
668
+ while ((m = NEW_WORKER.exec(safe)) !== null) {
669
+ const line = lineOf(m.index);
670
+ const node = handlerNode(ctx, facts, safe.slice(m.index + m[0].length, m.index + m[0].length + 200), line, cache);
671
+ if (!node)
672
+ continue;
673
+ consumers.push({ node, file: facts.file, line, queue: m[2], job: null });
674
+ }
675
+ // Bull: `queue.process('welcome', handler)` / `queue.process(handler)`.
676
+ QUEUE_PROCESS.lastIndex = 0;
677
+ while ((m = QUEUE_PROCESS.exec(safe)) !== null) {
678
+ const receiver = m[1];
679
+ const queue = queueFor(ctx, facts, receiver, cache);
680
+ const last = receiver.replace(/\s+/g, '').split('.').pop();
681
+ if (queue === null && !QUEUE_SHAPED.test(last))
682
+ continue;
683
+ const line = lineOf(m.index);
684
+ const node = handlerNode(ctx, facts, safe.slice(m.index + m[0].length, m.index + m[0].length + 200), line, cache);
685
+ if (!node)
686
+ continue;
687
+ consumers.push({ node, file: facts.file, line, queue, job: m[3] ?? null });
688
+ }
689
+ }
690
+ function pairQueue(producers, consumers, edges, seen) {
691
+ for (const p of producers) {
692
+ let candidates = consumers.filter((c) => (p.queue === null || c.queue === null || c.queue === p.queue) && (c.job === null || c.job === p.job));
693
+ // The most specific pairing wins: the job by name on the named queue,
694
+ // then the job by name, then the queue's default consumer.
695
+ const exact = candidates.filter((c) => c.job === p.job && c.queue === p.queue && p.queue !== null);
696
+ if (exact.length > 0)
697
+ candidates = exact;
698
+ else {
699
+ const byJob = candidates.filter((c) => c.job === p.job);
700
+ if (byJob.length > 0)
701
+ candidates = byJob;
702
+ else if (p.queue === null)
703
+ continue; // an unnamed queue and no consumer naming the job: a guess
704
+ else
705
+ candidates = candidates.filter((c) => c.queue === p.queue);
706
+ }
707
+ if (candidates.length === 0 || candidates.length > EVENT_FANOUT_CAP)
708
+ continue;
709
+ for (const c of candidates) {
710
+ if (c.node.id === p.fn.id)
711
+ continue;
712
+ const key = `${p.fn.id}>${c.node.id}`;
713
+ if (seen.has(key))
714
+ continue;
715
+ seen.add(key);
716
+ edges.push({
717
+ source: p.fn.id,
718
+ target: c.node.id,
719
+ kind: 'calls',
720
+ line: p.line,
721
+ column: p.column,
722
+ provenance: 'heuristic',
723
+ metadata: {
724
+ synthesizedBy: 'queue-job',
725
+ channel: 'queue',
726
+ callee: p.callee,
727
+ event: p.job,
728
+ ...(p.queue ?? c.queue ? { queue: p.queue ?? c.queue } : {}),
729
+ registeredAt: `${c.file}:${c.line}`,
730
+ },
731
+ });
732
+ }
733
+ }
734
+ }
735
+ // =============================================================================
736
+ // 3. Events: a bus, and sockets both ways
737
+ // =============================================================================
738
+ // A word character opens these chains, so a word's first character is the
739
+ // only start that can match (same argument as CHAIN_START).
740
+ const EMIT = /(?<![\w$])((?:[\w$]+(?:\([^()]*\))?\s*\.\s*)*[\w$]+)\s*\.\s*(emit|emitAsync)\s*\(\s*(['"`])([^'"`\n]+)\3/g;
741
+ const SOCKET_ON = /(?<![\w$])((?:[\w$]+(?:\([^()]*\))?\s*\.\s*)*[\w$]+)\s*\.\s*(?:on|once)\s*\(\s*(['"`])([^'"`\n]+)\2\s*,\s*/g;
742
+ const SOCKET_WORDS = /^(?:socket|io|ws|wss|client|server|namespace|nsp|conn|connection|gateway|broadcast|to|in|of|except|volatile|local|sockets|socketServer|wsServer|room|channel|pusher|ably|ioClient|socketClient|sock)$/;
743
+ const BUS_WORDS = /^(?:eventEmitter|emitter|events|eventBus|bus|dispatcher|pubsub|publisher|eventPublisher|ee|hub|mediator|broker|messageBus|appEvents|domainEvents|eventsService|eventService)$/;
744
+ /** The transport's own events — every socket emits and handles them; pairing them says nothing. */
745
+ const GENERIC_EVENT = /^(?:error|connect|connect_error|connect_failed|connection|disconnect|disconnecting|reconnect|reconnect_attempt|reconnecting|reconnect_error|reconnect_failed|close|open|end|data|ready|drain|finish|pipe|unpipe|listening|timeout|ping|pong|upgrade|newListener|removeListener)$/;
746
+ function shapeOf(receiver) {
747
+ const segs = receiver.replace(/\([^()]*\)/g, '').replace(/\s+/g, '').split('.').filter((s) => s !== 'this');
748
+ if (segs.some((s) => SOCKET_WORDS.test(s)))
749
+ return 'socket';
750
+ if (segs.some((s) => BUS_WORDS.test(s)))
751
+ return 'bus';
752
+ return null;
753
+ }
754
+ function collectEvents(ctx, facts, dispatches, handlers, cache) {
755
+ const { safe, lineOf } = facts;
756
+ const side = facts.socketServer ? 'server' : 'client';
757
+ let m;
758
+ EMIT.lastIndex = 0;
759
+ while ((m = EMIT.exec(safe)) !== null) {
760
+ const shape = shapeOf(m[1]);
761
+ if (!shape || GENERIC_EVENT.test(m[4]))
762
+ continue;
763
+ const line = lineOf(m.index);
764
+ const fn = (0, synth_utils_1.enclosingFn)(facts.nodes, line);
765
+ if (!fn)
766
+ continue;
767
+ dispatches.push({ fn, file: facts.file, line, column: facts.columnOf(m.index), callee: `${m[1].replace(/\s+/g, '')}.${m[2]}`, event: m[4], shape, side });
768
+ }
769
+ for (const d of decorators(safe, 'OnEvent')) {
770
+ const method = decoratedMethod(facts, d.end, null);
771
+ if (!method)
772
+ continue;
773
+ const patterns = d.args.trimStart().startsWith('[') ? allLiterals(d.args) : [firstLiteral(d.args)].filter((x) => x !== null);
774
+ for (const pattern of patterns)
775
+ handlers.push({ node: method, file: facts.file, line: lineOf(d.index), pattern, kind: 'bus', side });
776
+ }
777
+ for (const d of decorators(safe, 'SubscribeMessage')) {
778
+ const method = decoratedMethod(facts, d.end, null);
779
+ const event = firstLiteral(d.args);
780
+ if (!method || event === null)
781
+ continue;
782
+ handlers.push({ node: method, file: facts.file, line: lineOf(d.index), pattern: event, kind: 'socket', side: 'server' });
783
+ }
784
+ SOCKET_ON.lastIndex = 0;
785
+ while ((m = SOCKET_ON.exec(safe)) !== null) {
786
+ if (shapeOf(m[1]) !== 'socket' || GENERIC_EVENT.test(m[3]))
787
+ continue;
788
+ const line = lineOf(m.index);
789
+ const node = handlerNode(ctx, facts, safe.slice(m.index + m[0].length, m.index + m[0].length + 200), line, cache);
790
+ if (!node)
791
+ continue;
792
+ handlers.push({ node, file: facts.file, line, pattern: m[3], kind: 'socket', side });
793
+ }
794
+ }
795
+ /** `user.*` matches one segment, `**` any; anything else is exact. */
796
+ function eventMatches(pattern, event) {
797
+ if (pattern === event)
798
+ return true;
799
+ if (!pattern.includes('*'))
800
+ return false;
801
+ const re = new RegExp('^' + pattern.split('**').map((part) => part.split('*').map(escapeRe).join('[^.]+')).join('.*') + '$');
802
+ return re.test(event);
803
+ }
804
+ function escapeRe(s) {
805
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
806
+ }
807
+ function pairEvents(dispatches, handlers, edges, seen) {
808
+ // Fan-out is judged per event name on each side, as the emitter pass does.
809
+ const dispatchesByEvent = new Map();
810
+ for (const d of dispatches)
811
+ dispatchesByEvent.set(`${d.shape}:${d.event}`, [...(dispatchesByEvent.get(`${d.shape}:${d.event}`) ?? []), d]);
812
+ for (const [, group] of dispatchesByEvent) {
813
+ if (group.length > EVENT_FANOUT_CAP)
814
+ continue;
815
+ for (const d of group) {
816
+ let matched;
817
+ let tier = null;
818
+ if (d.shape === 'bus')
819
+ matched = handlers.filter((h) => h.kind === 'bus' && eventMatches(h.pattern, d.event));
820
+ else if (d.side === 'client') {
821
+ matched = handlers.filter((h) => h.kind === 'socket' && h.side === 'server' && h.pattern === d.event);
822
+ tier = exports.TIER_CLIENT_TO_SERVER;
823
+ }
824
+ else {
825
+ matched = handlers.filter((h) => h.kind === 'socket' && h.side === 'client' && h.pattern === d.event);
826
+ tier = exports.TIER_SERVER_TO_CLIENT;
827
+ }
828
+ if (matched.length === 0 || matched.length > EVENT_FANOUT_CAP)
829
+ continue;
830
+ for (const h of matched) {
831
+ if (h.node.id === d.fn.id)
832
+ continue;
833
+ const key = `${d.fn.id}>${h.node.id}`;
834
+ if (seen.has(key))
835
+ continue;
836
+ seen.add(key);
837
+ edges.push({
838
+ source: d.fn.id,
839
+ target: h.node.id,
840
+ kind: 'calls',
841
+ line: d.line,
842
+ column: d.column,
843
+ provenance: 'heuristic',
844
+ metadata: {
845
+ synthesizedBy: 'event-bus',
846
+ channel: d.shape === 'bus' ? 'event' : 'socket',
847
+ callee: d.callee,
848
+ event: d.event,
849
+ ...(tier ? { tier } : {}),
850
+ registeredAt: `${h.file}:${h.line}`,
851
+ },
852
+ });
853
+ }
854
+ }
855
+ }
856
+ }
857
+ // =============================================================================
858
+ // The pass
859
+ // =============================================================================
860
+ // =============================================================================
861
+ // 4. Test request → route
862
+ // =============================================================================
863
+ /*
864
+ * Spring and Laravel tests reach a controller by URL, never by a call:
865
+ * MockMvc's `perform(post("/owners/new"))`, WebTestClient's `.get().uri(…)`,
866
+ * TestRestTemplate, RestAssured, and Laravel's `$this->postJson('api/me')`.
867
+ * Without an edge every controller those suites exercise reads as untested —
868
+ * the viewer's "No test reaches this" and explore's `tests:` line both walk
869
+ * callers. One edge per (test, route), from the test method (the file, for a
870
+ * Pest closure) to the route; the route's own edge reaches the handler.
871
+ *
872
+ * These are the one kind of edge whose source is a test. They don't count
873
+ * toward a route's fan-in (countIncomingEdges), so a well-tested endpoint
874
+ * never becomes a hub the Steps walk refuses to enter.
875
+ */
876
+ const TEST_REQUEST_FILE = /\.(?:java|kt|kts|php)$/;
877
+ const TEST_VERBS = 'get|post|put|patch|delete|head|options';
878
+ /** A bare `get("/x")` is MockMvc's only where the file imports its builders. */
879
+ const MOCKMVC_BUILDERS = /\bMockMvcRequestBuilders\b/;
880
+ const MOCKMVC_BUILDER = new RegExp(String.raw `(?<![\w$.])(?:MockMvcRequestBuilders\s*\.\s*)?(${TEST_VERBS}|multipart)\s*\(`, 'g');
881
+ const MOCKMVC_REQUEST = /(?<![\w$.])(?:MockMvcRequestBuilders\s*\.\s*)?request\s*\(\s*HttpMethod\s*\.\s*([A-Z]+)\s*,/g;
882
+ const KOTLIN_MOCKMVC = new RegExp(String.raw `\b\w*[mM]ockMvc\s*\.\s*(${TEST_VERBS})\s*\(`, 'g');
883
+ const WEB_TEST_CLIENT = new RegExp(String.raw `\.\s*(${TEST_VERBS})\s*\(\s*\)\s*\.\s*uri\s*\(`, 'g');
884
+ const REST_TEMPLATE = /\b\w*[Rr]estTemplate\s*\.\s*(getForEntity|getForObject|postForEntity|postForObject|postForLocation|put|delete|patchForObject|exchange)\s*\(/g;
885
+ const REST_ASSURED = new RegExp(String.raw `(?:\.\s*when\s*\(\s*\)\s*|\bRestAssured\s*)\.\s*(${TEST_VERBS})\s*\(`, 'g');
886
+ const LARAVEL_REQUEST = new RegExp(String.raw `->\s*(${TEST_VERBS})(Json)?\s*\(`, 'g');
887
+ const LARAVEL_JSON_CALL = /->\s*(?:json|call)\s*\(\s*(['"])([A-Za-z]+)\1\s*,/g;
888
+ /** A project's own request helper, named for its verb: koel's `getAs($url, $user)`, `postAsAdmin(…)`. */
889
+ const LARAVEL_HELPER = new RegExp(String.raw `->\s*((${TEST_VERBS})[A-Z]\w*)\s*\(`, 'g');
890
+ const LARAVEL_BUILTIN_JSON = new RegExp(String.raw `^(?:${TEST_VERBS})Json$`);
891
+ /** A Laravel request made in a method body: the call a request helper must end in. */
892
+ const LARAVEL_REQUEST_IN_BODY = new RegExp(String.raw `->\s*(?:(?:${TEST_VERBS})(?:Json)?|json|call)\s*\(`);
893
+ /** Pest's `get('/x')` / `postJson(…)` helpers, where the file imports them. */
894
+ const PEST_HELPERS = /Pest\\Laravel/;
895
+ const PEST_REQUEST = new RegExp(String.raw `(?<![\w$>:\\])(${TEST_VERBS})(Json)?\s*\(`, 'g');
896
+ const TEST_REQUEST_GATE = /[mM]ockMvc|[wW]ebTestClient\b|[rR]estTemplate\b|\bRestAssured\b|\.\s*when\s*\(\s*\)|\$this\s*->|Pest\\Laravel/;
897
+ function hasTestRequestPattern(filePath, content) {
898
+ return TEST_REQUEST_FILE.test(filePath) && (0, query_utils_1.isTestPath)(filePath) && TEST_REQUEST_GATE.test(content);
899
+ }
900
+ /**
901
+ * The string a request's path argument builds, starting at `i`: literals
902
+ * verbatim, each computed piece (a concatenated operand, an interpolation, a
903
+ * Spring `{var}` template) as one HOLE. Null unless it starts with a literal —
904
+ * `route('songs.index')` or `$url` names no path this pass can read.
905
+ */
906
+ function readRequestPath(s, i, lang) {
907
+ const concat = lang === 'php' ? '.' : '+';
908
+ let out = '';
909
+ let literal = false;
910
+ for (;;) {
911
+ while (i < s.length && /\s/.test(s[i]))
912
+ i++;
913
+ const ch = s[i];
914
+ if (ch === '"' || ch === "'") {
915
+ const q = ch;
916
+ const interpolates = q === '"' && lang !== 'java';
917
+ for (i++; i < s.length && s[i] !== q; i++) {
918
+ const c = s[i];
919
+ if (c === '\\') {
920
+ out += s[++i] ?? '';
921
+ continue;
922
+ }
923
+ if (interpolates && c === '$' && /[{\w]/.test(s[i + 1] ?? '')) {
924
+ // `${expr}`, `$name`, PHP's `$user->id` / `$row['k']`
925
+ if (s[i + 1] === '{')
926
+ i = closeBrace(s, i + 1);
927
+ else {
928
+ while (/\w/.test(s[i + 1] ?? ''))
929
+ i++;
930
+ if (lang === 'php') {
931
+ while (s.startsWith('->', i + 1) && /\w/.test(s[i + 3] ?? ''))
932
+ for (i += 2; /\w/.test(s[i + 1] ?? ''); i++)
933
+ ;
934
+ }
935
+ }
936
+ out += expo_router_1.HOLE;
937
+ continue;
938
+ }
939
+ if (interpolates && lang === 'php' && c === '{' && s[i + 1] === '$') {
940
+ i = closeBrace(s, i);
941
+ out += expo_router_1.HOLE;
942
+ continue;
943
+ }
944
+ out += c;
945
+ }
946
+ if (i >= s.length)
947
+ return null;
948
+ i++;
949
+ literal = true;
950
+ }
951
+ else {
952
+ if (!literal)
953
+ return null;
954
+ const end = operandEnd(s, i, concat);
955
+ if (end === i)
956
+ break;
957
+ out += expo_router_1.HOLE;
958
+ i = end;
959
+ }
960
+ while (i < s.length && /\s/.test(s[i]))
961
+ i++;
962
+ if (s[i] === concat && !(concat === '.' && /\d/.test(s[i + 1] ?? ''))) {
963
+ i++;
964
+ continue;
965
+ }
966
+ break;
967
+ }
968
+ if (!literal)
969
+ return null;
970
+ // Spring's URI templates: `get("/owners/{ownerId}", id)`.
971
+ return lang === 'php' ? out : out.replace(/\{[^{}/]*\}/g, expo_router_1.HOLE);
972
+ }
973
+ /** Index of the `}` closing the `{` at `open` (or the end of `s`). */
974
+ function closeBrace(s, open) {
975
+ let depth = 0;
976
+ for (let i = open; i < s.length; i++) {
977
+ if (s[i] === '{')
978
+ depth++;
979
+ else if (s[i] === '}' && --depth === 0)
980
+ return i;
981
+ }
982
+ return s.length - 1;
983
+ }
984
+ /** End of a concatenated operand starting at `i`: the next top-level operator, `,` or `)`. */
985
+ function operandEnd(s, i, concat) {
986
+ let depth = 0;
987
+ for (; i < s.length; i++) {
988
+ const c = s[i];
989
+ if (c === '"' || c === "'") {
990
+ for (i++; i < s.length && s[i] !== c; i++)
991
+ if (s[i] === '\\')
992
+ i++;
993
+ continue;
994
+ }
995
+ if (c === '(' || c === '[')
996
+ depth++;
997
+ else if (c === ')' || c === ']') {
998
+ if (depth === 0)
999
+ return i;
1000
+ depth--;
1001
+ }
1002
+ else if (depth === 0 && (c === ',' || c === ';' || (c === concat && !(concat === '.' && /\d/.test(s[i + 1] ?? '')))))
1003
+ return i;
1004
+ // `$user->id`: an arrow is part of the operand, not a concatenation.
1005
+ if (c === '-' && s[i + 1] === '>')
1006
+ i++;
1007
+ }
1008
+ return i;
1009
+ }
1010
+ /** A test URL as route segments: app-relative, holes as `*`; null when nothing literal is left. */
1011
+ function testRequestPath(raw) {
1012
+ let p = raw;
1013
+ const cut = p.search(/[?#]/);
1014
+ if (cut >= 0)
1015
+ p = p.slice(0, cut);
1016
+ const absolute = /^(?:[a-z][a-z0-9+.-]*:)?\/\/[^/]*(\/.*)?$/i.exec(p);
1017
+ if (absolute)
1018
+ p = absolute[1] ?? '/';
1019
+ const segs = p
1020
+ .split('/')
1021
+ .filter((s) => s.length > 0)
1022
+ .map((s) => (s.includes(expo_router_1.HOLE) ? '*' : s));
1023
+ if (segs.some((s) => s !== '*') === false && segs.length > 0)
1024
+ return null;
1025
+ return { segs, display: '/' + segs.map((s) => (s === '*' ? '${…}' : s)).join('/') };
1026
+ }
1027
+ /**
1028
+ * Whether `name` is a request helper in the project's tests: a PHP method
1029
+ * defined in a test file whose body makes a Laravel request, directly or
1030
+ * through one more `$this->helper(…)` (koel's `getAs` → `jsonAs` →
1031
+ * `$this->json($method, $uri)`). The name alone never decides it.
1032
+ */
1033
+ function isRequestHelper(ctx, name, memo, depth = 0) {
1034
+ const known = memo.get(name);
1035
+ if (known !== undefined)
1036
+ return known;
1037
+ memo.set(name, false); // cycle guard
1038
+ let ok = false;
1039
+ for (const n of ctx.getNodesByName(name)) {
1040
+ if (n.kind !== 'method' || n.language !== 'php' || !(0, query_utils_1.isTestPath)(n.filePath))
1041
+ continue;
1042
+ const lines = ctx.getFileLines?.(n.filePath) ?? ctx.readFile(n.filePath)?.split(/\r?\n/) ?? null;
1043
+ if (!lines)
1044
+ continue;
1045
+ const body = lines.slice(n.startLine - 1, n.endLine).join('\n');
1046
+ if (LARAVEL_REQUEST_IN_BODY.test(body))
1047
+ ok = true;
1048
+ else if (depth === 0) {
1049
+ for (const call of body.matchAll(/\$this\s*->\s*(\w+)\s*\(/g)) {
1050
+ if (call[1] !== name && isRequestHelper(ctx, call[1], memo, depth + 1)) {
1051
+ ok = true;
1052
+ break;
1053
+ }
1054
+ }
1055
+ }
1056
+ if (ok)
1057
+ break;
1058
+ }
1059
+ // A depth-limited "no" is not final; only remember it at the top level.
1060
+ if (ok || depth === 0)
1061
+ memo.set(name, ok);
1062
+ else
1063
+ memo.delete(name);
1064
+ return ok;
1065
+ }
1066
+ /**
1067
+ * Whether the `->` at `arrow` ends a chain rooted at `$this` that only CALLS
1068
+ * methods — `$this->actingAs($u)->withHeaders([…])->` — so `$this->app->get(
1069
+ * 'config')` (a container lookup through a property) is never a request.
1070
+ */
1071
+ function thisRootedCalls(s, arrow) {
1072
+ let from = arrow;
1073
+ for (let tries = 0; tries < 4; tries++) {
1074
+ const root = s.lastIndexOf('$this', from - 1);
1075
+ if (root < 0)
1076
+ return false;
1077
+ let i = root + '$this'.length;
1078
+ for (;;) {
1079
+ while (/\s/.test(s[i] ?? ''))
1080
+ i++;
1081
+ if (i === arrow)
1082
+ return true;
1083
+ if (!s.startsWith('->', i))
1084
+ break;
1085
+ i += 2;
1086
+ while (/\s/.test(s[i] ?? ''))
1087
+ i++;
1088
+ const ident = /^\w+/.exec(s.slice(i, i + 64));
1089
+ if (!ident)
1090
+ break;
1091
+ i += ident[0].length;
1092
+ while (/\s/.test(s[i] ?? ''))
1093
+ i++;
1094
+ if (s[i] !== '(')
1095
+ break;
1096
+ const close = closeParen(s, i);
1097
+ if (close < 0)
1098
+ break;
1099
+ i = close + 1;
1100
+ }
1101
+ from = root;
1102
+ }
1103
+ return false;
1104
+ }
1105
+ function collectTestRequests(ctx, file, sites, helpers) {
1106
+ const content = ctx.readFile(file);
1107
+ if (!content || !TEST_REQUEST_GATE.test(content))
1108
+ return;
1109
+ const lang = file.endsWith('.php') ? 'php' : /\.kts?$/.test(file) ? 'kotlin' : 'java';
1110
+ const safe = (0, strip_comments_1.stripCommentsForRegex)(content, lang === 'php' ? 'php' : 'java');
1111
+ const lineOf = (0, synth_utils_1.makeLineAt)(safe, 1);
1112
+ let nodes = null;
1113
+ const add = (index, argAt, method, callee) => {
1114
+ const raw = readRequestPath(safe, argAt, lang);
1115
+ if (raw === null)
1116
+ return;
1117
+ const path = testRequestPath(raw);
1118
+ if (!path)
1119
+ return;
1120
+ const line = lineOf(index);
1121
+ nodes ??= ctx.getNodesInFile(file);
1122
+ const from = (0, synth_utils_1.enclosingFn)(nodes, line) ?? nodes.find((n) => n.kind === 'file');
1123
+ if (!from)
1124
+ return;
1125
+ const column = index - (safe.lastIndexOf('\n', index - 1) + 1);
1126
+ sites.push({ from, line, column, callee, method: method.toUpperCase(), segs: path.segs, display: path.display });
1127
+ };
1128
+ const each = (re, fn) => {
1129
+ re.lastIndex = 0;
1130
+ let m;
1131
+ while ((m = re.exec(safe)) !== null)
1132
+ fn(m);
1133
+ };
1134
+ const openOf = (m) => m.index + m[0].length;
1135
+ if (lang === 'php') {
1136
+ each(LARAVEL_REQUEST, (m) => {
1137
+ if (thisRootedCalls(safe, m.index))
1138
+ add(m.index, openOf(m), m[1], `$this->${m[1]}${m[2] ?? ''}`);
1139
+ });
1140
+ each(LARAVEL_JSON_CALL, (m) => {
1141
+ if (thisRootedCalls(safe, m.index))
1142
+ add(m.index, openOf(m), m[2], '$this->json');
1143
+ });
1144
+ each(LARAVEL_HELPER, (m) => {
1145
+ const name = m[1];
1146
+ if (LARAVEL_BUILTIN_JSON.test(name))
1147
+ return; // getJson / postJson, read above
1148
+ if (thisRootedCalls(safe, m.index) && isRequestHelper(ctx, name, helpers))
1149
+ add(m.index, openOf(m), m[2], `$this->${name}`);
1150
+ });
1151
+ if (PEST_HELPERS.test(safe))
1152
+ each(PEST_REQUEST, (m) => add(m.index, openOf(m), m[1], `${m[1]}${m[2] ?? ''}`));
1153
+ return;
1154
+ }
1155
+ if (MOCKMVC_BUILDERS.test(safe)) {
1156
+ each(MOCKMVC_BUILDER, (m) => add(m.index, openOf(m), m[1] === 'multipart' ? 'post' : m[1], m[1]));
1157
+ each(MOCKMVC_REQUEST, (m) => add(m.index, openOf(m), m[1], 'request'));
1158
+ }
1159
+ each(KOTLIN_MOCKMVC, (m) => add(m.index, openOf(m), m[1], `mockMvc.${m[1]}`));
1160
+ each(WEB_TEST_CLIENT, (m) => add(m.index, openOf(m), m[1], `${m[1]}().uri`));
1161
+ each(REST_ASSURED, (m) => add(m.index, openOf(m), m[1], m[1]));
1162
+ each(REST_TEMPLATE, (m) => {
1163
+ const name = m[1];
1164
+ let method = name.startsWith('get') ? 'GET' : name.startsWith('post') ? 'POST'
1165
+ : name === 'put' ? 'PUT' : name === 'delete' ? 'DELETE' : name.startsWith('patch') ? 'PATCH' : null;
1166
+ if (name === 'exchange') {
1167
+ const args = argumentsAt(safe, openOf(m) - 1);
1168
+ method = /HttpMethod\s*\.\s*([A-Z]+)/.exec(args?.[1] ?? '')?.[1] ?? null;
1169
+ }
1170
+ if (method)
1171
+ add(m.index, openOf(m), method, `restTemplate.${name}`);
1172
+ });
1173
+ }
1174
+ /** Edges from a test's request to the route it reaches. */
1175
+ async function testRequestEdges(ctx, onYield) {
1176
+ const routes = httpRoutes(ctx);
1177
+ if (routes.length === 0)
1178
+ return [];
1179
+ const sites = [];
1180
+ const helpers = new Map();
1181
+ let scanned = 0;
1182
+ for (const file of ctx.getAllFiles()) {
1183
+ if (!TEST_REQUEST_FILE.test(file) || !(0, query_utils_1.isTestPath)(file))
1184
+ continue;
1185
+ if ((++scanned & 63) === 0)
1186
+ await onYield();
1187
+ collectTestRequests(ctx, file, sites, helpers);
1188
+ }
1189
+ const edges = [];
1190
+ const seen = new Set();
1191
+ for (const site of sites) {
1192
+ const route = matchHttp({ fn: site.from, file: site.from.filePath, line: site.line, column: site.column, callee: site.callee,
1193
+ method: site.method, segs: site.segs, suffix: false, display: site.display }, routes);
1194
+ if (!route)
1195
+ continue;
1196
+ const key = `${site.from.id}>${route.node.id}`;
1197
+ if (seen.has(key))
1198
+ continue;
1199
+ seen.add(key);
1200
+ edges.push({
1201
+ source: site.from.id,
1202
+ target: route.node.id,
1203
+ kind: 'calls',
1204
+ line: site.line,
1205
+ column: site.column,
1206
+ provenance: 'heuristic',
1207
+ metadata: {
1208
+ synthesizedBy: 'test-request',
1209
+ channel: 'http',
1210
+ callee: site.callee,
1211
+ method: site.method,
1212
+ href: site.display,
1213
+ registeredAt: `${route.node.filePath}:${route.node.startLine}`,
1214
+ },
1215
+ });
1216
+ }
1217
+ return edges;
1218
+ }
1219
+ const HTTP_GATE = /\b(?:fetch|\$fetch|ofetch|axios|ky|got|useFetch|useSWR)\b|\.\s*(?:get|post|put|patch|delete|head|options|request|\$get|\$post)\s*[<(]/;
1220
+ const QUEUE_GATE = /\.\s*add\s*\(|@Processor\s*\(|\bnew\s+Worker\s*[<(]|\.\s*process\s*\(/;
1221
+ const EVENT_GATE = /\.\s*(?:emit|emitAsync|on|once)\s*\(|@OnEvent\s*\(|@SubscribeMessage\s*\(/;
1222
+ function hasCrossTierPattern(content) {
1223
+ return HTTP_GATE.test(content) || QUEUE_GATE.test(content) || EVENT_GATE.test(content);
1224
+ }
1225
+ async function crossTierEdges(ctx, onYield) {
1226
+ const routes = httpRoutes(ctx);
1227
+ const httpSites = [];
1228
+ const producers = [];
1229
+ const consumers = [];
1230
+ const dispatches = [];
1231
+ const handlers = [];
1232
+ const cache = new Map();
1233
+ let scanned = 0;
1234
+ for (const file of ctx.getAllFiles()) {
1235
+ if (!JS_FILE.test(file) || (0, query_utils_1.isTestPath)(file) || (0, generated_detection_1.isGeneratedFile)(file))
1236
+ continue;
1237
+ if ((++scanned & 63) === 0)
1238
+ await onYield();
1239
+ const content = ctx.readFile(file);
1240
+ if (!content)
1241
+ continue;
1242
+ const wantsHttp = routes.length > 0 && HTTP_GATE.test(content);
1243
+ const wantsQueue = QUEUE_GATE.test(content);
1244
+ const wantsEvents = EVENT_GATE.test(content);
1245
+ if (!wantsHttp && !wantsQueue && !wantsEvents)
1246
+ continue;
1247
+ let facts = cache.get(file);
1248
+ if (facts === undefined) {
1249
+ facts = readFacts(ctx, file);
1250
+ cache.set(file, facts);
1251
+ }
1252
+ if (!facts)
1253
+ continue;
1254
+ if (wantsHttp)
1255
+ collectHttpSites(ctx, facts, httpSites, cache);
1256
+ if (wantsQueue)
1257
+ collectQueue(ctx, facts, producers, consumers, cache);
1258
+ if (wantsEvents)
1259
+ collectEvents(ctx, facts, dispatches, handlers, cache);
1260
+ }
1261
+ const edges = [];
1262
+ const seen = new Set();
1263
+ for (const site of httpSites) {
1264
+ const route = matchHttp(site, routes);
1265
+ if (!route || route.node.id === site.fn.id)
1266
+ continue;
1267
+ const key = `${site.fn.id}>${route.node.id}`;
1268
+ if (seen.has(key))
1269
+ continue;
1270
+ seen.add(key);
1271
+ edges.push({
1272
+ source: site.fn.id,
1273
+ target: route.node.id,
1274
+ kind: 'calls',
1275
+ line: site.line,
1276
+ column: site.column,
1277
+ provenance: 'heuristic',
1278
+ metadata: {
1279
+ synthesizedBy: 'http-client',
1280
+ channel: 'http',
1281
+ callee: site.callee,
1282
+ tier: exports.TIER_CLIENT_TO_SERVER,
1283
+ method: site.method,
1284
+ href: site.display,
1285
+ registeredAt: `${route.node.filePath}:${route.node.startLine}`,
1286
+ },
1287
+ });
1288
+ }
1289
+ await onYield();
1290
+ pairQueue(producers, consumers, edges, seen);
1291
+ pairEvents(dispatches, handlers, edges, seen);
1292
+ return edges;
1293
+ }
1294
+ //# sourceMappingURL=tier-synthesizer.js.map