codesift-mcp 0.14.0 → 0.15.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 (454) hide show
  1. package/README.md +5 -1
  2. package/dist/cli/commands-admin.d.ts +18 -0
  3. package/dist/cli/commands-admin.d.ts.map +1 -0
  4. package/dist/cli/commands-admin.js +187 -0
  5. package/dist/cli/commands-admin.js.map +1 -0
  6. package/dist/cli/commands-daemon.d.ts +42 -0
  7. package/dist/cli/commands-daemon.d.ts.map +1 -0
  8. package/dist/cli/commands-daemon.js +163 -0
  9. package/dist/cli/commands-daemon.js.map +1 -0
  10. package/dist/cli/commands-index.d.ts +12 -0
  11. package/dist/cli/commands-index.d.ts.map +1 -0
  12. package/dist/cli/commands-index.js +133 -0
  13. package/dist/cli/commands-index.js.map +1 -0
  14. package/dist/cli/commands-maintenance.d.ts +12 -0
  15. package/dist/cli/commands-maintenance.d.ts.map +1 -0
  16. package/dist/cli/commands-maintenance.js +409 -0
  17. package/dist/cli/commands-maintenance.js.map +1 -0
  18. package/dist/cli/commands-query.d.ts +21 -0
  19. package/dist/cli/commands-query.d.ts.map +1 -0
  20. package/dist/cli/commands-query.js +270 -0
  21. package/dist/cli/commands-query.js.map +1 -0
  22. package/dist/cli/commands.d.ts +2 -36
  23. package/dist/cli/commands.d.ts.map +1 -1
  24. package/dist/cli/commands.js +8 -867
  25. package/dist/cli/commands.js.map +1 -1
  26. package/dist/cli/embed-child.js +13 -2
  27. package/dist/cli/embed-child.js.map +1 -1
  28. package/dist/instructions.d.ts +1 -1
  29. package/dist/instructions.d.ts.map +1 -1
  30. package/dist/instructions.js +5 -2
  31. package/dist/instructions.js.map +1 -1
  32. package/dist/parser/extractors/php-class-metadata.d.ts +10 -0
  33. package/dist/parser/extractors/php-class-metadata.d.ts.map +1 -0
  34. package/dist/parser/extractors/php-class-metadata.js +65 -0
  35. package/dist/parser/extractors/php-class-metadata.js.map +1 -0
  36. package/dist/parser/extractors/php-doc.d.ts +13 -0
  37. package/dist/parser/extractors/php-doc.d.ts.map +1 -0
  38. package/dist/parser/extractors/php-doc.js +39 -0
  39. package/dist/parser/extractors/php-doc.js.map +1 -0
  40. package/dist/parser/extractors/php-node-metadata.d.ts +26 -0
  41. package/dist/parser/extractors/php-node-metadata.d.ts.map +1 -0
  42. package/dist/parser/extractors/php-node-metadata.js +132 -0
  43. package/dist/parser/extractors/php-node-metadata.js.map +1 -0
  44. package/dist/parser/extractors/php-symbol-builders.d.ts +13 -0
  45. package/dist/parser/extractors/php-symbol-builders.d.ts.map +1 -0
  46. package/dist/parser/extractors/php-symbol-builders.js +58 -0
  47. package/dist/parser/extractors/php-symbol-builders.js.map +1 -0
  48. package/dist/parser/extractors/php-walker.d.ts +4 -0
  49. package/dist/parser/extractors/php-walker.d.ts.map +1 -0
  50. package/dist/parser/extractors/php-walker.js +238 -0
  51. package/dist/parser/extractors/php-walker.js.map +1 -0
  52. package/dist/parser/extractors/php.d.ts +2 -33
  53. package/dist/parser/extractors/php.d.ts.map +1 -1
  54. package/dist/parser/extractors/php.js +2 -747
  55. package/dist/parser/extractors/php.js.map +1 -1
  56. package/dist/register-tool-loaders.d.ts +11 -11
  57. package/dist/register-tool-loaders.d.ts.map +1 -1
  58. package/dist/register-tools/runtime.d.ts +11 -0
  59. package/dist/register-tools/runtime.d.ts.map +1 -1
  60. package/dist/register-tools/runtime.js +40 -0
  61. package/dist/register-tools/runtime.js.map +1 -1
  62. package/dist/register-tools.d.ts.map +1 -1
  63. package/dist/register-tools.js +5 -1
  64. package/dist/register-tools.js.map +1 -1
  65. package/dist/retrieval/semantic-handlers.d.ts.map +1 -1
  66. package/dist/retrieval/semantic-handlers.js +10 -3
  67. package/dist/retrieval/semantic-handlers.js.map +1 -1
  68. package/dist/search/semantic.d.ts +2 -0
  69. package/dist/search/semantic.d.ts.map +1 -1
  70. package/dist/search/semantic.js +41 -9
  71. package/dist/search/semantic.js.map +1 -1
  72. package/dist/storage/chunk-store.d.ts +14 -2
  73. package/dist/storage/chunk-store.d.ts.map +1 -1
  74. package/dist/storage/chunk-store.js +214 -34
  75. package/dist/storage/chunk-store.js.map +1 -1
  76. package/dist/storage/embedding-store.d.ts +0 -12
  77. package/dist/storage/embedding-store.d.ts.map +1 -1
  78. package/dist/storage/embedding-store.js +69 -9
  79. package/dist/storage/embedding-store.js.map +1 -1
  80. package/dist/storage/index-cache.d.ts +12 -0
  81. package/dist/storage/index-cache.d.ts.map +1 -0
  82. package/dist/storage/index-cache.js +89 -0
  83. package/dist/storage/index-cache.js.map +1 -0
  84. package/dist/storage/index-json-mutations.d.ts +11 -0
  85. package/dist/storage/index-json-mutations.d.ts.map +1 -0
  86. package/dist/storage/index-json-mutations.js +93 -0
  87. package/dist/storage/index-json-mutations.js.map +1 -0
  88. package/dist/storage/index-migration.d.ts +22 -0
  89. package/dist/storage/index-migration.d.ts.map +1 -0
  90. package/dist/storage/index-migration.js +80 -0
  91. package/dist/storage/index-migration.js.map +1 -0
  92. package/dist/storage/index-store.d.ts +5 -61
  93. package/dist/storage/index-store.d.ts.map +1 -1
  94. package/dist/storage/index-store.js +33 -414
  95. package/dist/storage/index-store.js.map +1 -1
  96. package/dist/storage/index-version.d.ts +39 -0
  97. package/dist/storage/index-version.d.ts.map +1 -0
  98. package/dist/storage/index-version.js +87 -0
  99. package/dist/storage/index-version.js.map +1 -0
  100. package/dist/storage/registry.d.ts +9 -1
  101. package/dist/storage/registry.d.ts.map +1 -1
  102. package/dist/storage/registry.js +169 -8
  103. package/dist/storage/registry.js.map +1 -1
  104. package/dist/storage/shared-embedding-cache.d.ts +23 -3
  105. package/dist/storage/shared-embedding-cache.d.ts.map +1 -1
  106. package/dist/storage/shared-embedding-cache.js +300 -45
  107. package/dist/storage/shared-embedding-cache.js.map +1 -1
  108. package/dist/storage/telemetry/config.d.ts.map +1 -1
  109. package/dist/storage/telemetry/config.js +3 -2
  110. package/dist/storage/telemetry/config.js.map +1 -1
  111. package/dist/storage/telemetry/retro-aggregator.d.ts +21 -0
  112. package/dist/storage/telemetry/retro-aggregator.d.ts.map +1 -1
  113. package/dist/storage/telemetry/retro-aggregator.js +58 -16
  114. package/dist/storage/telemetry/retro-aggregator.js.map +1 -1
  115. package/dist/storage/telemetry/sanitizer.d.ts +16 -0
  116. package/dist/storage/telemetry/sanitizer.d.ts.map +1 -1
  117. package/dist/storage/telemetry/sanitizer.js +2 -0
  118. package/dist/storage/telemetry/sanitizer.js.map +1 -1
  119. package/dist/storage/telemetry/uploader.d.ts +3 -1
  120. package/dist/storage/telemetry/uploader.d.ts.map +1 -1
  121. package/dist/storage/telemetry/uploader.js +158 -21
  122. package/dist/storage/telemetry/uploader.js.map +1 -1
  123. package/dist/tools/cross-repo-contract-adapters.d.ts +39 -0
  124. package/dist/tools/cross-repo-contract-adapters.d.ts.map +1 -0
  125. package/dist/tools/cross-repo-contract-adapters.js +70 -0
  126. package/dist/tools/cross-repo-contract-adapters.js.map +1 -0
  127. package/dist/tools/cross-repo-contract-group.d.ts +17 -0
  128. package/dist/tools/cross-repo-contract-group.d.ts.map +1 -0
  129. package/dist/tools/cross-repo-contract-group.js +89 -0
  130. package/dist/tools/cross-repo-contract-group.js.map +1 -0
  131. package/dist/tools/cross-repo-contract-matcher.d.ts +24 -0
  132. package/dist/tools/cross-repo-contract-matcher.d.ts.map +1 -0
  133. package/dist/tools/cross-repo-contract-matcher.js +134 -0
  134. package/dist/tools/cross-repo-contract-matcher.js.map +1 -0
  135. package/dist/tools/cross-repo-contract-resolver.d.ts +9 -0
  136. package/dist/tools/cross-repo-contract-resolver.d.ts.map +1 -0
  137. package/dist/tools/cross-repo-contract-resolver.js +86 -0
  138. package/dist/tools/cross-repo-contract-resolver.js.map +1 -0
  139. package/dist/tools/cross-repo-contract-tools.d.ts +12 -135
  140. package/dist/tools/cross-repo-contract-tools.d.ts.map +1 -1
  141. package/dist/tools/cross-repo-contract-tools.js +8 -589
  142. package/dist/tools/cross-repo-contract-tools.js.map +1 -1
  143. package/dist/tools/cross-repo-contract-types.d.ts +30 -0
  144. package/dist/tools/cross-repo-contract-types.d.ts.map +1 -0
  145. package/dist/tools/cross-repo-contract-types.js +3 -0
  146. package/dist/tools/cross-repo-contract-types.js.map +1 -0
  147. package/dist/tools/cross-repo-outbound-calls.d.ts +28 -0
  148. package/dist/tools/cross-repo-outbound-calls.d.ts.map +1 -0
  149. package/dist/tools/cross-repo-outbound-calls.js +347 -0
  150. package/dist/tools/cross-repo-outbound-calls.js.map +1 -0
  151. package/dist/tools/cross-repo-outbound-lexer/call-detectors.d.ts.map +1 -1
  152. package/dist/tools/cross-repo-outbound-lexer/call-detectors.js +2 -0
  153. package/dist/tools/cross-repo-outbound-lexer/call-detectors.js.map +1 -1
  154. package/dist/tools/cross-repo-outbound-lexer/types.d.ts +2 -0
  155. package/dist/tools/cross-repo-outbound-lexer/types.d.ts.map +1 -1
  156. package/dist/tools/index-tools/file-indexer.js +3 -3
  157. package/dist/tools/index-tools/file-indexer.js.map +1 -1
  158. package/dist/tools/index-tools/folder-indexer.d.ts.map +1 -1
  159. package/dist/tools/index-tools/folder-indexer.js +47 -2
  160. package/dist/tools/index-tools/folder-indexer.js.map +1 -1
  161. package/dist/tools/index-tools/parse.d.ts +12 -3
  162. package/dist/tools/index-tools/parse.d.ts.map +1 -1
  163. package/dist/tools/index-tools/parse.js +33 -8
  164. package/dist/tools/index-tools/parse.js.map +1 -1
  165. package/dist/tools/index-tools/registry.d.ts +19 -0
  166. package/dist/tools/index-tools/registry.d.ts.map +1 -1
  167. package/dist/tools/index-tools/registry.js +88 -2
  168. package/dist/tools/index-tools/registry.js.map +1 -1
  169. package/dist/tools/index-tools/state.d.ts +31 -0
  170. package/dist/tools/index-tools/state.d.ts.map +1 -1
  171. package/dist/tools/index-tools/state.js +53 -0
  172. package/dist/tools/index-tools/state.js.map +1 -1
  173. package/dist/tools/index-tools/types.d.ts +9 -0
  174. package/dist/tools/index-tools/types.d.ts.map +1 -1
  175. package/dist/tools/index-tools/watcher.js +3 -3
  176. package/dist/tools/index-tools/watcher.js.map +1 -1
  177. package/dist/tools/index-tools/worktree-seed.d.ts +44 -0
  178. package/dist/tools/index-tools/worktree-seed.d.ts.map +1 -0
  179. package/dist/tools/index-tools/worktree-seed.js +302 -0
  180. package/dist/tools/index-tools/worktree-seed.js.map +1 -0
  181. package/dist/tools/index-tools.d.ts.map +1 -1
  182. package/dist/tools/index-tools.js +3 -1
  183. package/dist/tools/index-tools.js.map +1 -1
  184. package/dist/tools/nest-ext-tools/graphql.d.ts +17 -0
  185. package/dist/tools/nest-ext-tools/graphql.d.ts.map +1 -0
  186. package/dist/tools/nest-ext-tools/graphql.js +54 -0
  187. package/dist/tools/nest-ext-tools/graphql.js.map +1 -0
  188. package/dist/tools/nest-ext-tools/microservice.d.ts +17 -0
  189. package/dist/tools/nest-ext-tools/microservice.d.ts.map +1 -0
  190. package/dist/tools/nest-ext-tools/microservice.js +51 -0
  191. package/dist/tools/nest-ext-tools/microservice.js.map +1 -0
  192. package/dist/tools/nest-ext-tools/openapi.d.ts +81 -0
  193. package/dist/tools/nest-ext-tools/openapi.d.ts.map +1 -0
  194. package/dist/tools/nest-ext-tools/openapi.js +254 -0
  195. package/dist/tools/nest-ext-tools/openapi.js.map +1 -0
  196. package/dist/tools/nest-ext-tools/queue.d.ts +27 -0
  197. package/dist/tools/nest-ext-tools/queue.d.ts.map +1 -0
  198. package/dist/tools/nest-ext-tools/queue.js +95 -0
  199. package/dist/tools/nest-ext-tools/queue.js.map +1 -0
  200. package/dist/tools/nest-ext-tools/schedule.d.ts +19 -0
  201. package/dist/tools/nest-ext-tools/schedule.d.ts.map +1 -0
  202. package/dist/tools/nest-ext-tools/schedule.js +89 -0
  203. package/dist/tools/nest-ext-tools/schedule.js.map +1 -0
  204. package/dist/tools/nest-ext-tools/scope.d.ts +19 -0
  205. package/dist/tools/nest-ext-tools/scope.d.ts.map +1 -0
  206. package/dist/tools/nest-ext-tools/scope.js +151 -0
  207. package/dist/tools/nest-ext-tools/scope.js.map +1 -0
  208. package/dist/tools/nest-ext-tools/shared.d.ts +33 -0
  209. package/dist/tools/nest-ext-tools/shared.d.ts.map +1 -0
  210. package/dist/tools/nest-ext-tools/shared.js +279 -0
  211. package/dist/tools/nest-ext-tools/shared.js.map +1 -0
  212. package/dist/tools/nest-ext-tools/typeorm.d.ts +22 -0
  213. package/dist/tools/nest-ext-tools/typeorm.d.ts.map +1 -0
  214. package/dist/tools/nest-ext-tools/typeorm.js +59 -0
  215. package/dist/tools/nest-ext-tools/typeorm.js.map +1 -0
  216. package/dist/tools/nest-ext-tools/websocket.d.ts +21 -0
  217. package/dist/tools/nest-ext-tools/websocket.d.ts.map +1 -0
  218. package/dist/tools/nest-ext-tools/websocket.js +70 -0
  219. package/dist/tools/nest-ext-tools/websocket.js.map +1 -0
  220. package/dist/tools/nest-ext-tools.d.ts +10 -204
  221. package/dist/tools/nest-ext-tools.d.ts.map +1 -1
  222. package/dist/tools/nest-ext-tools.js +10 -749
  223. package/dist/tools/nest-ext-tools.js.map +1 -1
  224. package/dist/tools/perf-tools.js +2 -2
  225. package/dist/tools/python-audit/checks/dead-code.d.ts +1 -1
  226. package/dist/tools/python-audit/checks/dead-code.d.ts.map +1 -1
  227. package/dist/tools/python-constants/constant-match.d.ts +5 -0
  228. package/dist/tools/python-constants/constant-match.d.ts.map +1 -0
  229. package/dist/tools/python-constants/constant-match.js +24 -0
  230. package/dist/tools/python-constants/constant-match.js.map +1 -0
  231. package/dist/tools/python-constants/file-context.d.ts +5 -0
  232. package/dist/tools/python-constants/file-context.d.ts.map +1 -0
  233. package/dist/tools/python-constants/file-context.js +91 -0
  234. package/dist/tools/python-constants/file-context.js.map +1 -0
  235. package/dist/tools/python-constants/function-defaults.d.ts +5 -0
  236. package/dist/tools/python-constants/function-defaults.d.ts.map +1 -0
  237. package/dist/tools/python-constants/function-defaults.js +98 -0
  238. package/dist/tools/python-constants/function-defaults.js.map +1 -0
  239. package/dist/tools/python-constants/model.d.ts +78 -0
  240. package/dist/tools/python-constants/model.d.ts.map +1 -0
  241. package/dist/tools/python-constants/model.js +2 -0
  242. package/dist/tools/python-constants/model.js.map +1 -0
  243. package/dist/tools/python-constants/syntax.d.ts +22 -0
  244. package/dist/tools/python-constants/syntax.d.ts.map +1 -0
  245. package/dist/tools/python-constants/syntax.js +93 -0
  246. package/dist/tools/python-constants/syntax.js.map +1 -0
  247. package/dist/tools/python-constants/value-resolver.d.ts +6 -0
  248. package/dist/tools/python-constants/value-resolver.d.ts.map +1 -0
  249. package/dist/tools/python-constants/value-resolver.js +320 -0
  250. package/dist/tools/python-constants/value-resolver.js.map +1 -0
  251. package/dist/tools/python-constants-tools.d.ts +3 -45
  252. package/dist/tools/python-constants-tools.d.ts.map +1 -1
  253. package/dist/tools/python-constants-tools.js +2 -505
  254. package/dist/tools/python-constants-tools.js.map +1 -1
  255. package/dist/tools/route-tools/django.d.ts +8 -0
  256. package/dist/tools/route-tools/django.d.ts.map +1 -0
  257. package/dist/tools/route-tools/django.js +52 -0
  258. package/dist/tools/route-tools/django.js.map +1 -0
  259. package/dist/tools/route-tools/express.d.ts +5 -0
  260. package/dist/tools/route-tools/express.d.ts.map +1 -0
  261. package/dist/tools/route-tools/express.js +33 -0
  262. package/dist/tools/route-tools/express.js.map +1 -0
  263. package/dist/tools/route-tools/file-sources.d.ts +7 -0
  264. package/dist/tools/route-tools/file-sources.d.ts.map +1 -0
  265. package/dist/tools/route-tools/file-sources.js +15 -0
  266. package/dist/tools/route-tools/file-sources.js.map +1 -0
  267. package/dist/tools/route-tools/handler-discovery.d.ts +4 -0
  268. package/dist/tools/route-tools/handler-discovery.d.ts.map +1 -0
  269. package/dist/tools/route-tools/handler-discovery.js +38 -0
  270. package/dist/tools/route-tools/handler-discovery.js.map +1 -0
  271. package/dist/tools/route-tools/hono.d.ts +5 -0
  272. package/dist/tools/route-tools/hono.d.ts.map +1 -0
  273. package/dist/tools/route-tools/hono.js +61 -0
  274. package/dist/tools/route-tools/hono.js.map +1 -0
  275. package/dist/tools/route-tools/ktor.d.ts +5 -0
  276. package/dist/tools/route-tools/ktor.d.ts.map +1 -0
  277. package/dist/tools/route-tools/ktor.js +76 -0
  278. package/dist/tools/route-tools/ktor.js.map +1 -0
  279. package/dist/tools/route-tools/laravel.d.ts +5 -0
  280. package/dist/tools/route-tools/laravel.d.ts.map +1 -0
  281. package/dist/tools/route-tools/laravel.js +45 -0
  282. package/dist/tools/route-tools/laravel.js.map +1 -0
  283. package/dist/tools/route-tools/mermaid-call-chain.d.ts +3 -0
  284. package/dist/tools/route-tools/mermaid-call-chain.d.ts.map +1 -0
  285. package/dist/tools/route-tools/mermaid-call-chain.js +50 -0
  286. package/dist/tools/route-tools/mermaid-call-chain.js.map +1 -0
  287. package/dist/tools/route-tools/nest.d.ts +5 -0
  288. package/dist/tools/route-tools/nest.d.ts.map +1 -0
  289. package/dist/tools/route-tools/nest.js +75 -0
  290. package/dist/tools/route-tools/nest.js.map +1 -0
  291. package/dist/tools/route-tools/next-trace.d.ts +4 -0
  292. package/dist/tools/route-tools/next-trace.d.ts.map +1 -0
  293. package/dist/tools/route-tools/next-trace.js +48 -0
  294. package/dist/tools/route-tools/next-trace.js.map +1 -0
  295. package/dist/tools/route-tools/next.d.ts +7 -0
  296. package/dist/tools/route-tools/next.d.ts.map +1 -0
  297. package/dist/tools/route-tools/next.js +77 -0
  298. package/dist/tools/route-tools/next.js.map +1 -0
  299. package/dist/tools/route-tools/python-decorators.d.ts +7 -0
  300. package/dist/tools/route-tools/python-decorators.d.ts.map +1 -0
  301. package/dist/tools/route-tools/python-decorators.js +53 -0
  302. package/dist/tools/route-tools/python-decorators.js.map +1 -0
  303. package/dist/tools/route-tools/route-mermaid.d.ts +4 -0
  304. package/dist/tools/route-tools/route-mermaid.d.ts.map +1 -0
  305. package/dist/tools/route-tools/route-mermaid.js +53 -0
  306. package/dist/tools/route-tools/route-mermaid.js.map +1 -0
  307. package/dist/tools/route-tools/spring-kotlin.d.ts +5 -0
  308. package/dist/tools/route-tools/spring-kotlin.d.ts.map +1 -0
  309. package/dist/tools/route-tools/spring-kotlin.js +52 -0
  310. package/dist/tools/route-tools/spring-kotlin.js.map +1 -0
  311. package/dist/tools/route-tools/trace-analysis.d.ts +4 -0
  312. package/dist/tools/route-tools/trace-analysis.d.ts.map +1 -0
  313. package/dist/tools/route-tools/trace-analysis.js +37 -0
  314. package/dist/tools/route-tools/trace-analysis.js.map +1 -0
  315. package/dist/tools/route-tools/trace-route.d.ts +6 -0
  316. package/dist/tools/route-tools/trace-route.d.ts.map +1 -0
  317. package/dist/tools/route-tools/trace-route.js +53 -0
  318. package/dist/tools/route-tools/trace-route.js.map +1 -0
  319. package/dist/tools/route-tools/types.d.ts +35 -0
  320. package/dist/tools/route-tools/types.d.ts.map +1 -0
  321. package/dist/tools/route-tools/types.js +2 -0
  322. package/dist/tools/route-tools/types.js.map +1 -0
  323. package/dist/tools/route-tools/yii2.d.ts +5 -0
  324. package/dist/tools/route-tools/yii2.d.ts.map +1 -0
  325. package/dist/tools/route-tools/yii2.js +73 -0
  326. package/dist/tools/route-tools/yii2.js.map +1 -0
  327. package/dist/tools/route-tools.d.ts +7 -49
  328. package/dist/tools/route-tools.d.ts.map +1 -1
  329. package/dist/tools/route-tools.js +5 -978
  330. package/dist/tools/route-tools.js.map +1 -1
  331. package/dist/tools/symbol-analysis-tools.d.ts +81 -0
  332. package/dist/tools/symbol-analysis-tools.d.ts.map +1 -0
  333. package/dist/tools/symbol-analysis-tools.js +327 -0
  334. package/dist/tools/symbol-analysis-tools.js.map +1 -0
  335. package/dist/tools/symbol-context-tools.d.ts +68 -0
  336. package/dist/tools/symbol-context-tools.d.ts.map +1 -0
  337. package/dist/tools/symbol-context-tools.js +232 -0
  338. package/dist/tools/symbol-context-tools.js.map +1 -0
  339. package/dist/tools/symbol-lookup-tools.d.ts +77 -0
  340. package/dist/tools/symbol-lookup-tools.d.ts.map +1 -0
  341. package/dist/tools/symbol-lookup-tools.js +305 -0
  342. package/dist/tools/symbol-lookup-tools.js.map +1 -0
  343. package/dist/tools/symbol-reference-tools.d.ts +38 -0
  344. package/dist/tools/symbol-reference-tools.d.ts.map +1 -0
  345. package/dist/tools/symbol-reference-tools.js +253 -0
  346. package/dist/tools/symbol-reference-tools.js.map +1 -0
  347. package/dist/tools/symbol-tool-internals.d.ts +9 -0
  348. package/dist/tools/symbol-tool-internals.d.ts.map +1 -0
  349. package/dist/tools/symbol-tool-internals.js +47 -0
  350. package/dist/tools/symbol-tool-internals.js.map +1 -0
  351. package/dist/tools/symbol-tools.d.ts +11 -270
  352. package/dist/tools/symbol-tools.d.ts.map +1 -1
  353. package/dist/tools/symbol-tools.js +7 -1131
  354. package/dist/tools/symbol-tools.js.map +1 -1
  355. package/dist/tools/taint/python-context.d.ts +10 -0
  356. package/dist/tools/taint/python-context.d.ts.map +1 -0
  357. package/dist/tools/taint/python-context.js +150 -0
  358. package/dist/tools/taint/python-context.js.map +1 -0
  359. package/dist/tools/taint/taint-analyzer.d.ts +2 -0
  360. package/dist/tools/taint/taint-analyzer.d.ts.map +1 -0
  361. package/dist/tools/taint/taint-analyzer.js +2 -0
  362. package/dist/tools/taint/taint-analyzer.js.map +1 -0
  363. package/dist/tools/taint/taint-expression.d.ts +18 -0
  364. package/dist/tools/taint/taint-expression.d.ts.map +1 -0
  365. package/dist/tools/taint/taint-expression.js +265 -0
  366. package/dist/tools/taint/taint-expression.js.map +1 -0
  367. package/dist/tools/taint/taint-flow.d.ts +10 -0
  368. package/dist/tools/taint/taint-flow.d.ts.map +1 -0
  369. package/dist/tools/taint/taint-flow.js +178 -0
  370. package/dist/tools/taint/taint-flow.js.map +1 -0
  371. package/dist/tools/taint/taint-model.d.ts +113 -0
  372. package/dist/tools/taint/taint-model.d.ts.map +1 -0
  373. package/dist/tools/taint/taint-model.js +277 -0
  374. package/dist/tools/taint/taint-model.js.map +1 -0
  375. package/dist/tools/taint-tools.d.ts +2 -34
  376. package/dist/tools/taint-tools.d.ts.map +1 -1
  377. package/dist/tools/taint-tools.js +2 -852
  378. package/dist/tools/taint-tools.js.map +1 -1
  379. package/dist/tools/typescript-constants/file-context.d.ts +7 -0
  380. package/dist/tools/typescript-constants/file-context.d.ts.map +1 -0
  381. package/dist/tools/typescript-constants/file-context.js +219 -0
  382. package/dist/tools/typescript-constants/file-context.js.map +1 -0
  383. package/dist/tools/typescript-constants/symbol-resolver.d.ts +7 -0
  384. package/dist/tools/typescript-constants/symbol-resolver.d.ts.map +1 -0
  385. package/dist/tools/typescript-constants/symbol-resolver.js +165 -0
  386. package/dist/tools/typescript-constants/symbol-resolver.js.map +1 -0
  387. package/dist/tools/typescript-constants/types.d.ts +44 -0
  388. package/dist/tools/typescript-constants/types.d.ts.map +1 -0
  389. package/dist/tools/typescript-constants/types.js +2 -0
  390. package/dist/tools/typescript-constants/types.js.map +1 -0
  391. package/dist/tools/typescript-constants/value-evaluator.d.ts +8 -0
  392. package/dist/tools/typescript-constants/value-evaluator.d.ts.map +1 -0
  393. package/dist/tools/typescript-constants/value-evaluator.js +404 -0
  394. package/dist/tools/typescript-constants/value-evaluator.js.map +1 -0
  395. package/dist/tools/typescript-constants-tools.d.ts.map +1 -1
  396. package/dist/tools/typescript-constants-tools.js +19 -709
  397. package/dist/tools/typescript-constants-tools.js.map +1 -1
  398. package/dist/utils/import-graph/collect.d.ts +5 -0
  399. package/dist/utils/import-graph/collect.d.ts.map +1 -0
  400. package/dist/utils/import-graph/collect.js +47 -0
  401. package/dist/utils/import-graph/collect.js.map +1 -0
  402. package/dist/utils/import-graph/edge-accumulator.d.ts +7 -0
  403. package/dist/utils/import-graph/edge-accumulator.d.ts.map +1 -0
  404. package/dist/utils/import-graph/edge-accumulator.js +47 -0
  405. package/dist/utils/import-graph/edge-accumulator.js.map +1 -0
  406. package/dist/utils/import-graph/language-imports.d.ts +6 -0
  407. package/dist/utils/import-graph/language-imports.d.ts.map +1 -0
  408. package/dist/utils/import-graph/language-imports.js +84 -0
  409. package/dist/utils/import-graph/language-imports.js.map +1 -0
  410. package/dist/utils/import-graph/metrics.d.ts +6 -0
  411. package/dist/utils/import-graph/metrics.d.ts.map +1 -0
  412. package/dist/utils/import-graph/metrics.js +51 -0
  413. package/dist/utils/import-graph/metrics.js.map +1 -0
  414. package/dist/utils/import-graph/path-map.d.ts +7 -0
  415. package/dist/utils/import-graph/path-map.d.ts.map +1 -0
  416. package/dist/utils/import-graph/path-map.js +56 -0
  417. package/dist/utils/import-graph/path-map.js.map +1 -0
  418. package/dist/utils/import-graph/python-php-edge-collectors.d.ts +5 -0
  419. package/dist/utils/import-graph/python-php-edge-collectors.d.ts.map +1 -0
  420. package/dist/utils/import-graph/python-php-edge-collectors.js +56 -0
  421. package/dist/utils/import-graph/python-php-edge-collectors.js.map +1 -0
  422. package/dist/utils/import-graph/source-edge-collector.d.ts +13 -0
  423. package/dist/utils/import-graph/source-edge-collector.d.ts.map +1 -0
  424. package/dist/utils/import-graph/source-edge-collector.js +42 -0
  425. package/dist/utils/import-graph/source-edge-collector.js.map +1 -0
  426. package/dist/utils/import-graph/source-imports.d.ts +5 -0
  427. package/dist/utils/import-graph/source-imports.d.ts.map +1 -0
  428. package/dist/utils/import-graph/source-imports.js +33 -0
  429. package/dist/utils/import-graph/source-imports.js.map +1 -0
  430. package/dist/utils/import-graph/types.d.ts +15 -0
  431. package/dist/utils/import-graph/types.d.ts.map +1 -0
  432. package/dist/utils/import-graph/types.js +2 -0
  433. package/dist/utils/import-graph/types.js.map +1 -0
  434. package/dist/utils/import-graph/typescript-edge-collector.d.ts +7 -0
  435. package/dist/utils/import-graph/typescript-edge-collector.d.ts.map +1 -0
  436. package/dist/utils/import-graph/typescript-edge-collector.js +58 -0
  437. package/dist/utils/import-graph/typescript-edge-collector.js.map +1 -0
  438. package/dist/utils/import-graph/workspace-alias.d.ts +7 -0
  439. package/dist/utils/import-graph/workspace-alias.d.ts.map +1 -0
  440. package/dist/utils/import-graph/workspace-alias.js +95 -0
  441. package/dist/utils/import-graph/workspace-alias.js.map +1 -0
  442. package/dist/utils/import-graph/workspace-entry.d.ts +3 -0
  443. package/dist/utils/import-graph/workspace-entry.d.ts.map +1 -0
  444. package/dist/utils/import-graph/workspace-entry.js +81 -0
  445. package/dist/utils/import-graph/workspace-entry.js.map +1 -0
  446. package/dist/utils/import-graph.d.ts +10 -94
  447. package/dist/utils/import-graph.d.ts.map +1 -1
  448. package/dist/utils/import-graph.js +6 -702
  449. package/dist/utils/import-graph.js.map +1 -1
  450. package/package.json +1 -1
  451. package/rules/codesift.md +11 -2
  452. package/rules/codesift.mdc +11 -2
  453. package/rules/codex.md +11 -2
  454. package/rules/gemini.md +11 -2
@@ -1,982 +1,9 @@
1
1
  /**
2
- * HTTP route tracing — given a URL path, find handler → service → DB calls.
3
- * Supports NestJS decorators, Next.js App Router, Express, Yii2 conventions, and Laravel routes.
2
+ * Stable public facade for HTTP route tracing.
3
+ * Implementations live in focused modules under ./route-tools/.
4
4
  */
5
- import { getCodeIndex } from "./index-tools.js";
6
- import { buildAdjacencyIndex, buildCallTree, stripSource } from "./graph-tools.js";
7
- import { findAstroHandlers } from "./astro-routes.js";
8
- import { matchPath } from "./route-shared.js";
9
5
  export { matchPath } from "./route-shared.js";
10
- import { deriveUrlPath, computeLayoutChain, traceMiddleware, scanDirective } from "../utils/nextjs.js";
11
- import { join } from "node:path";
12
- const DB_PATTERNS = [
13
- /prisma\.\w+\.(findMany|findFirst|findUnique|create|update|delete|upsert|count|aggregate|groupBy)/,
14
- /\.\$(transaction|queryRaw|executeRaw)/,
15
- /getRepository|\.query\(|\.execute\(/,
16
- /knex\.|\.raw\(/,
17
- // PHP / Yii2 ActiveRecord
18
- /->find\(\)|->findOne\(|->findAll\(|->findBySql\(/,
19
- /->createCommand\(|Yii::\$app->db/,
20
- /::find\(\)->where\(|->andWhere\(|->orWhere\(/,
21
- // Kotlin — Exposed ORM, Spring Data, Ktor
22
- /transaction\s*\{[\s\S]*?\.(select|insert|update|delete)/,
23
- /\.(findById|findAll|save|deleteById|findBy\w+)\s*\(/,
24
- /\bSchemaUtils\.(create|drop)/,
25
- // Python — Django ORM, SQLAlchemy
26
- /\.objects\.(get|filter|all|exclude|create|update|delete|aggregate|annotate|values|values_list|count|exists|first|last|bulk_create|bulk_update|get_or_create|update_or_create)\s*\(/,
27
- /\.query\.(filter|filter_by|get|all|first|one|one_or_none|join|outerjoin|subquery)\s*\(/,
28
- /session\.(add|delete|commit|rollback|flush|execute|query)\s*\(/,
29
- /\.select_related\(|\.prefetch_related\(/,
30
- ];
31
- /**
32
- * Find NestJS route handlers via @Controller + @Get/@Post/etc. decorators.
33
- * Reads raw file content because tree-sitter symbol source may not include decorators.
34
- */
35
- export async function findNestJSHandlers(index, searchPath) {
36
- const handlers = [];
37
- const methods = ["Get", "Post", "Put", "Delete", "Patch"];
38
- // Find controller files
39
- const controllerFiles = index.files.filter((f) => f.path.endsWith(".controller.ts") || f.path.endsWith(".controller.js"));
40
- const { readFile } = await import("node:fs/promises");
41
- const { join: joinPath } = await import("node:path");
42
- for (const file of controllerFiles) {
43
- let source;
44
- try {
45
- source = await readFile(joinPath(index.root, file.path), "utf-8");
46
- }
47
- catch {
48
- continue;
49
- }
50
- // Extract controller prefix — supports both @Controller('prefix') and @Controller()
51
- const ctrlMatchStr = /@Controller\s*\(\s*['"`]([^'"`]*)['"`]/.exec(source);
52
- const ctrlMatchEmpty = !ctrlMatchStr ? /@Controller\s*\(\s*\)/.exec(source) : null;
53
- const controllerPrefix = ctrlMatchStr?.[1] ?? (ctrlMatchEmpty ? "" : "");
54
- for (const method of methods) {
55
- // Pass 1: string-literal paths — @Get('path')
56
- const re = new RegExp(`@${method}\\s*\\(\\s*['"\`]([^'"\`]*)['"\`]\\s*\\)\\s*\\n\\s*(?:async\\s+)?(\\w+)`, "g");
57
- let match;
58
- while ((match = re.exec(source)) !== null) {
59
- const routePath = match[1] ?? "";
60
- const funcName = match[2] ?? "";
61
- const fullPath = `/${controllerPrefix}/${routePath}`.replace(/\/+/g, "/");
62
- if (matchPath(fullPath, searchPath)) {
63
- const sym = index.symbols.find((s) => s.file === file.path && s.name === funcName);
64
- handlers.push({
65
- symbol: sym ? stripSource(sym) : { id: `${file.path}:${funcName}`, name: funcName, kind: "method", file: file.path, start_line: 1, end_line: 1 },
66
- file: file.path,
67
- method: method.toUpperCase(),
68
- framework: "nestjs",
69
- });
70
- }
71
- }
72
- // Pass 2: empty decorator — @Get() with no path argument
73
- const reEmpty = new RegExp(`@${method}\\s*\\(\\s*\\)\\s*\\n\\s*(?:async\\s+)?(\\w+)`, "g");
74
- let emptyMatch;
75
- while ((emptyMatch = reEmpty.exec(source)) !== null) {
76
- const funcName = emptyMatch[1] ?? "";
77
- const fullPath = `/${controllerPrefix}`.replace(/\/+/g, "/") || "/";
78
- if (matchPath(fullPath, searchPath)) {
79
- if (handlers.some((h) => h.file === file.path && h.symbol.name === funcName && h.method === method.toUpperCase()))
80
- continue;
81
- const sym = index.symbols.find((s) => s.file === file.path && s.name === funcName);
82
- handlers.push({
83
- symbol: sym ? stripSource(sym) : { id: `${file.path}:${funcName}`, name: funcName, kind: "method", file: file.path, start_line: 1, end_line: 1 },
84
- file: file.path,
85
- method: method.toUpperCase(),
86
- framework: "nestjs",
87
- });
88
- }
89
- }
90
- }
91
- }
92
- return handlers;
93
- }
94
- /**
95
- * Find Next.js App Router handlers — file path IS the route.
96
- */
97
- function findNextJSHandlers(index, searchPath) {
98
- const handlers = [];
99
- const normalized = searchPath.replace(/^\/|\/$/g, "");
100
- for (const file of index.files) {
101
- // Match app/api/...route.{ts,tsx,js,jsx} or app/...route.{ts,tsx,js,jsx}
102
- if (!/\/route\.[jt]sx?$/.test(file.path))
103
- continue;
104
- // Extract route path from file path: app/api/users/[id]/route.ts → /api/users/[id]
105
- const routeMatch = file.path.match(/app\/(.*?)\/route\.[jt]sx?$/);
106
- if (!routeMatch)
107
- continue;
108
- // Strip route groups: (auth)/login → login
109
- const filePath = routeMatch[1].replace(/\([^)]+\)\/?/g, "");
110
- if (matchPath(filePath, normalized)) {
111
- // Find exported handler functions (GET, POST, etc.)
112
- const fileSymbols = index.symbols.filter((s) => s.file === file.path && /^(GET|POST|PUT|DELETE|PATCH)$/.test(s.name));
113
- for (const sym of fileSymbols) {
114
- handlers.push({
115
- symbol: stripSource(sym),
116
- file: sym.file,
117
- method: sym.name,
118
- framework: "nextjs",
119
- router: "app",
120
- });
121
- }
122
- // If no named exports found, add the file itself
123
- if (fileSymbols.length === 0) {
124
- handlers.push({
125
- symbol: { id: file.path, name: "route", kind: "function", file: file.path, start_line: 1, end_line: 1 },
126
- file: file.path,
127
- framework: "nextjs",
128
- router: "app",
129
- });
130
- }
131
- }
132
- }
133
- return handlers;
134
- }
135
- /**
136
- * Find Pages Router API route handlers via default exports in pages/api/.
137
- * @internal exported for unit testing
138
- */
139
- function findPagesRouterHandlers(index, searchPath) {
140
- const handlers = [];
141
- // Require Next.js project signal to disambiguate from Astro's src/pages/api/*.
142
- // Accept next.config.* at root/src OR an App Router convention file.
143
- const hasNextSignal = index.files.some((f) => /^(src\/)?next\.config\.[mc]?[jt]sx?$/.test(f.path) ||
144
- /(^|\/)app\/.*\/(page|layout|route)\.[jt]sx?$/.test(f.path));
145
- if (!hasNextSignal)
146
- return handlers;
147
- for (const file of index.files) {
148
- // Only match files under pages/api/ (not src/pages/api/ which is Astro convention)
149
- if (!/^(\.\/)?pages\/api\//.test(file.path))
150
- continue;
151
- // Derive URL path from file path
152
- const urlPath = deriveUrlPath(file.path, "pages");
153
- const normalizedSearch = searchPath.replace(/^\/|\/$/g, "");
154
- const normalizedUrl = urlPath.replace(/^\/|\/$/g, "");
155
- if (normalizedUrl !== normalizedSearch)
156
- continue;
157
- // Find default export or named handler in the file
158
- const fileSymbols = index.symbols.filter((s) => s.file === file.path);
159
- // Look for default export
160
- const defaultExport = fileSymbols.find((s) => s.name === "default" || s.name === "handler");
161
- if (defaultExport) {
162
- handlers.push({
163
- symbol: stripSource(defaultExport),
164
- file: file.path,
165
- framework: "nextjs",
166
- router: "pages",
167
- });
168
- }
169
- else if (fileSymbols.length > 0) {
170
- // Try variable indirection: find any exported function
171
- const exported = fileSymbols.find((s) => s.kind === "function" || s.kind === "variable");
172
- if (exported) {
173
- handlers.push({
174
- symbol: stripSource(exported),
175
- file: file.path,
176
- framework: "nextjs",
177
- router: "pages",
178
- });
179
- }
180
- }
181
- // Fallback: at least mark the file as having a handler
182
- if (handlers.filter((h) => h.file === file.path).length === 0) {
183
- handlers.push({
184
- symbol: {
185
- id: file.path, name: "handler", kind: "function",
186
- file: file.path, start_line: 1, end_line: 1,
187
- },
188
- file: file.path,
189
- framework: "nextjs",
190
- router: "pages",
191
- });
192
- }
193
- }
194
- return handlers;
195
- }
196
- /**
197
- * Find Express-style route handlers via router.get/app.post patterns.
198
- * Restricted to JS/TS files to avoid false positives from Python test
199
- * code like `client.get('/')` or Ruby/PHP method calls.
200
- */
201
- /**
202
- * Find Hono route handlers by consulting the HonoExtractor-produced model.
203
- * Uses HonoCache when available, falls back to on-demand parse.
204
- *
205
- * The model has pre-resolved paths (basePath + mount prefix + route path),
206
- * so path matching is a direct matchPath() call against each route.
207
- */
208
- async function findHonoHandlers(repo, index, searchPath) {
209
- // Only run if Hono is detected
210
- const { detectFrameworks } = await import("../utils/framework-detect.js");
211
- const frameworks = detectFrameworks(index);
212
- if (!frameworks.has("hono"))
213
- return [];
214
- // Resolve entry file: prefer orchestrator, else first file containing `new Hono()`
215
- const entryFile = resolveHonoEntryFile(index);
216
- if (!entryFile)
217
- return [];
218
- // Get model (via cache)
219
- let model;
220
- try {
221
- const { honoCache } = await import("../cache/hono-cache.js");
222
- const { HonoExtractor } = await import("../parser/extractors/hono.js");
223
- const extractor = new HonoExtractor();
224
- model = await honoCache.get(repo, entryFile, extractor);
225
- }
226
- catch {
227
- return [];
228
- }
229
- const handlers = [];
230
- for (const route of model.routes) {
231
- if (!matchPath(route.path, searchPath))
232
- continue;
233
- // Find the handler symbol in the index by file + name + line
234
- let handlerSym = index.symbols.find((s) => s.file === route.handler.file.replace(index.root + "/", "") &&
235
- s.name === route.handler.name &&
236
- Math.abs(s.start_line - route.handler.line) <= 2);
237
- // Fallback: synthetic stub for inline handlers
238
- if (!handlerSym) {
239
- handlerSym = {
240
- id: `hono:${route.file}:${route.line}`,
241
- repo,
242
- name: route.handler.name,
243
- kind: "function",
244
- file: route.handler.file.replace(index.root + "/", ""),
245
- start_line: route.handler.line,
246
- end_line: route.handler.line,
247
- start_byte: 0,
248
- end_byte: 0,
249
- source: "",
250
- tokens: [route.handler.name],
251
- };
252
- }
253
- handlers.push({
254
- symbol: stripSource(handlerSym),
255
- file: handlerSym.file,
256
- method: route.method,
257
- framework: "hono",
258
- });
259
- }
260
- return handlers;
261
- }
262
- /** Find the Hono entry file for a repo — ORCHESTRATOR or first new Hono() file. */
263
- function resolveHonoEntryFile(index) {
264
- for (const sym of index.symbols) {
265
- if (sym.source && /new\s+(?:Hono|OpenAPIHono)\s*(?:<[^>]*>)?\s*\(/.test(sym.source)) {
266
- // Return absolute path
267
- return join(index.root, sym.file);
268
- }
269
- }
270
- return null;
271
- }
272
- function findExpressHandlers(index, searchPath) {
273
- const handlers = [];
274
- const methods = ["get", "post", "put", "delete", "patch"];
275
- for (const sym of index.symbols) {
276
- if (!sym.source)
277
- continue;
278
- // Only scan JS/TS files — .get()/.post() is ambiguous across languages
279
- if (!/\.(ts|tsx|js|jsx|mjs|cjs)$/.test(sym.file))
280
- continue;
281
- // Skip test files to avoid matching test harness client calls
282
- if (/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(sym.file))
283
- continue;
284
- for (const method of methods) {
285
- const re = new RegExp(`\\.(${method})\\s*\\(\\s*['"\`]([^'"\`]+)['"\`]`);
286
- const match = re.exec(sym.source);
287
- if (!match)
288
- continue;
289
- const routePath = match[2] ?? "";
290
- if (matchPath(routePath, searchPath)) {
291
- handlers.push({
292
- symbol: stripSource(sym),
293
- file: sym.file,
294
- method: method.toUpperCase(),
295
- framework: "express",
296
- });
297
- }
298
- }
299
- }
300
- return handlers;
301
- }
302
- /**
303
- * Detect DB operations in a symbol's call chain.
304
- */
305
- /**
306
- * Detect server actions in the call chain by checking for "use server" directive
307
- * at the file level (not function-body level).
308
- */
309
- async function findServerActions(repoRoot, calleeSymbols, callChain) {
310
- const actions = [];
311
- const checkedFiles = new Map();
312
- for (const sym of calleeSymbols) {
313
- const absPath = join(repoRoot, sym.file);
314
- let hasDirective;
315
- if (checkedFiles.has(sym.file)) {
316
- hasDirective = checkedFiles.get(sym.file);
317
- }
318
- else {
319
- const directive = await scanDirective(absPath);
320
- hasDirective = directive === "use server";
321
- checkedFiles.set(sym.file, hasDirective);
322
- }
323
- if (hasDirective) {
324
- // Find who called this symbol
325
- const callerIdx = callChain.findIndex((c) => c.file === sym.file && c.name === sym.name);
326
- const calledFrom = callerIdx > 0 ? callChain[callerIdx - 1]?.name : undefined;
327
- actions.push({
328
- name: sym.name,
329
- file: sym.file,
330
- called_from: calledFrom,
331
- });
332
- }
333
- }
334
- return actions;
335
- }
336
- function findDbCalls(symbols) {
337
- const calls = [];
338
- for (const sym of symbols) {
339
- if (!sym.source)
340
- continue;
341
- for (const pattern of DB_PATTERNS) {
342
- const match = pattern.exec(sym.source);
343
- if (match) {
344
- calls.push({
345
- symbol_name: sym.name,
346
- file: sym.file,
347
- line: sym.start_line,
348
- operation: match[0],
349
- });
350
- break; // One match per symbol
351
- }
352
- }
353
- }
354
- return calls;
355
- }
356
- function nodeKey(node) {
357
- return `${node.file}:${node.name}`;
358
- }
359
- function nodeAlias(node, aliases) {
360
- const key = nodeKey(node);
361
- const existing = aliases.get(key);
362
- if (existing)
363
- return existing;
364
- const baseName = node.file.split("/").pop()?.replace(/\.\w+$/, "") ?? node.name;
365
- const alias = `${baseName}_${node.name}`.replace(/[^a-zA-Z0-9_]/g, "_");
366
- aliases.set(key, alias);
367
- return alias;
368
- }
369
- function appendDbCalls(lines, dbCalls, node, actor) {
370
- const callsForNode = dbCalls.filter((db) => db.file === node.file && db.symbol_name === node.name);
371
- for (const db of callsForNode.slice(0, 3)) {
372
- lines.push(` ${actor}->>+DB: ${db.operation}`);
373
- lines.push(` DB-->>-${actor}: result`);
374
- }
375
- }
376
- /**
377
- * Render a RouteTraceResult as a Mermaid sequence diagram.
378
- * @internal exported for unit testing
379
- */
380
- export function routeToMermaid(result) {
381
- if (result.handlers.length === 0) {
382
- return "sequenceDiagram\n Note over Client: No handler found for " + result.path;
383
- }
384
- const lines = ["sequenceDiagram"];
385
- const handler = result.handlers[0];
386
- const method = handler.method ?? "REQUEST";
387
- const aliases = new Map();
388
- // Add Middleware participant if middleware applies
389
- if (result.middleware?.applies) {
390
- lines.push(` participant Middleware`);
391
- lines.push(` Client->>+Middleware: ${method} ${result.path}`);
392
- lines.push(` Middleware->>+Controller: continue`);
393
- }
394
- else {
395
- lines.push(` Client->>+Controller: ${method} ${result.path}`);
396
- }
397
- // Add Layout chain rendering
398
- if (result.layout_chain && result.layout_chain.length > 0) {
399
- let prev = "Controller";
400
- for (let i = 0; i < result.layout_chain.length; i++) {
401
- const layoutName = `Layout${i + 1}`;
402
- const layoutFile = result.layout_chain[i];
403
- lines.push(` participant ${layoutName}`);
404
- lines.push(` ${prev}->>+${layoutName}: render (${layoutFile})`);
405
- prev = layoutName;
406
- }
407
- }
408
- const root = result.call_chain[0];
409
- if (root) {
410
- appendDbCalls(lines, result.db_calls, root, "Controller");
411
- }
412
- const descendants = result.call_chain
413
- .filter((node, idx) => idx > 0 && node.depth > 0)
414
- .slice(0, 12);
415
- const stack = [];
416
- const closeUntilDepth = (nextDepth) => {
417
- while (stack.length > 0 && (stack[stack.length - 1]?.node.depth ?? -1) >= nextDepth) {
418
- const finished = stack.pop();
419
- if (!finished)
420
- break;
421
- const returnTo = stack.length > 0 ? stack[stack.length - 1].alias : "Controller";
422
- lines.push(` ${finished.alias}-->>-${returnTo}: result`);
423
- }
424
- };
425
- for (let i = 0; i < descendants.length; i++) {
426
- const node = descendants[i];
427
- closeUntilDepth(node.depth);
428
- const parentActor = stack.length > 0 ? stack[stack.length - 1].alias : "Controller";
429
- const alias = nodeAlias(node, aliases);
430
- lines.push(` ${parentActor}->>+${alias}: ${node.name}()`);
431
- appendDbCalls(lines, result.db_calls, node, alias);
432
- stack.push({ node, alias });
433
- const nextDepth = descendants[i + 1]?.depth ?? 0;
434
- closeUntilDepth(nextDepth);
435
- }
436
- closeUntilDepth(0);
437
- // Close layout chain
438
- if (result.layout_chain && result.layout_chain.length > 0) {
439
- for (let i = result.layout_chain.length - 1; i >= 0; i--) {
440
- const layoutName = `Layout${i + 1}`;
441
- const returnTo = i > 0 ? `Layout${i}` : "Controller";
442
- lines.push(` ${layoutName}-->>-${returnTo}: rendered`);
443
- }
444
- }
445
- if (result.middleware?.applies) {
446
- lines.push(` Controller-->>-Middleware: response`);
447
- lines.push(` Middleware-->>-Client: response`);
448
- }
449
- else {
450
- lines.push(` Controller-->>-Client: response`);
451
- }
452
- return lines.join("\n");
453
- }
454
- /**
455
- * Find Yii2 route handlers via convention: controller-id/action-id → ControllerIdController::actionActionId().
456
- * Supports modules: module-id/controller-id/action-id.
457
- */
458
- async function findYii2Handlers(index, searchPath) {
459
- const handlers = [];
460
- const normalized = searchPath.replace(/^\/|\/$/g, "").toLowerCase();
461
- const segments = normalized.split("/").filter(Boolean);
462
- if (segments.length === 0)
463
- return handlers;
464
- // Determine controller ID and action ID
465
- // Patterns: "controller/action", "module/controller/action", "controller" (default action=index)
466
- let controllerId;
467
- let actionId;
468
- if (segments.length === 1) {
469
- controllerId = segments[0];
470
- actionId = "index";
471
- }
472
- else if (segments.length === 2) {
473
- controllerId = segments[0];
474
- actionId = segments[1];
475
- }
476
- else {
477
- // Module routing: take last two segments as controller/action
478
- controllerId = segments[segments.length - 2];
479
- actionId = segments[segments.length - 1];
480
- }
481
- // Convert kebab-case to PascalCase for class name: "site" → "Site", "user-comment" → "UserComment"
482
- const toPascal = (s) => s.split("-").map(p => p.charAt(0).toUpperCase() + p.slice(1)).join("");
483
- // Convert kebab-case to camelCase for action method: "hello-world" → "HelloWorld"
484
- const toCamelAction = (s) => s.split("-").map(p => p.charAt(0).toUpperCase() + p.slice(1)).join("");
485
- const controllerName = toPascal(controllerId) + "Controller";
486
- const actionMethod = "action" + toCamelAction(actionId);
487
- // Find controller class in index
488
- const controllerSymbol = index.symbols.find((s) => s.name === controllerName && s.kind === "class");
489
- if (!controllerSymbol) {
490
- // Fallback: try urlManager rules from config/web.php
491
- return findYii2HandlersFromConfig(index, searchPath);
492
- }
493
- // Find action method within the controller
494
- const actionSymbol = index.symbols.find((s) => s.name === actionMethod && s.parent === controllerSymbol.id);
495
- if (actionSymbol) {
496
- handlers.push({
497
- symbol: stripSource(actionSymbol),
498
- file: actionSymbol.file,
499
- method: "GET",
500
- framework: "yii2",
501
- });
502
- }
503
- else {
504
- // Fallback: controller found but action method not indexed — report controller
505
- handlers.push({
506
- symbol: stripSource(controllerSymbol),
507
- file: controllerSymbol.file,
508
- framework: "yii2",
509
- });
510
- }
511
- return handlers;
512
- }
513
- /**
514
- * Fallback: parse Yii2 urlManager rules from config/web.php.
515
- * Matches patterns like: 'GET api/users/<id>' => 'user/view'
516
- */
517
- async function findYii2HandlersFromConfig(index, searchPath) {
518
- const handlers = [];
519
- const configFile = index.files.find((f) => /config\/web\.php$/.test(f.path));
520
- if (!configFile)
521
- return handlers;
522
- const { readFile: rf } = await import("node:fs/promises");
523
- const { join: j } = await import("node:path");
524
- let source;
525
- try {
526
- source = await rf(j(index.root, configFile.path), "utf-8");
527
- }
528
- catch {
529
- return handlers;
530
- }
531
- const normalized = searchPath.replace(/^\/|\/$/g, "").toLowerCase();
532
- // Match: 'route/pattern' => 'controller/action' or ['GET method/pattern'] => 'controller/action'
533
- const ruleRe = /['"](?:(?:GET|POST|PUT|DELETE|PATCH)\s+)?([^'"]+)['"]\s*=>\s*['"]([^'"]+)['"]/g;
534
- let match;
535
- while ((match = ruleRe.exec(source)) !== null) {
536
- const rulePattern = match[1].replace(/<\w+(?::[^>]+)?>/g, "[param]").toLowerCase();
537
- if (!matchPath(rulePattern, normalized))
538
- continue;
539
- const route = match[2]; // e.g. "user/view"
540
- const parts = route.split("/");
541
- if (parts.length < 2)
542
- continue;
543
- const controllerId = parts[parts.length - 2];
544
- const actionId = parts[parts.length - 1];
545
- const toPascal = (s) => s.split("-").map(p => p.charAt(0).toUpperCase() + p.slice(1)).join("");
546
- const controllerName = toPascal(controllerId) + "Controller";
547
- const actionMethod = "action" + toPascal(actionId);
548
- const ctrlSym = index.symbols.find(s => s.name === controllerName && s.kind === "class");
549
- if (!ctrlSym)
550
- continue;
551
- const actionSym = index.symbols.find(s => s.name === actionMethod && s.parent === ctrlSym.id);
552
- handlers.push({
553
- symbol: stripSource(actionSym ?? ctrlSym),
554
- file: (actionSym ?? ctrlSym).file,
555
- method: "GET",
556
- framework: "yii2",
557
- });
558
- }
559
- return handlers;
560
- }
561
- /**
562
- * Find Laravel route handlers by scanning route files for Route::method() patterns.
563
- */
564
- async function findLaravelHandlers(index, searchPath) {
565
- const handlers = [];
566
- const routeFiles = index.files.filter((f) => /routes\/(web|api)\.php$/.test(f.path));
567
- if (routeFiles.length === 0)
568
- return handlers;
569
- const { readFile } = await import("node:fs/promises");
570
- const { join } = await import("node:path");
571
- const methods = ["get", "post", "put", "delete", "patch"];
572
- for (const file of routeFiles) {
573
- let source;
574
- try {
575
- source = await readFile(join(index.root, file.path), "utf-8");
576
- }
577
- catch {
578
- continue;
579
- }
580
- for (const method of methods) {
581
- // Match: Route::get('/path', [Controller::class, 'method']) or Route::get('/path', 'Controller@method')
582
- const re = new RegExp(`Route::${method}\\s*\\(\\s*['"\`]([^'"\`]+)['"\`]\\s*,\\s*(?:\\[([\\w\\\\]+)::class\\s*,\\s*['"\`](\\w+)['"\`]\\]|['"\`](\\w+)@(\\w+)['"\`])`, "gi");
583
- let match;
584
- while ((match = re.exec(source)) !== null) {
585
- const routePath = match[1] ?? "";
586
- const controllerClass = match[2] ?? match[4] ?? "";
587
- const methodName = match[3] ?? match[5] ?? "";
588
- if (!matchPath(routePath, searchPath))
589
- continue;
590
- // Find the controller method in the index
591
- const controllerName = controllerClass.split("\\").pop() ?? controllerClass;
592
- const sym = index.symbols.find((s) => s.name === methodName && s.kind === "method" &&
593
- index.symbols.some((c) => c.id === s.parent && c.name === controllerName));
594
- handlers.push({
595
- symbol: sym
596
- ? stripSource(sym)
597
- : { id: `${controllerName}::${methodName}`, name: methodName, kind: "method", file: file.path, start_line: 0, end_line: 0 },
598
- file: sym?.file ?? file.path,
599
- method: method.toUpperCase(),
600
- framework: "laravel",
601
- });
602
- }
603
- }
604
- }
605
- return handlers;
606
- }
607
- /**
608
- * Find Ktor route handlers via `routing { get("/path") { ... } }` DSL.
609
- * Supports nested `route("/prefix") { get("/sub") { } }` patterns.
610
- */
611
- async function findKtorHandlers(index, searchPath) {
612
- const handlers = [];
613
- const methods = ["get", "post", "put", "delete", "patch", "head", "options"];
614
- // Ktor handlers are in .kt files, typically in files containing "routing {" or with "Route" in name
615
- const kotlinFiles = index.files.filter((f) => /\.kts?$/.test(f.path));
616
- if (kotlinFiles.length === 0)
617
- return handlers;
618
- const { readFile } = await import("node:fs/promises");
619
- const { join } = await import("node:path");
620
- for (const file of kotlinFiles) {
621
- let source;
622
- try {
623
- source = await readFile(join(index.root, file.path), "utf-8");
624
- }
625
- catch {
626
- continue;
627
- }
628
- // Skip files without routing DSL
629
- if (!/\b(routing|route)\s*[({]/.test(source))
630
- continue;
631
- // Extract route("/prefix") blocks to support nested prefixes
632
- // Simple approach: find all method calls with path args, combine with enclosing route() prefix via line scan
633
- const lines = source.split("\n");
634
- const prefixStack = [];
635
- let braceDepth = 0;
636
- for (let i = 0; i < lines.length; i++) {
637
- const line = lines[i];
638
- // Track route("/prefix") { ... } blocks
639
- const routeMatch = /\broute\s*\(\s*["']([^"']+)["']\s*\)\s*\{/.exec(line);
640
- if (routeMatch) {
641
- prefixStack.push({ prefix: routeMatch[1], braceDepth });
642
- }
643
- // Count braces to detect when route() scope closes
644
- for (const ch of line) {
645
- if (ch === "{")
646
- braceDepth++;
647
- else if (ch === "}") {
648
- braceDepth--;
649
- // Pop route prefixes whose scope ended
650
- while (prefixStack.length > 0 &&
651
- prefixStack[prefixStack.length - 1].braceDepth >= braceDepth) {
652
- prefixStack.pop();
653
- }
654
- }
655
- }
656
- // Match method handlers: get("/path") { ... } or post("/path") { ... }
657
- for (const method of methods) {
658
- const re = new RegExp(`\\b${method}\\s*\\(\\s*["']([^"']+)["']\\s*\\)\\s*\\{`);
659
- const match = re.exec(line);
660
- if (!match)
661
- continue;
662
- const methodPath = match[1];
663
- const prefix = prefixStack.map((p) => p.prefix).join("");
664
- const fullPath = `${prefix}/${methodPath}`.replace(/\/+/g, "/");
665
- if (!matchPath(fullPath, searchPath))
666
- continue;
667
- // Find enclosing function symbol (if any) for this line
668
- const lineNum = i + 1;
669
- const sym = index.symbols.find((s) => s.file === file.path && s.start_line <= lineNum && s.end_line >= lineNum);
670
- handlers.push({
671
- symbol: sym
672
- ? stripSource(sym)
673
- : {
674
- id: `${file.path}:${method}:${methodPath}`,
675
- name: `${method} ${methodPath}`,
676
- kind: "function",
677
- file: file.path,
678
- start_line: lineNum,
679
- end_line: lineNum,
680
- },
681
- file: file.path,
682
- method: method.toUpperCase(),
683
- framework: "ktor",
684
- });
685
- }
686
- }
687
- }
688
- return handlers;
689
- }
690
- /**
691
- * Find Spring Boot Kotlin route handlers via @RestController/@Controller + @GetMapping/etc.
692
- */
693
- async function findSpringBootKotlinHandlers(index, searchPath) {
694
- const handlers = [];
695
- const mappingAnnotations = [
696
- { ann: "GetMapping", method: "GET" },
697
- { ann: "PostMapping", method: "POST" },
698
- { ann: "PutMapping", method: "PUT" },
699
- { ann: "DeleteMapping", method: "DELETE" },
700
- { ann: "PatchMapping", method: "PATCH" },
701
- ];
702
- const kotlinFiles = index.files.filter((f) => /\.kts?$/.test(f.path));
703
- if (kotlinFiles.length === 0)
704
- return handlers;
705
- const { readFile } = await import("node:fs/promises");
706
- const { join } = await import("node:path");
707
- for (const file of kotlinFiles) {
708
- let source;
709
- try {
710
- source = await readFile(join(index.root, file.path), "utf-8");
711
- }
712
- catch {
713
- continue;
714
- }
715
- // Must have @RestController or @Controller annotation
716
- if (!/@(?:RestController|Controller)\b/.test(source))
717
- continue;
718
- // Extract class-level @RequestMapping prefix (optional)
719
- const classRequestMatch = /@RequestMapping\s*\(\s*(?:value\s*=\s*)?["']([^"']*)["']/.exec(source);
720
- const classPrefix = classRequestMatch?.[1] ?? "";
721
- for (const { ann, method } of mappingAnnotations) {
722
- // Match: @GetMapping("/path") fun funcName(...)
723
- // Or: @GetMapping(value = "/path") fun funcName(...)
724
- const re = new RegExp(`@${ann}\\s*\\(\\s*(?:value\\s*=\\s*)?["']([^"']*)["'](?:[^)]*)?\\)\\s*(?:fun|\\n\\s*fun)\\s+(\\w+)`, "g");
725
- let match;
726
- while ((match = re.exec(source)) !== null) {
727
- const routePath = match[1] ?? "";
728
- const funcName = match[2] ?? "";
729
- const fullPath = `${classPrefix}/${routePath}`.replace(/\/+/g, "/");
730
- if (!matchPath(fullPath, searchPath))
731
- continue;
732
- const sym = index.symbols.find((s) => s.file === file.path && s.name === funcName);
733
- handlers.push({
734
- symbol: sym
735
- ? stripSource(sym)
736
- : {
737
- id: `${file.path}:${funcName}`,
738
- name: funcName,
739
- kind: "method",
740
- file: file.path,
741
- start_line: 1,
742
- end_line: 1,
743
- },
744
- file: file.path,
745
- method,
746
- framework: "spring-kotlin",
747
- });
748
- }
749
- }
750
- }
751
- return handlers;
752
- }
753
- // --- Python frameworks ---
754
- /**
755
- * Detect Python test files by pytest naming conventions.
756
- * Matches: test_*.py, *_test.py, conftest.py, and tests/ subdirectories.
757
- */
758
- function isPythonTestFile(path) {
759
- const basename = path.split("/").pop() ?? path;
760
- if (basename === "conftest.py")
761
- return true;
762
- if (/^test_.*\.py$/.test(basename))
763
- return true;
764
- if (/_test\.py$/.test(basename))
765
- return true;
766
- if (/\/tests?\//.test(path))
767
- return true;
768
- return false;
769
- }
770
- /**
771
- * Find Flask route handlers via @app.route() and @bp.route() decorators.
772
- * Also handles @app.get/post/put/delete() (Flask 2.0+ shorthand).
773
- */
774
- function findFlaskHandlers(index, searchPath) {
775
- const handlers = [];
776
- // Exclude test files and conftest — users tracing a production route rarely
777
- // want test fixture routes like `@app.route('/')` inside test_*.py
778
- const pyFiles = index.files.filter((f) => f.path.endsWith(".py")
779
- && !isPythonTestFile(f.path));
780
- for (const file of pyFiles) {
781
- const fileSymbols = index.symbols.filter((s) => s.file === file.path);
782
- for (const sym of fileSymbols) {
783
- if (!sym.decorators || sym.decorators.length === 0)
784
- continue;
785
- for (const dec of sym.decorators) {
786
- // Match @app.route('/path') or @bp.route('/path') — Flask-specific.
787
- // For HTTP verb shortcuts (@app.get, @app.post), prefer FastAPI handler
788
- // since the syntax is ambiguous between Flask 2.0+ and FastAPI.
789
- const routeMatch = dec.match(/@\w+\.route\s*\(\s*['"]([^'"]*)['"]/);
790
- if (!routeMatch)
791
- continue;
792
- const routePath = routeMatch[1] ?? "";
793
- if (!matchPath(routePath, searchPath))
794
- continue;
795
- // @app.route can handle any method — omit method field
796
- handlers.push({
797
- symbol: stripSource(sym),
798
- file: file.path,
799
- framework: "flask",
800
- });
801
- }
802
- }
803
- }
804
- return handlers;
805
- }
806
- /**
807
- * Find FastAPI route handlers via @app.get/post/put/delete() and @router.get() decorators.
808
- * Handles APIRouter prefix extraction.
809
- */
810
- function findFastAPIHandlers(index, searchPath) {
811
- const handlers = [];
812
- const pyFiles = index.files.filter((f) => f.path.endsWith(".py")
813
- && !isPythonTestFile(f.path));
814
- for (const file of pyFiles) {
815
- const fileSymbols = index.symbols.filter((s) => s.file === file.path);
816
- for (const sym of fileSymbols) {
817
- if (!sym.decorators || sym.decorators.length === 0)
818
- continue;
819
- for (const dec of sym.decorators) {
820
- // Match @app.get('/path') or @router.get('/path') etc.
821
- const routeMatch = dec.match(/@\w+\.(get|post|put|delete|patch|options|head)\s*\(\s*['"]([^'"]*)['"]/);
822
- if (!routeMatch)
823
- continue;
824
- const method = routeMatch[1].toUpperCase();
825
- const routePath = routeMatch[2] ?? "";
826
- if (!matchPath(routePath, searchPath))
827
- continue;
828
- handlers.push({
829
- symbol: stripSource(sym),
830
- file: file.path,
831
- method,
832
- framework: "fastapi",
833
- });
834
- }
835
- }
836
- }
837
- return handlers;
838
- }
839
- /**
840
- * Find Django route handlers by parsing urlpatterns in urls.py files.
841
- * Handles path(), re_path(), and include() chains.
842
- */
843
- async function findDjangoHandlers(index, searchPath) {
844
- const handlers = [];
845
- const { readFile } = await import("node:fs/promises");
846
- const { join } = await import("node:path");
847
- // Find urls.py files
848
- const urlFiles = index.files.filter((f) => f.path.endsWith("urls.py"));
849
- for (const file of urlFiles) {
850
- let source;
851
- try {
852
- source = await readFile(join(index.root, file.path), "utf-8");
853
- }
854
- catch {
855
- continue;
856
- }
857
- // Extract path() patterns: path('users/', views.user_list, name='...')
858
- // and path('users/<int:pk>/', views.user_detail)
859
- const pathRe = /path\s*\(\s*['"]([^'"]*)['"]\s*,\s*([\w.]+)/g;
860
- let match;
861
- // Get the URL prefix from the file's directory context
862
- // e.g., if this urls.py is included from a parent with prefix 'api/'
863
- while ((match = pathRe.exec(source)) !== null) {
864
- const routePath = match[1] ?? "";
865
- const viewRef = match[2] ?? "";
866
- // Skip include() references
867
- if (viewRef === "include")
868
- continue;
869
- // Convert Django <type:name> to :name for matchPath
870
- const normalizedPath = `/${routePath}`.replace(/<\w+:(\w+)>/g, ":$1").replace(/\/+/g, "/");
871
- if (!matchPath(normalizedPath, searchPath))
872
- continue;
873
- // Resolve view reference to a symbol
874
- const viewName = viewRef.split(".").pop() ?? viewRef;
875
- const sym = index.symbols.find((s) => s.name === viewName && s.file.endsWith(".py"));
876
- handlers.push({
877
- symbol: sym
878
- ? stripSource(sym)
879
- : {
880
- id: `${file.path}:${viewName}`,
881
- name: viewName,
882
- kind: "function",
883
- file: file.path,
884
- start_line: 1,
885
- end_line: 1,
886
- },
887
- file: sym?.file ?? file.path,
888
- framework: "django",
889
- });
890
- }
891
- }
892
- return handlers;
893
- }
894
- /**
895
- * Trace an HTTP route: find handler, trace callees, identify DB calls.
896
- */
897
- export async function traceRoute(repo, path, outputFormat) {
898
- const index = await getCodeIndex(repo);
899
- if (!index)
900
- throw new Error(`Repository "${repo}" not found.`);
901
- // Try all frameworks
902
- const astroHandlers = findAstroHandlers(index, path);
903
- const handlers = [
904
- ...(await findNestJSHandlers(index, path)),
905
- ...findNextJSHandlers(index, path),
906
- ...findPagesRouterHandlers(index, path),
907
- ...findExpressHandlers(index, path),
908
- ...(await findHonoHandlers(repo, index, path)),
909
- ...(await findYii2Handlers(index, path)),
910
- ...(await findLaravelHandlers(index, path)),
911
- ...(await findKtorHandlers(index, path)),
912
- ...(await findSpringBootKotlinHandlers(index, path)),
913
- ...astroHandlers,
914
- ...findFastAPIHandlers(index, path),
915
- ...findFlaskHandlers(index, path),
916
- ...(await findDjangoHandlers(index, path)),
917
- ];
918
- if (handlers.length === 0) {
919
- return { path, handlers: [], call_chain: [], db_calls: [] };
920
- }
921
- // Trace callees from handler symbols
922
- const adjacency = buildAdjacencyIndex(index.symbols, false);
923
- const callChain = [];
924
- const allCalleeSymbols = [];
925
- for (const handler of handlers) {
926
- // Find the full symbol in index.
927
- // handler.symbol has a stripped ID (no repo prefix, from stripSource), so match
928
- // by file + name + start_line instead of id to avoid the prefix mismatch.
929
- const fullSym = index.symbols.find((s) => s.file === handler.symbol.file &&
930
- s.name === handler.symbol.name &&
931
- s.start_line === handler.symbol.start_line);
932
- if (!fullSym)
933
- continue;
934
- const tree = buildCallTree(fullSym, adjacency, "callees", 3);
935
- // Flatten tree
936
- function flatten(node, depth) {
937
- callChain.push({ name: node.symbol.name, file: node.symbol.file, kind: node.symbol.kind, depth });
938
- allCalleeSymbols.push(node.symbol);
939
- for (const child of node.children) {
940
- flatten(child, depth + 1);
941
- }
942
- }
943
- flatten(tree, 0);
944
- }
945
- const dbCalls = findDbCalls(allCalleeSymbols);
946
- const result = { path, handlers, call_chain: callChain, db_calls: dbCalls };
947
- // Next.js-specific: layout chain, middleware, and server actions tracing
948
- const hasNextjsHandler = handlers.some((h) => h.framework === "nextjs");
949
- if (hasNextjsHandler) {
950
- const repoRoot = index.root;
951
- // Layout chain from the first handler's file
952
- const firstFile = handlers[0]?.file;
953
- if (firstFile) {
954
- try {
955
- result.layout_chain = await computeLayoutChain(firstFile, repoRoot);
956
- }
957
- catch {
958
- result.layout_chain = [];
959
- }
960
- }
961
- else {
962
- result.layout_chain = [];
963
- }
964
- // Middleware tracing
965
- try {
966
- const mw = await traceMiddleware(repoRoot, path);
967
- if (mw) {
968
- result.middleware = mw;
969
- }
970
- }
971
- catch {
972
- // Middleware tracing failed — skip
973
- }
974
- // Server actions detection
975
- result.server_actions = await findServerActions(repoRoot, allCalleeSymbols, callChain);
976
- }
977
- if (outputFormat === "mermaid") {
978
- return { mermaid: routeToMermaid(result) };
979
- }
980
- return result;
981
- }
6
+ export { findNestJSHandlers } from "./route-tools/nest.js";
7
+ export { routeToMermaid } from "./route-tools/route-mermaid.js";
8
+ export { traceRoute } from "./route-tools/trace-route.js";
982
9
  //# sourceMappingURL=route-tools.js.map