codesift-mcp 0.3.0 → 0.5.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 (543) hide show
  1. package/README.md +215 -23
  2. package/dist/cache/hono-cache.d.ts +50 -0
  3. package/dist/cache/hono-cache.d.ts.map +1 -0
  4. package/dist/cache/hono-cache.js +132 -0
  5. package/dist/cache/hono-cache.js.map +1 -0
  6. package/dist/cli/help.d.ts.map +1 -1
  7. package/dist/cli/help.js +8 -6
  8. package/dist/cli/help.js.map +1 -1
  9. package/dist/cli/platform.d.ts.map +1 -1
  10. package/dist/cli/platform.js +12 -14
  11. package/dist/cli/platform.js.map +1 -1
  12. package/dist/cli/setup.d.ts +1 -1
  13. package/dist/cli/setup.d.ts.map +1 -1
  14. package/dist/cli/setup.js +27 -3
  15. package/dist/cli/setup.js.map +1 -1
  16. package/dist/formatters-shortening.d.ts +13 -0
  17. package/dist/formatters-shortening.d.ts.map +1 -1
  18. package/dist/formatters-shortening.js +131 -0
  19. package/dist/formatters-shortening.js.map +1 -1
  20. package/dist/formatters.d.ts +38 -0
  21. package/dist/formatters.d.ts.map +1 -1
  22. package/dist/formatters.js +521 -0
  23. package/dist/formatters.js.map +1 -1
  24. package/dist/instructions.d.ts +1 -1
  25. package/dist/instructions.d.ts.map +1 -1
  26. package/dist/instructions.js +39 -38
  27. package/dist/instructions.js.map +1 -1
  28. package/dist/lsp/lsp-servers.d.ts.map +1 -1
  29. package/dist/lsp/lsp-servers.js +5 -0
  30. package/dist/lsp/lsp-servers.js.map +1 -1
  31. package/dist/lsp/lsp-tools.d.ts.map +1 -1
  32. package/dist/lsp/lsp-tools.js +1 -0
  33. package/dist/lsp/lsp-tools.js.map +1 -1
  34. package/dist/parser/astro-template.d.ts +47 -0
  35. package/dist/parser/astro-template.d.ts.map +1 -0
  36. package/dist/parser/astro-template.js +171 -0
  37. package/dist/parser/astro-template.js.map +1 -0
  38. package/dist/parser/extractors/_shared.d.ts +4 -0
  39. package/dist/parser/extractors/_shared.d.ts.map +1 -1
  40. package/dist/parser/extractors/_shared.js +8 -0
  41. package/dist/parser/extractors/_shared.js.map +1 -1
  42. package/dist/parser/extractors/astro.d.ts +4 -5
  43. package/dist/parser/extractors/astro.d.ts.map +1 -1
  44. package/dist/parser/extractors/astro.js +102 -26
  45. package/dist/parser/extractors/astro.js.map +1 -1
  46. package/dist/parser/extractors/gradle-kts.d.ts +4 -0
  47. package/dist/parser/extractors/gradle-kts.d.ts.map +1 -0
  48. package/dist/parser/extractors/gradle-kts.js +246 -0
  49. package/dist/parser/extractors/gradle-kts.js.map +1 -0
  50. package/dist/parser/extractors/hono-inline-analyzer.d.ts +34 -0
  51. package/dist/parser/extractors/hono-inline-analyzer.d.ts.map +1 -0
  52. package/dist/parser/extractors/hono-inline-analyzer.js +465 -0
  53. package/dist/parser/extractors/hono-inline-analyzer.js.map +1 -0
  54. package/dist/parser/extractors/hono-model.d.ts +196 -0
  55. package/dist/parser/extractors/hono-model.d.ts.map +1 -0
  56. package/dist/parser/extractors/hono-model.js +10 -0
  57. package/dist/parser/extractors/hono-model.js.map +1 -0
  58. package/dist/parser/extractors/hono.d.ts +118 -0
  59. package/dist/parser/extractors/hono.d.ts.map +1 -0
  60. package/dist/parser/extractors/hono.js +1527 -0
  61. package/dist/parser/extractors/hono.js.map +1 -0
  62. package/dist/parser/extractors/kotlin.d.ts +4 -0
  63. package/dist/parser/extractors/kotlin.d.ts.map +1 -0
  64. package/dist/parser/extractors/kotlin.js +521 -0
  65. package/dist/parser/extractors/kotlin.js.map +1 -0
  66. package/dist/parser/extractors/php.d.ts +22 -0
  67. package/dist/parser/extractors/php.d.ts.map +1 -0
  68. package/dist/parser/extractors/php.js +334 -0
  69. package/dist/parser/extractors/php.js.map +1 -0
  70. package/dist/parser/extractors/python.d.ts.map +1 -1
  71. package/dist/parser/extractors/python.js +234 -11
  72. package/dist/parser/extractors/python.js.map +1 -1
  73. package/dist/parser/extractors/sql.d.ts +33 -0
  74. package/dist/parser/extractors/sql.d.ts.map +1 -0
  75. package/dist/parser/extractors/sql.js +506 -0
  76. package/dist/parser/extractors/sql.js.map +1 -0
  77. package/dist/parser/extractors/typescript.d.ts.map +1 -1
  78. package/dist/parser/extractors/typescript.js +209 -3
  79. package/dist/parser/extractors/typescript.js.map +1 -1
  80. package/dist/parser/languages/tree-sitter-javascript.wasm +0 -0
  81. package/dist/parser/languages/tree-sitter-kotlin.wasm +0 -0
  82. package/dist/parser/languages/tree-sitter-php.wasm +0 -0
  83. package/dist/parser/languages/tree-sitter-php_only.wasm +0 -0
  84. package/dist/parser/languages/tree-sitter-python.wasm +0 -0
  85. package/dist/parser/parse-cache.d.ts +39 -0
  86. package/dist/parser/parse-cache.d.ts.map +1 -0
  87. package/dist/parser/parse-cache.js +87 -0
  88. package/dist/parser/parse-cache.js.map +1 -0
  89. package/dist/parser/parser-manager.d.ts +32 -0
  90. package/dist/parser/parser-manager.d.ts.map +1 -1
  91. package/dist/parser/parser-manager.js +93 -3
  92. package/dist/parser/parser-manager.js.map +1 -1
  93. package/dist/parser/symbol-extractor.d.ts.map +1 -1
  94. package/dist/parser/symbol-extractor.js +16 -0
  95. package/dist/parser/symbol-extractor.js.map +1 -1
  96. package/dist/register-tools.d.ts +38 -2
  97. package/dist/register-tools.d.ts.map +1 -1
  98. package/dist/register-tools.js +2444 -195
  99. package/dist/register-tools.js.map +1 -1
  100. package/dist/search/reranker.js +1 -1
  101. package/dist/search/reranker.js.map +1 -1
  102. package/dist/search/tool-ranker.d.ts +90 -0
  103. package/dist/search/tool-ranker.d.ts.map +1 -0
  104. package/dist/search/tool-ranker.js +420 -0
  105. package/dist/search/tool-ranker.js.map +1 -0
  106. package/dist/server-helpers.d.ts.map +1 -1
  107. package/dist/server-helpers.js +11 -0
  108. package/dist/server-helpers.js.map +1 -1
  109. package/dist/server.js +47 -14
  110. package/dist/server.js.map +1 -1
  111. package/dist/storage/index-store.d.ts +15 -1
  112. package/dist/storage/index-store.d.ts.map +1 -1
  113. package/dist/storage/index-store.js +27 -1
  114. package/dist/storage/index-store.js.map +1 -1
  115. package/dist/storage/session-state.d.ts +1 -1
  116. package/dist/storage/session-state.d.ts.map +1 -1
  117. package/dist/storage/session-state.js +6 -4
  118. package/dist/storage/session-state.js.map +1 -1
  119. package/dist/storage/usage-tracker.d.ts.map +1 -1
  120. package/dist/storage/usage-tracker.js +4 -1
  121. package/dist/storage/usage-tracker.js.map +1 -1
  122. package/dist/tools/agent-config-tools.d.ts +24 -0
  123. package/dist/tools/agent-config-tools.d.ts.map +1 -0
  124. package/dist/tools/agent-config-tools.js +119 -0
  125. package/dist/tools/agent-config-tools.js.map +1 -0
  126. package/dist/tools/architecture-tools.d.ts +23 -0
  127. package/dist/tools/architecture-tools.d.ts.map +1 -0
  128. package/dist/tools/architecture-tools.js +140 -0
  129. package/dist/tools/architecture-tools.js.map +1 -0
  130. package/dist/tools/astro-actions.d.ts +54 -0
  131. package/dist/tools/astro-actions.d.ts.map +1 -0
  132. package/dist/tools/astro-actions.js +561 -0
  133. package/dist/tools/astro-actions.js.map +1 -0
  134. package/dist/tools/astro-audit.d.ts +87 -0
  135. package/dist/tools/astro-audit.d.ts.map +1 -0
  136. package/dist/tools/astro-audit.js +345 -0
  137. package/dist/tools/astro-audit.js.map +1 -0
  138. package/dist/tools/astro-config.d.ts +33 -0
  139. package/dist/tools/astro-config.d.ts.map +1 -0
  140. package/dist/tools/astro-config.js +260 -0
  141. package/dist/tools/astro-config.js.map +1 -0
  142. package/dist/tools/astro-content-collections.d.ts +44 -0
  143. package/dist/tools/astro-content-collections.d.ts.map +1 -0
  144. package/dist/tools/astro-content-collections.js +630 -0
  145. package/dist/tools/astro-content-collections.js.map +1 -0
  146. package/dist/tools/astro-islands.d.ts +63 -0
  147. package/dist/tools/astro-islands.d.ts.map +1 -0
  148. package/dist/tools/astro-islands.js +255 -0
  149. package/dist/tools/astro-islands.js.map +1 -0
  150. package/dist/tools/astro-migration.d.ts +31 -0
  151. package/dist/tools/astro-migration.d.ts.map +1 -0
  152. package/dist/tools/astro-migration.js +378 -0
  153. package/dist/tools/astro-migration.js.map +1 -0
  154. package/dist/tools/astro-routes.d.ts +49 -0
  155. package/dist/tools/astro-routes.d.ts.map +1 -0
  156. package/dist/tools/astro-routes.js +119 -0
  157. package/dist/tools/astro-routes.js.map +1 -0
  158. package/dist/tools/async-correctness.d.ts +26 -0
  159. package/dist/tools/async-correctness.d.ts.map +1 -0
  160. package/dist/tools/async-correctness.js +166 -0
  161. package/dist/tools/async-correctness.js.map +1 -0
  162. package/dist/tools/audit-tools.d.ts +38 -0
  163. package/dist/tools/audit-tools.d.ts.map +1 -0
  164. package/dist/tools/audit-tools.js +248 -0
  165. package/dist/tools/audit-tools.js.map +1 -0
  166. package/dist/tools/celery-tools.d.ts +38 -0
  167. package/dist/tools/celery-tools.d.ts.map +1 -0
  168. package/dist/tools/celery-tools.js +154 -0
  169. package/dist/tools/celery-tools.js.map +1 -0
  170. package/dist/tools/clone-tools.js +1 -1
  171. package/dist/tools/clone-tools.js.map +1 -1
  172. package/dist/tools/complexity-tools.d.ts +4 -0
  173. package/dist/tools/complexity-tools.d.ts.map +1 -1
  174. package/dist/tools/complexity-tools.js +78 -4
  175. package/dist/tools/complexity-tools.js.map +1 -1
  176. package/dist/tools/compose-tools.d.ts +60 -0
  177. package/dist/tools/compose-tools.d.ts.map +1 -0
  178. package/dist/tools/compose-tools.js +203 -0
  179. package/dist/tools/compose-tools.js.map +1 -0
  180. package/dist/tools/coupling-tools.d.ts +50 -0
  181. package/dist/tools/coupling-tools.d.ts.map +1 -0
  182. package/dist/tools/coupling-tools.js +262 -0
  183. package/dist/tools/coupling-tools.js.map +1 -0
  184. package/dist/tools/dependency-audit-tools.d.ts +65 -0
  185. package/dist/tools/dependency-audit-tools.d.ts.map +1 -0
  186. package/dist/tools/dependency-audit-tools.js +553 -0
  187. package/dist/tools/dependency-audit-tools.js.map +1 -0
  188. package/dist/tools/django-settings.d.ts +22 -0
  189. package/dist/tools/django-settings.d.ts.map +1 -0
  190. package/dist/tools/django-settings.js +301 -0
  191. package/dist/tools/django-settings.js.map +1 -0
  192. package/dist/tools/django-view-security-tools.d.ts +32 -0
  193. package/dist/tools/django-view-security-tools.d.ts.map +1 -0
  194. package/dist/tools/django-view-security-tools.js +184 -0
  195. package/dist/tools/django-view-security-tools.js.map +1 -0
  196. package/dist/tools/fastapi-depends.d.ts +63 -0
  197. package/dist/tools/fastapi-depends.d.ts.map +1 -0
  198. package/dist/tools/fastapi-depends.js +191 -0
  199. package/dist/tools/fastapi-depends.js.map +1 -0
  200. package/dist/tools/frequency-tools.js +1 -1
  201. package/dist/tools/frequency-tools.js.map +1 -1
  202. package/dist/tools/graph-tools.d.ts +8 -2
  203. package/dist/tools/graph-tools.d.ts.map +1 -1
  204. package/dist/tools/graph-tools.js +44 -3
  205. package/dist/tools/graph-tools.js.map +1 -1
  206. package/dist/tools/hilt-tools.d.ts +55 -0
  207. package/dist/tools/hilt-tools.d.ts.map +1 -0
  208. package/dist/tools/hilt-tools.js +258 -0
  209. package/dist/tools/hilt-tools.js.map +1 -0
  210. package/dist/tools/hono-analyze-app.d.ts +48 -0
  211. package/dist/tools/hono-analyze-app.d.ts.map +1 -0
  212. package/dist/tools/hono-analyze-app.js +94 -0
  213. package/dist/tools/hono-analyze-app.js.map +1 -0
  214. package/dist/tools/hono-api-contract.d.ts +22 -0
  215. package/dist/tools/hono-api-contract.d.ts.map +1 -0
  216. package/dist/tools/hono-api-contract.js +112 -0
  217. package/dist/tools/hono-api-contract.js.map +1 -0
  218. package/dist/tools/hono-conditional-middleware.d.ts +27 -0
  219. package/dist/tools/hono-conditional-middleware.d.ts.map +1 -0
  220. package/dist/tools/hono-conditional-middleware.js +62 -0
  221. package/dist/tools/hono-conditional-middleware.js.map +1 -0
  222. package/dist/tools/hono-context-flow.d.ts +24 -0
  223. package/dist/tools/hono-context-flow.d.ts.map +1 -0
  224. package/dist/tools/hono-context-flow.js +70 -0
  225. package/dist/tools/hono-context-flow.js.map +1 -0
  226. package/dist/tools/hono-dead-routes.d.ts +26 -0
  227. package/dist/tools/hono-dead-routes.d.ts.map +1 -0
  228. package/dist/tools/hono-dead-routes.js +102 -0
  229. package/dist/tools/hono-dead-routes.js.map +1 -0
  230. package/dist/tools/hono-entry-resolver.d.ts +27 -0
  231. package/dist/tools/hono-entry-resolver.d.ts.map +1 -0
  232. package/dist/tools/hono-entry-resolver.js +31 -0
  233. package/dist/tools/hono-entry-resolver.js.map +1 -0
  234. package/dist/tools/hono-env-regression.d.ts +29 -0
  235. package/dist/tools/hono-env-regression.d.ts.map +1 -0
  236. package/dist/tools/hono-env-regression.js +157 -0
  237. package/dist/tools/hono-env-regression.js.map +1 -0
  238. package/dist/tools/hono-inline-analyze.d.ts +31 -0
  239. package/dist/tools/hono-inline-analyze.d.ts.map +1 -0
  240. package/dist/tools/hono-inline-analyze.js +59 -0
  241. package/dist/tools/hono-inline-analyze.js.map +1 -0
  242. package/dist/tools/hono-middleware-chain.d.ts +40 -0
  243. package/dist/tools/hono-middleware-chain.d.ts.map +1 -0
  244. package/dist/tools/hono-middleware-chain.js +121 -0
  245. package/dist/tools/hono-middleware-chain.js.map +1 -0
  246. package/dist/tools/hono-modules.d.ts +22 -0
  247. package/dist/tools/hono-modules.d.ts.map +1 -0
  248. package/dist/tools/hono-modules.js +118 -0
  249. package/dist/tools/hono-modules.js.map +1 -0
  250. package/dist/tools/hono-response-types.d.ts +37 -0
  251. package/dist/tools/hono-response-types.d.ts.map +1 -0
  252. package/dist/tools/hono-response-types.js +76 -0
  253. package/dist/tools/hono-response-types.js.map +1 -0
  254. package/dist/tools/hono-rpc-types.d.ts +21 -0
  255. package/dist/tools/hono-rpc-types.d.ts.map +1 -0
  256. package/dist/tools/hono-rpc-types.js +49 -0
  257. package/dist/tools/hono-rpc-types.js.map +1 -0
  258. package/dist/tools/hono-security.d.ts +31 -0
  259. package/dist/tools/hono-security.d.ts.map +1 -0
  260. package/dist/tools/hono-security.js +269 -0
  261. package/dist/tools/hono-security.js.map +1 -0
  262. package/dist/tools/hono-visualize.d.ts +13 -0
  263. package/dist/tools/hono-visualize.d.ts.map +1 -0
  264. package/dist/tools/hono-visualize.js +64 -0
  265. package/dist/tools/hono-visualize.js.map +1 -0
  266. package/dist/tools/hotspot-tools.d.ts.map +1 -1
  267. package/dist/tools/hotspot-tools.js +9 -7
  268. package/dist/tools/hotspot-tools.js.map +1 -1
  269. package/dist/tools/index-tools.d.ts +17 -0
  270. package/dist/tools/index-tools.d.ts.map +1 -1
  271. package/dist/tools/index-tools.js +210 -10
  272. package/dist/tools/index-tools.js.map +1 -1
  273. package/dist/tools/kotlin-tools.d.ts +142 -0
  274. package/dist/tools/kotlin-tools.d.ts.map +1 -0
  275. package/dist/tools/kotlin-tools.js +572 -0
  276. package/dist/tools/kotlin-tools.js.map +1 -0
  277. package/dist/tools/legacy-hono-conventions.d.ts +14 -0
  278. package/dist/tools/legacy-hono-conventions.d.ts.map +1 -0
  279. package/dist/tools/legacy-hono-conventions.js +152 -0
  280. package/dist/tools/legacy-hono-conventions.js.map +1 -0
  281. package/dist/tools/migration-lint-tools.d.ts +26 -0
  282. package/dist/tools/migration-lint-tools.d.ts.map +1 -0
  283. package/dist/tools/migration-lint-tools.js +247 -0
  284. package/dist/tools/migration-lint-tools.js.map +1 -0
  285. package/dist/tools/model-tools.d.ts +30 -0
  286. package/dist/tools/model-tools.d.ts.map +1 -0
  287. package/dist/tools/model-tools.js +145 -0
  288. package/dist/tools/model-tools.js.map +1 -0
  289. package/dist/tools/nest-ext-tools.d.ts +207 -0
  290. package/dist/tools/nest-ext-tools.d.ts.map +1 -0
  291. package/dist/tools/nest-ext-tools.js +752 -0
  292. package/dist/tools/nest-ext-tools.js.map +1 -0
  293. package/dist/tools/nest-tools.d.ts +198 -0
  294. package/dist/tools/nest-tools.d.ts.map +1 -0
  295. package/dist/tools/nest-tools.js +1142 -0
  296. package/dist/tools/nest-tools.js.map +1 -0
  297. package/dist/tools/nextjs-api-contract-readers.d.ts +14 -0
  298. package/dist/tools/nextjs-api-contract-readers.d.ts.map +1 -0
  299. package/dist/tools/nextjs-api-contract-readers.js +204 -0
  300. package/dist/tools/nextjs-api-contract-readers.js.map +1 -0
  301. package/dist/tools/nextjs-api-contract-tools.d.ts +57 -0
  302. package/dist/tools/nextjs-api-contract-tools.d.ts.map +1 -0
  303. package/dist/tools/nextjs-api-contract-tools.js +144 -0
  304. package/dist/tools/nextjs-api-contract-tools.js.map +1 -0
  305. package/dist/tools/nextjs-boundary-tools.d.ts +39 -0
  306. package/dist/tools/nextjs-boundary-tools.d.ts.map +1 -0
  307. package/dist/tools/nextjs-boundary-tools.js +152 -0
  308. package/dist/tools/nextjs-boundary-tools.js.map +1 -0
  309. package/dist/tools/nextjs-component-readers.d.ts +101 -0
  310. package/dist/tools/nextjs-component-readers.d.ts.map +1 -0
  311. package/dist/tools/nextjs-component-readers.js +287 -0
  312. package/dist/tools/nextjs-component-readers.js.map +1 -0
  313. package/dist/tools/nextjs-component-tools.d.ts +51 -0
  314. package/dist/tools/nextjs-component-tools.d.ts.map +1 -0
  315. package/dist/tools/nextjs-component-tools.js +212 -0
  316. package/dist/tools/nextjs-component-tools.js.map +1 -0
  317. package/dist/tools/nextjs-data-flow-tools.d.ts +42 -0
  318. package/dist/tools/nextjs-data-flow-tools.d.ts.map +1 -0
  319. package/dist/tools/nextjs-data-flow-tools.js +158 -0
  320. package/dist/tools/nextjs-data-flow-tools.js.map +1 -0
  321. package/dist/tools/nextjs-framework-audit-tools.d.ts +60 -0
  322. package/dist/tools/nextjs-framework-audit-tools.d.ts.map +1 -0
  323. package/dist/tools/nextjs-framework-audit-tools.js +394 -0
  324. package/dist/tools/nextjs-framework-audit-tools.js.map +1 -0
  325. package/dist/tools/nextjs-link-tools.d.ts +41 -0
  326. package/dist/tools/nextjs-link-tools.d.ts.map +1 -0
  327. package/dist/tools/nextjs-link-tools.js +157 -0
  328. package/dist/tools/nextjs-link-tools.js.map +1 -0
  329. package/dist/tools/nextjs-metadata-tools.d.ts +74 -0
  330. package/dist/tools/nextjs-metadata-tools.d.ts.map +1 -0
  331. package/dist/tools/nextjs-metadata-tools.js +252 -0
  332. package/dist/tools/nextjs-metadata-tools.js.map +1 -0
  333. package/dist/tools/nextjs-middleware-coverage-tools.d.ts +41 -0
  334. package/dist/tools/nextjs-middleware-coverage-tools.d.ts.map +1 -0
  335. package/dist/tools/nextjs-middleware-coverage-tools.js +88 -0
  336. package/dist/tools/nextjs-middleware-coverage-tools.js.map +1 -0
  337. package/dist/tools/nextjs-route-readers.d.ts +81 -0
  338. package/dist/tools/nextjs-route-readers.d.ts.map +1 -0
  339. package/dist/tools/nextjs-route-readers.js +340 -0
  340. package/dist/tools/nextjs-route-readers.js.map +1 -0
  341. package/dist/tools/nextjs-route-tools.d.ts +36 -0
  342. package/dist/tools/nextjs-route-tools.d.ts.map +1 -0
  343. package/dist/tools/nextjs-route-tools.js +175 -0
  344. package/dist/tools/nextjs-route-tools.js.map +1 -0
  345. package/dist/tools/nextjs-security-readers.d.ts +22 -0
  346. package/dist/tools/nextjs-security-readers.d.ts.map +1 -0
  347. package/dist/tools/nextjs-security-readers.js +318 -0
  348. package/dist/tools/nextjs-security-readers.js.map +1 -0
  349. package/dist/tools/nextjs-security-scoring.d.ts +15 -0
  350. package/dist/tools/nextjs-security-scoring.d.ts.map +1 -0
  351. package/dist/tools/nextjs-security-scoring.js +65 -0
  352. package/dist/tools/nextjs-security-scoring.js.map +1 -0
  353. package/dist/tools/nextjs-security-tools.d.ts +75 -0
  354. package/dist/tools/nextjs-security-tools.d.ts.map +1 -0
  355. package/dist/tools/nextjs-security-tools.js +153 -0
  356. package/dist/tools/nextjs-security-tools.js.map +1 -0
  357. package/dist/tools/nextjs-tools.d.ts +15 -0
  358. package/dist/tools/nextjs-tools.d.ts.map +1 -0
  359. package/dist/tools/nextjs-tools.js +15 -0
  360. package/dist/tools/nextjs-tools.js.map +1 -0
  361. package/dist/tools/outline-tools.d.ts.map +1 -1
  362. package/dist/tools/outline-tools.js +20 -0
  363. package/dist/tools/outline-tools.js.map +1 -1
  364. package/dist/tools/pattern-tools.d.ts +8 -0
  365. package/dist/tools/pattern-tools.d.ts.map +1 -1
  366. package/dist/tools/pattern-tools.js +651 -3
  367. package/dist/tools/pattern-tools.js.map +1 -1
  368. package/dist/tools/perf-tools.d.ts +32 -0
  369. package/dist/tools/perf-tools.d.ts.map +1 -0
  370. package/dist/tools/perf-tools.js +227 -0
  371. package/dist/tools/perf-tools.js.map +1 -0
  372. package/dist/tools/php-tools.d.ts +185 -0
  373. package/dist/tools/php-tools.d.ts.map +1 -0
  374. package/dist/tools/php-tools.js +645 -0
  375. package/dist/tools/php-tools.js.map +1 -0
  376. package/dist/tools/plan-turn-tools.d.ts +89 -0
  377. package/dist/tools/plan-turn-tools.d.ts.map +1 -0
  378. package/dist/tools/plan-turn-tools.js +508 -0
  379. package/dist/tools/plan-turn-tools.js.map +1 -0
  380. package/dist/tools/prisma-schema-tools.d.ts +44 -0
  381. package/dist/tools/prisma-schema-tools.d.ts.map +1 -0
  382. package/dist/tools/prisma-schema-tools.js +358 -0
  383. package/dist/tools/prisma-schema-tools.js.map +1 -0
  384. package/dist/tools/project-tools.d.ts +116 -7
  385. package/dist/tools/project-tools.d.ts.map +1 -1
  386. package/dist/tools/project-tools.js +595 -218
  387. package/dist/tools/project-tools.js.map +1 -1
  388. package/dist/tools/pydantic-models.d.ts +46 -0
  389. package/dist/tools/pydantic-models.d.ts.map +1 -0
  390. package/dist/tools/pydantic-models.js +249 -0
  391. package/dist/tools/pydantic-models.js.map +1 -0
  392. package/dist/tools/pyproject-tools.d.ts +23 -0
  393. package/dist/tools/pyproject-tools.d.ts.map +1 -0
  394. package/dist/tools/pyproject-tools.js +133 -0
  395. package/dist/tools/pyproject-tools.js.map +1 -0
  396. package/dist/tools/pytest-tools.d.ts +20 -0
  397. package/dist/tools/pytest-tools.d.ts.map +1 -0
  398. package/dist/tools/pytest-tools.js +106 -0
  399. package/dist/tools/pytest-tools.js.map +1 -0
  400. package/dist/tools/python-audit.d.ts +40 -0
  401. package/dist/tools/python-audit.d.ts.map +1 -0
  402. package/dist/tools/python-audit.js +244 -0
  403. package/dist/tools/python-audit.js.map +1 -0
  404. package/dist/tools/python-callers.d.ts +28 -0
  405. package/dist/tools/python-callers.d.ts.map +1 -0
  406. package/dist/tools/python-callers.js +110 -0
  407. package/dist/tools/python-callers.js.map +1 -0
  408. package/dist/tools/python-circular-imports.d.ts +19 -0
  409. package/dist/tools/python-circular-imports.d.ts.map +1 -0
  410. package/dist/tools/python-circular-imports.js +126 -0
  411. package/dist/tools/python-circular-imports.js.map +1 -0
  412. package/dist/tools/python-constants-tools.d.ts +44 -0
  413. package/dist/tools/python-constants-tools.d.ts.map +1 -0
  414. package/dist/tools/python-constants-tools.js +525 -0
  415. package/dist/tools/python-constants-tools.js.map +1 -0
  416. package/dist/tools/python-deps-analyzer.d.ts +46 -0
  417. package/dist/tools/python-deps-analyzer.d.ts.map +1 -0
  418. package/dist/tools/python-deps-analyzer.js +227 -0
  419. package/dist/tools/python-deps-analyzer.js.map +1 -0
  420. package/dist/tools/query-tools.d.ts +23 -0
  421. package/dist/tools/query-tools.d.ts.map +1 -0
  422. package/dist/tools/query-tools.js +256 -0
  423. package/dist/tools/query-tools.js.map +1 -0
  424. package/dist/tools/react-tools.d.ts +263 -0
  425. package/dist/tools/react-tools.d.ts.map +1 -0
  426. package/dist/tools/react-tools.js +839 -0
  427. package/dist/tools/react-tools.js.map +1 -0
  428. package/dist/tools/report-tools.js +47 -0
  429. package/dist/tools/report-tools.js.map +1 -1
  430. package/dist/tools/review-diff-tools.d.ts +5 -4
  431. package/dist/tools/review-diff-tools.d.ts.map +1 -1
  432. package/dist/tools/review-diff-tools.js +157 -66
  433. package/dist/tools/review-diff-tools.js.map +1 -1
  434. package/dist/tools/room-tools.d.ts +36 -0
  435. package/dist/tools/room-tools.d.ts.map +1 -0
  436. package/dist/tools/room-tools.js +147 -0
  437. package/dist/tools/room-tools.js.map +1 -0
  438. package/dist/tools/route-tools.d.ts +27 -1
  439. package/dist/tools/route-tools.d.ts.map +1 -1
  440. package/dist/tools/route-tools.js +744 -18
  441. package/dist/tools/route-tools.js.map +1 -1
  442. package/dist/tools/ruff-tools.d.ts +32 -0
  443. package/dist/tools/ruff-tools.d.ts.map +1 -0
  444. package/dist/tools/ruff-tools.js +114 -0
  445. package/dist/tools/ruff-tools.js.map +1 -0
  446. package/dist/tools/search-ranker.d.ts.map +1 -1
  447. package/dist/tools/search-ranker.js +7 -0
  448. package/dist/tools/search-ranker.js.map +1 -1
  449. package/dist/tools/search-tools.d.ts +3 -2
  450. package/dist/tools/search-tools.d.ts.map +1 -1
  451. package/dist/tools/search-tools.js +16 -3
  452. package/dist/tools/search-tools.js.map +1 -1
  453. package/dist/tools/serialization-tools.d.ts +24 -0
  454. package/dist/tools/serialization-tools.d.ts.map +1 -0
  455. package/dist/tools/serialization-tools.js +156 -0
  456. package/dist/tools/serialization-tools.js.map +1 -0
  457. package/dist/tools/sql-tools.d.ts +274 -0
  458. package/dist/tools/sql-tools.d.ts.map +1 -0
  459. package/dist/tools/sql-tools.js +1160 -0
  460. package/dist/tools/sql-tools.js.map +1 -0
  461. package/dist/tools/status-tools.d.ts +10 -0
  462. package/dist/tools/status-tools.d.ts.map +1 -0
  463. package/dist/tools/status-tools.js +32 -0
  464. package/dist/tools/status-tools.js.map +1 -0
  465. package/dist/tools/symbol-tools.d.ts +19 -0
  466. package/dist/tools/symbol-tools.d.ts.map +1 -1
  467. package/dist/tools/symbol-tools.js +75 -4
  468. package/dist/tools/symbol-tools.js.map +1 -1
  469. package/dist/tools/taint-tools.d.ts +43 -0
  470. package/dist/tools/taint-tools.d.ts.map +1 -0
  471. package/dist/tools/taint-tools.js +922 -0
  472. package/dist/tools/taint-tools.js.map +1 -0
  473. package/dist/tools/test-impact-tools.d.ts +29 -0
  474. package/dist/tools/test-impact-tools.d.ts.map +1 -0
  475. package/dist/tools/test-impact-tools.js +156 -0
  476. package/dist/tools/test-impact-tools.js.map +1 -0
  477. package/dist/tools/typecheck-tools.d.ts +39 -0
  478. package/dist/tools/typecheck-tools.d.ts.map +1 -0
  479. package/dist/tools/typecheck-tools.js +191 -0
  480. package/dist/tools/typecheck-tools.js.map +1 -0
  481. package/dist/tools/wiring-tools.d.ts +19 -0
  482. package/dist/tools/wiring-tools.d.ts.map +1 -0
  483. package/dist/tools/wiring-tools.js +147 -0
  484. package/dist/tools/wiring-tools.js.map +1 -0
  485. package/dist/types.d.ts +9 -1
  486. package/dist/types.d.ts.map +1 -1
  487. package/dist/utils/framework-detect.d.ts +18 -2
  488. package/dist/utils/framework-detect.d.ts.map +1 -1
  489. package/dist/utils/framework-detect.js +150 -3
  490. package/dist/utils/framework-detect.js.map +1 -1
  491. package/dist/utils/import-graph.d.ts +42 -0
  492. package/dist/utils/import-graph.d.ts.map +1 -1
  493. package/dist/utils/import-graph.js +248 -9
  494. package/dist/utils/import-graph.js.map +1 -1
  495. package/dist/utils/language-detect.d.ts +21 -0
  496. package/dist/utils/language-detect.d.ts.map +1 -0
  497. package/dist/utils/language-detect.js +183 -0
  498. package/dist/utils/language-detect.js.map +1 -0
  499. package/dist/utils/nextjs-ast-readers.d.ts +44 -0
  500. package/dist/utils/nextjs-ast-readers.d.ts.map +1 -0
  501. package/dist/utils/nextjs-ast-readers.js +341 -0
  502. package/dist/utils/nextjs-ast-readers.js.map +1 -0
  503. package/dist/utils/nextjs-audit-cache.d.ts +51 -0
  504. package/dist/utils/nextjs-audit-cache.d.ts.map +1 -0
  505. package/dist/utils/nextjs-audit-cache.js +116 -0
  506. package/dist/utils/nextjs-audit-cache.js.map +1 -0
  507. package/dist/utils/nextjs-metadata-readers.d.ts +65 -0
  508. package/dist/utils/nextjs-metadata-readers.d.ts.map +1 -0
  509. package/dist/utils/nextjs-metadata-readers.js +447 -0
  510. package/dist/utils/nextjs-metadata-readers.js.map +1 -0
  511. package/dist/utils/nextjs.d.ts +42 -0
  512. package/dist/utils/nextjs.d.ts.map +1 -0
  513. package/dist/utils/nextjs.js +284 -0
  514. package/dist/utils/nextjs.js.map +1 -0
  515. package/dist/utils/python-import-resolver.d.ts +42 -0
  516. package/dist/utils/python-import-resolver.d.ts.map +1 -0
  517. package/dist/utils/python-import-resolver.js +101 -0
  518. package/dist/utils/python-import-resolver.js.map +1 -0
  519. package/dist/utils/python-imports.d.ts +28 -0
  520. package/dist/utils/python-imports.d.ts.map +1 -0
  521. package/dist/utils/python-imports.js +117 -0
  522. package/dist/utils/python-imports.js.map +1 -0
  523. package/dist/utils/react-alias.d.ts +15 -0
  524. package/dist/utils/react-alias.d.ts.map +1 -0
  525. package/dist/utils/react-alias.js +31 -0
  526. package/dist/utils/react-alias.js.map +1 -0
  527. package/dist/utils/test-file.d.ts.map +1 -1
  528. package/dist/utils/test-file.js +7 -0
  529. package/dist/utils/test-file.js.map +1 -1
  530. package/dist/utils/walk.d.ts +22 -0
  531. package/dist/utils/walk.d.ts.map +1 -1
  532. package/dist/utils/walk.js +70 -2
  533. package/dist/utils/walk.js.map +1 -1
  534. package/package.json +4 -3
  535. package/rules/codesift.md +71 -5
  536. package/rules/codesift.mdc +71 -5
  537. package/rules/codex.md +71 -5
  538. package/rules/gemini.md +71 -5
  539. package/src/parser/languages/tree-sitter-javascript.wasm +0 -0
  540. package/src/parser/languages/tree-sitter-kotlin.wasm +0 -0
  541. package/src/parser/languages/tree-sitter-php.wasm +0 -0
  542. package/src/parser/languages/tree-sitter-php_only.wasm +0 -0
  543. package/src/parser/languages/tree-sitter-python.wasm +0 -0
@@ -0,0 +1,1527 @@
1
+ /**
2
+ * HonoExtractor — AST-based extractor for Hono framework applications.
3
+ *
4
+ * Parses a Hono entry file using tree-sitter TypeScript grammar and produces
5
+ * a HonoAppModel that describes routes, middleware, context flow, OpenAPI
6
+ * schemas, and RPC exports.
7
+ *
8
+ * Spec: docs/specs/2026-04-10-hono-framework-intelligence-spec.md
9
+ * Plan: docs/specs/2026-04-10-hono-framework-intelligence-plan.md (Task 2-3)
10
+ */
11
+ import { readFile } from "node:fs/promises";
12
+ import { existsSync, realpathSync } from "node:fs";
13
+ import path from "node:path";
14
+ import { getParser } from "../parser-manager.js";
15
+ import { HonoInlineAnalyzer } from "./hono-inline-analyzer.js";
16
+ const HTTP_METHODS = new Set([
17
+ "get", "post", "put", "delete", "patch", "options", "all", "on",
18
+ ]);
19
+ export class HonoExtractor {
20
+ inlineAnalyzer = new HonoInlineAnalyzer();
21
+ async parse(entryFile) {
22
+ const absoluteEntry = canonicalize(path.resolve(entryFile));
23
+ const model = emptyModel(absoluteEntry, {});
24
+ const parsedCache = new Map();
25
+ await this.parseFile(absoluteEntry, "", new Set(), parsedCache, model);
26
+ // Post-pass runtime upgrade: if the entry-file-only detector left runtime
27
+ // as "unknown" but an imported file (e.g. bindings.ts) contains a CF
28
+ // Worker type, promote runtime to "cloudflare". Real Hono apps commonly
29
+ // isolate Bindings type definitions in a separate file.
30
+ if (model.runtime === "unknown") {
31
+ model.runtime = await this.upgradeRuntimeFromImports(model.files_used);
32
+ }
33
+ return model;
34
+ }
35
+ /**
36
+ * Post-parse runtime upgrade. Scans each file in files_used (excluding the
37
+ * entry file, which was already checked) for Cloudflare Worker type
38
+ * references. Returns "cloudflare" on first match, "unknown" otherwise.
39
+ */
40
+ async upgradeRuntimeFromImports(filesUsed) {
41
+ for (const file of filesUsed) {
42
+ let source;
43
+ try {
44
+ source = await readFile(file, "utf-8");
45
+ }
46
+ catch {
47
+ continue;
48
+ }
49
+ if (hasCloudflareBindingsType(source))
50
+ return "cloudflare";
51
+ }
52
+ return "unknown";
53
+ }
54
+ /**
55
+ * Parse a single file and merge results into the model.
56
+ * @param file Absolute canonicalized path
57
+ * @param prefix Path prefix to apply to all routes (from parent app.route())
58
+ * @param inFlight In-flight stack for cycle detection (pushed/popped per call)
59
+ * @param parsedCache Memoized child models keyed by canonical path
60
+ * @param model Accumulated model being built
61
+ */
62
+ async parseFile(file, prefix, inFlight, parsedCache, model) {
63
+ // Cycle detection: break if this file is already being parsed up the call stack
64
+ if (inFlight.has(file))
65
+ return;
66
+ // Record file as used
67
+ if (!model.files_used.includes(file)) {
68
+ model.files_used.push(file);
69
+ }
70
+ // Check memoization cache for previously parsed child
71
+ const cached = parsedCache.get(file);
72
+ if (cached) {
73
+ // Re-use parsed routes with fresh prefix
74
+ // route.path already has basePath applied — apply mount prefix on top
75
+ for (const route of cached.routes) {
76
+ model.routes.push({
77
+ ...route,
78
+ path: joinPaths(prefix, route.path),
79
+ });
80
+ }
81
+ return;
82
+ }
83
+ let source;
84
+ try {
85
+ source = await readFile(file, "utf-8");
86
+ }
87
+ catch {
88
+ model.skip_reasons.file_read_failed =
89
+ (model.skip_reasons.file_read_failed ?? 0) + 1;
90
+ return;
91
+ }
92
+ const language = pickLanguage(file);
93
+ const parser = await getParser(language);
94
+ if (!parser) {
95
+ model.skip_reasons.parser_unavailable =
96
+ (model.skip_reasons.parser_unavailable ?? 0) + 1;
97
+ return;
98
+ }
99
+ const tree = parser.parse(source);
100
+ if (!tree) {
101
+ model.skip_reasons.parse_failed =
102
+ (model.skip_reasons.parse_failed ?? 0) + 1;
103
+ return;
104
+ }
105
+ try {
106
+ // Track local data
107
+ const localAppVars = {};
108
+ const localRoutes = [];
109
+ const localMounts = [];
110
+ const importMap = this.extractImportMap(tree.rootNode, file);
111
+ // Walk for Hono app variables
112
+ this.walkAppVariables(tree.rootNode, file, localAppVars);
113
+ // Merge local app variables into model
114
+ for (const [name, app] of Object.entries(localAppVars)) {
115
+ model.app_variables[name] = app;
116
+ }
117
+ // Walk for HTTP routes (not app.route — those are handled separately)
118
+ this.walkHttpRoutes(tree.rootNode, file, localAppVars, localRoutes);
119
+ // Apply mount prefix on top of routes (route.path already has basePath)
120
+ for (const route of localRoutes) {
121
+ model.routes.push({
122
+ ...route,
123
+ path: joinPaths(prefix, route.path),
124
+ });
125
+ }
126
+ // Walk for context flow: c.set(), c.get(), c.var.*, c.env.*
127
+ this.walkContextFlow(tree.rootNode, file, model);
128
+ // Walk for middleware chains: app.use("scope", mw1, mw2, ...)
129
+ this.walkMiddleware(tree.rootNode, file, localAppVars, importMap, model);
130
+ // Walk for RPC type exports: export type AppType = typeof app
131
+ this.walkRPCExports(tree.rootNode, file, localAppVars, model);
132
+ // Walk for OpenAPI: createRoute() definitions + app.openapi() registrations
133
+ this.walkOpenAPI(tree.rootNode, file, localAppVars, prefix, model);
134
+ // Scan imported files for context flow (middleware files like auth.ts)
135
+ for (const [, importedFile] of importMap) {
136
+ if (!model.files_used.includes(importedFile)) {
137
+ model.files_used.push(importedFile);
138
+ }
139
+ await this.scanFileForContextFlow(importedFile, model);
140
+ }
141
+ // Detect runtime and env bindings (only for entry file)
142
+ if (file === model.entry_file) {
143
+ model.runtime = await this.detectRuntime(file);
144
+ this.extractEnvBindings(tree.rootNode, source, model);
145
+ }
146
+ // Cache local parse result (routes stored with raw_path, prefix applied on read)
147
+ parsedCache.set(file, {
148
+ app_variables: localAppVars,
149
+ routes: localRoutes,
150
+ mounts: localMounts,
151
+ files_used: [file],
152
+ });
153
+ // Walk for app.route() mounts — recursive into child files
154
+ inFlight.add(file);
155
+ await this.walkRouteMounts(tree.rootNode, file, prefix, localAppVars, importMap, inFlight, parsedCache, model, localMounts);
156
+ inFlight.delete(file);
157
+ }
158
+ finally {
159
+ tree.delete();
160
+ }
161
+ }
162
+ /**
163
+ * Build a map of { variableName → absoluteFilePath } from import statements.
164
+ */
165
+ extractImportMap(root, currentFile) {
166
+ const importMap = new Map();
167
+ const cursor = root.walk();
168
+ walk(cursor, (node) => {
169
+ if (node.type !== "import_statement")
170
+ return;
171
+ const sourceNode = node.childForFieldName("source");
172
+ if (!sourceNode)
173
+ return;
174
+ const specifier = stringLiteralValue(sourceNode);
175
+ if (!specifier || !specifier.startsWith("."))
176
+ return;
177
+ const resolved = resolveImportPath(currentFile, specifier);
178
+ if (!resolved)
179
+ return;
180
+ // Default import: import X from "./file"
181
+ const importClause = node.children.find((c) => c.type === "import_clause");
182
+ if (!importClause)
183
+ return;
184
+ for (const child of importClause.namedChildren) {
185
+ if (child.type === "identifier") {
186
+ // default import
187
+ importMap.set(child.text, resolved);
188
+ }
189
+ if (child.type === "named_imports") {
190
+ for (const spec of child.namedChildren) {
191
+ if (spec.type === "import_specifier") {
192
+ const alias = spec.childForFieldName("alias");
193
+ const name = spec.childForFieldName("name");
194
+ const varName = alias?.text ?? name?.text;
195
+ if (varName)
196
+ importMap.set(varName, resolved);
197
+ }
198
+ }
199
+ }
200
+ }
201
+ });
202
+ return importMap;
203
+ }
204
+ walkAppVariables(root, file, localVars) {
205
+ // First pass: detect factory variables (createFactory<Env>())
206
+ const factoryVars = new Set();
207
+ const cursor1 = root.walk();
208
+ walk(cursor1, (node) => {
209
+ if (node.type !== "variable_declarator")
210
+ return;
211
+ const nameNode = node.childForFieldName("name");
212
+ const valueNode = node.childForFieldName("value");
213
+ if (!nameNode || !valueNode || nameNode.type !== "identifier")
214
+ return;
215
+ if (isCreateFactoryCall(valueNode)) {
216
+ factoryVars.add(nameNode.text);
217
+ }
218
+ });
219
+ // Second pass: detect Hono app variables
220
+ const cursor = root.walk();
221
+ walk(cursor, (node) => {
222
+ if (node.type !== "variable_declarator")
223
+ return;
224
+ const nameNode = node.childForFieldName("name");
225
+ const valueNode = node.childForFieldName("value");
226
+ if (!nameNode || !valueNode)
227
+ return;
228
+ if (nameNode.type !== "identifier")
229
+ return;
230
+ const name = nameNode.text;
231
+ // Direct creation: new Hono(), new OpenAPIHono()
232
+ const createdVia = classifyAppCreation(valueNode);
233
+ if (createdVia) {
234
+ localVars[name] = {
235
+ variable_name: name,
236
+ file,
237
+ line: nameNode.startPosition.row + 1,
238
+ created_via: createdVia,
239
+ base_path: "",
240
+ };
241
+ return;
242
+ }
243
+ // basePath derivation: const v1 = app.basePath("/v1")
244
+ const basePath = extractBasePathCall(valueNode, localVars);
245
+ if (basePath) {
246
+ localVars[name] = {
247
+ variable_name: name,
248
+ file,
249
+ line: nameNode.startPosition.row + 1,
250
+ created_via: "basePath",
251
+ base_path: basePath.prefix,
252
+ parent: basePath.parentVar,
253
+ };
254
+ return;
255
+ }
256
+ // factory.createApp(): const api = factory.createApp()
257
+ if (isFactoryCreateApp(valueNode, factoryVars)) {
258
+ localVars[name] = {
259
+ variable_name: name,
260
+ file,
261
+ line: nameNode.startPosition.row + 1,
262
+ created_via: "factory.createApp",
263
+ base_path: "",
264
+ };
265
+ }
266
+ });
267
+ }
268
+ walkHttpRoutes(root, file, appVars, routes) {
269
+ const cursor = root.walk();
270
+ walk(cursor, (node) => {
271
+ if (node.type !== "call_expression")
272
+ return;
273
+ const fnNode = node.childForFieldName("function");
274
+ const argsNode = node.childForFieldName("arguments");
275
+ if (!fnNode || !argsNode || fnNode.type !== "member_expression")
276
+ return;
277
+ const objectNode = fnNode.childForFieldName("object");
278
+ const propertyNode = fnNode.childForFieldName("property");
279
+ if (!objectNode || !propertyNode || objectNode.type !== "identifier")
280
+ return;
281
+ const ownerVar = objectNode.text;
282
+ const appDef = appVars[ownerVar];
283
+ if (!appDef)
284
+ return;
285
+ const method = propertyNode.text.toLowerCase();
286
+ if (!HTTP_METHODS.has(method) || method === "use" || method === "route")
287
+ return;
288
+ const argList = argsNode.namedChildren;
289
+ if (argList.length === 0)
290
+ return;
291
+ // basePath prefix inherited from the HonoApp variable
292
+ const basePrefix = appDef.base_path || "";
293
+ // Handle app.on(["GET", "POST"], "/path", handler) — fan out
294
+ if (method === "on") {
295
+ this.handleOnMethod(argList, file, node, ownerVar, basePrefix, routes);
296
+ return;
297
+ }
298
+ const firstArg = argList[0];
299
+ if (!firstArg)
300
+ return;
301
+ const rawPath = stringLiteralValue(firstArg);
302
+ if (rawPath == null)
303
+ return;
304
+ // Resolve once so buildHandler and analyzeHandlerIfInline see the same node.
305
+ const handlerNode = argList[argList.length - 1] ?? firstArg;
306
+ const handler = buildHandler(handlerNode, file);
307
+ const regexConstraint = parseRegexConstraints(rawPath);
308
+ const inlineAnalysis = this.analyzeHandlerIfInline(handler, handlerNode);
309
+ routes.push({
310
+ method: method.toUpperCase(),
311
+ path: joinPaths(basePrefix, rawPath),
312
+ raw_path: rawPath,
313
+ file,
314
+ line: node.startPosition.row + 1,
315
+ owner_var: ownerVar,
316
+ handler,
317
+ inline_middleware: [],
318
+ validators: [],
319
+ ...(regexConstraint ? { regex_constraint: regexConstraint } : {}),
320
+ ...(inlineAnalysis ? { inline_analysis: inlineAnalysis } : {}),
321
+ });
322
+ });
323
+ }
324
+ /**
325
+ * Run InlineHandlerAnalyzer on the handler AST node when buildHandler
326
+ * classified the handler as inline. Returns undefined for named-identifier
327
+ * handlers — those are defined elsewhere and analyzed via their symbol.
328
+ * The caller must pass the SAME node used by buildHandler so the two
329
+ * decisions cannot disagree.
330
+ */
331
+ analyzeHandlerIfInline(handler, handlerNode) {
332
+ if (!handler.inline)
333
+ return undefined;
334
+ return this.inlineAnalyzer.analyze(handlerNode);
335
+ }
336
+ /** Handle app.on(methods, path, handler) — fan out into per-method routes. */
337
+ handleOnMethod(argList, file, node, ownerVar, basePrefix, routes) {
338
+ if (argList.length < 2)
339
+ return;
340
+ const methodsArg = argList[0];
341
+ const pathArg = argList[1];
342
+ if (!methodsArg || !pathArg)
343
+ return;
344
+ // Methods: string or array of strings
345
+ const methods = [];
346
+ if (methodsArg.type === "array") {
347
+ for (const el of methodsArg.namedChildren) {
348
+ const v = stringLiteralValue(el);
349
+ if (v)
350
+ methods.push(v.toUpperCase());
351
+ }
352
+ }
353
+ else {
354
+ const v = stringLiteralValue(methodsArg);
355
+ if (v)
356
+ methods.push(v.toUpperCase());
357
+ }
358
+ if (methods.length === 0)
359
+ return;
360
+ const rawPath = stringLiteralValue(pathArg);
361
+ if (rawPath == null)
362
+ return;
363
+ // Resolve once so buildHandler and analyzeHandlerIfInline see the same node.
364
+ const handlerNode = argList[argList.length - 1] ?? pathArg;
365
+ const handler = buildHandler(handlerNode, file);
366
+ const regexConstraint = parseRegexConstraints(rawPath);
367
+ const inlineAnalysis = this.analyzeHandlerIfInline(handler, handlerNode);
368
+ for (const m of methods) {
369
+ routes.push({
370
+ method: m,
371
+ path: joinPaths(basePrefix, rawPath),
372
+ raw_path: rawPath,
373
+ file,
374
+ line: node.startPosition.row + 1,
375
+ owner_var: ownerVar,
376
+ handler,
377
+ inline_middleware: [],
378
+ validators: [],
379
+ ...(regexConstraint ? { regex_constraint: regexConstraint } : {}),
380
+ ...(inlineAnalysis ? { inline_analysis: inlineAnalysis } : {}),
381
+ });
382
+ }
383
+ }
384
+ /**
385
+ * Walk for app.use(scope, mw1, mw2, ...) calls.
386
+ * Handles: identifiers, inline arrows, some()/every() from hono/combine,
387
+ * spread arrays, call expressions like cors().
388
+ */
389
+ walkMiddleware(root, file, appVars, _importMap, model) {
390
+ // Build local variable → array declaration map for spread expansion
391
+ const arrayVars = this.collectArrayVars(root);
392
+ // Identify which imported names come from which packages
393
+ const importSources = this.collectImportSources(root);
394
+ const cursor = root.walk();
395
+ walk(cursor, (node) => {
396
+ if (node.type !== "call_expression")
397
+ return;
398
+ const fnNode = node.childForFieldName("function");
399
+ const argsNode = node.childForFieldName("arguments");
400
+ if (!fnNode || !argsNode || fnNode.type !== "member_expression")
401
+ return;
402
+ const objectNode = fnNode.childForFieldName("object");
403
+ const propertyNode = fnNode.childForFieldName("property");
404
+ if (!objectNode || !propertyNode || objectNode.type !== "identifier")
405
+ return;
406
+ const ownerVar = objectNode.text;
407
+ if (!appVars[ownerVar])
408
+ return;
409
+ if (propertyNode.text !== "use")
410
+ return;
411
+ const argList = argsNode.namedChildren;
412
+ if (argList.length === 0)
413
+ return;
414
+ // First arg may be a scope path string, or directly a middleware
415
+ let scope = "*";
416
+ let mwStartIdx = 0;
417
+ const firstArg = argList[0];
418
+ if (firstArg) {
419
+ const maybeScope = stringLiteralValue(firstArg);
420
+ if (maybeScope != null) {
421
+ scope = maybeScope;
422
+ mwStartIdx = 1;
423
+ }
424
+ }
425
+ const entries = [];
426
+ let order = 0;
427
+ for (let i = mwStartIdx; i < argList.length; i++) {
428
+ const arg = argList[i];
429
+ if (!arg)
430
+ continue;
431
+ order++;
432
+ // Spread: ...adminChain
433
+ if (arg.type === "spread_element") {
434
+ const inner = arg.namedChildren[0];
435
+ if (inner?.type === "identifier") {
436
+ const arrItems = arrayVars.get(inner.text);
437
+ if (arrItems) {
438
+ for (const item of arrItems) {
439
+ entries.push(this.buildMiddlewareEntry(item, file, node.startPosition.row + 1, order++, importSources, undefined));
440
+ }
441
+ }
442
+ }
443
+ continue;
444
+ }
445
+ // some(mw1, mw2) / every(mw1, mw2) from hono/combine
446
+ if (arg.type === "call_expression") {
447
+ const callFn = arg.childForFieldName("function");
448
+ const callArgs = arg.childForFieldName("arguments");
449
+ if (callFn?.type === "identifier" &&
450
+ (callFn.text === "some" || callFn.text === "every") && callArgs) {
451
+ const combineType = callFn.text;
452
+ for (const innerArg of callArgs.namedChildren) {
453
+ entries.push(this.buildMiddlewareEntry(innerArg.type === "identifier" ? innerArg.text : "<inline>", file, innerArg.startPosition.row + 1, order++, importSources, combineType));
454
+ }
455
+ continue;
456
+ }
457
+ }
458
+ // Regular middleware: identifier, call expression, or inline arrow
459
+ const mwName = this.extractMiddlewareName(arg);
460
+ entries.push(this.buildMiddlewareEntry(mwName, file, arg.startPosition.row + 1, order, importSources, undefined));
461
+ // T4: If this is an inline arrow, scan its body for conditional
462
+ // middleware calls like `if (cond) return mw(c, next)`. Each match
463
+ // produces an ADDITIONAL entry with applied_when set.
464
+ if (arg.type === "arrow_function" ||
465
+ arg.type === "function_expression") {
466
+ const conditional = this.detectConditionalMiddlewareCalls(arg);
467
+ for (const found of conditional) {
468
+ order++;
469
+ const extra = this.buildMiddlewareEntry(found.name, file, found.line, order, importSources, undefined);
470
+ extra.conditional = true;
471
+ extra.applied_when = found.applied_when;
472
+ entries.push(extra);
473
+ }
474
+ }
475
+ }
476
+ if (entries.length > 0) {
477
+ // Merge into existing chain for same scope, or create new
478
+ const existing = model.middleware_chains.find((mc) => mc.scope === scope && mc.owner_var === ownerVar);
479
+ if (existing) {
480
+ existing.entries.push(...entries);
481
+ }
482
+ else {
483
+ // scope_pattern is the raw scope (e.g., "*" or "/api/*") — downstream
484
+ // tools compile to regex via compileScopePattern()
485
+ model.middleware_chains.push({
486
+ scope,
487
+ scope_pattern: scope,
488
+ owner_var: ownerVar,
489
+ entries,
490
+ });
491
+ }
492
+ }
493
+ });
494
+ }
495
+ /**
496
+ * T4: walk an inline middleware arrow body and surface any conditional
497
+ * calls of the form `if (cond) return mw(c, next)` or `if (cond) await mw(c, next)`.
498
+ *
499
+ * We only inspect the DIRECT `if` statements at the top of the body (one level
500
+ * of `statement_block` / `return_statement`). Deep nesting is out of scope to
501
+ * keep false positives low.
502
+ *
503
+ * Returns an entry per conditional call found, with name + condition info.
504
+ */
505
+ detectConditionalMiddlewareCalls(fnNode) {
506
+ const results = [];
507
+ // Arrow function body is either an expression or a statement_block
508
+ const block = fnNode.childForFieldName("body");
509
+ if (!block)
510
+ return results;
511
+ // Only walk if the arrow body is a statement_block — expression-body
512
+ // arrows `(c) => foo(c, next)` are NOT conditional by definition.
513
+ if (block.type !== "statement_block")
514
+ return results;
515
+ for (let i = 0; i < block.childCount; i++) {
516
+ const stmt = block.child(i);
517
+ if (stmt?.type !== "if_statement")
518
+ continue;
519
+ const condition = stmt.childForFieldName("condition");
520
+ const consequence = stmt.childForFieldName("consequence");
521
+ if (!condition || !consequence)
522
+ continue;
523
+ // Resolve block-local `const x = mwFactory({...})` aliases so that
524
+ // const auth = basicAuth({...});
525
+ // return auth(c, next);
526
+ // reports "basicAuth" instead of "auth".
527
+ const localAliases = collectLocalAliases(consequence);
528
+ // Find mw call inside the consequence.
529
+ const mwCall = findMiddlewareCallInBlock(consequence);
530
+ if (!mwCall)
531
+ continue;
532
+ const rawName = extractCallCalleeName(mwCall);
533
+ if (!rawName)
534
+ continue;
535
+ const name = localAliases.get(rawName) ?? rawName;
536
+ const condText = condition.text.slice(0, 200);
537
+ const applied_when = {
538
+ condition_type: classifyConditionType(condition),
539
+ condition_text: condText,
540
+ };
541
+ results.push({
542
+ name,
543
+ line: mwCall.startPosition.row + 1,
544
+ applied_when,
545
+ });
546
+ }
547
+ return results;
548
+ }
549
+ extractMiddlewareName(node) {
550
+ if (node.type === "identifier")
551
+ return node.text;
552
+ if (node.type === "arrow_function" || node.type === "function_expression") {
553
+ return "<inline>";
554
+ }
555
+ if (node.type === "call_expression") {
556
+ const fn = node.childForFieldName("function");
557
+ if (fn?.type === "identifier")
558
+ return fn.text;
559
+ if (fn?.type === "member_expression") {
560
+ const prop = fn.childForFieldName("property");
561
+ return prop?.text ?? "<inline>";
562
+ }
563
+ }
564
+ return "<inline>";
565
+ }
566
+ buildMiddlewareEntry(name, file, line, order, importSources, expandedFrom) {
567
+ const importedFrom = importSources.get(name);
568
+ const isThirdParty = !!importedFrom && (importedFrom.startsWith("hono/") ||
569
+ !importedFrom.startsWith("."));
570
+ const entry = {
571
+ name,
572
+ order,
573
+ line,
574
+ file,
575
+ inline: name === "<inline>",
576
+ is_third_party: isThirdParty,
577
+ conditional: expandedFrom === "some",
578
+ };
579
+ if (importedFrom)
580
+ entry.imported_from = importedFrom;
581
+ if (expandedFrom)
582
+ entry.expanded_from = expandedFrom;
583
+ return entry;
584
+ }
585
+ /**
586
+ * Walk for `export type X = typeof varName` declarations.
587
+ * Classifies as "full_app" if varName is the root app (has mounts/middleware on it),
588
+ * or "route_group" if it's a sub-router without child mounts.
589
+ */
590
+ walkRPCExports(root, file, appVars, model) {
591
+ const cursor = root.walk();
592
+ walk(cursor, (node) => {
593
+ // export_statement > type_alias_declaration
594
+ if (node.type !== "export_statement")
595
+ return;
596
+ const typeAlias = node.namedChildren.find((c) => c.type === "type_alias_declaration");
597
+ if (!typeAlias)
598
+ return;
599
+ const nameNode = typeAlias.childForFieldName("name");
600
+ const valueNode = typeAlias.childForFieldName("value");
601
+ if (!nameNode || !valueNode)
602
+ return;
603
+ // typeof <varName>
604
+ if (valueNode.type !== "type_query")
605
+ return;
606
+ const queryArg = valueNode.namedChildren[0];
607
+ if (!queryArg || queryArg.type !== "identifier")
608
+ return;
609
+ const sourceVar = queryArg.text;
610
+ // Must reference a known Hono app variable
611
+ if (!appVars[sourceVar])
612
+ return;
613
+ // Classify: full_app if the variable is the root entry (has mounts), else route_group
614
+ const hasMounts = model.mounts.some((m) => m.parent_var === sourceVar);
615
+ const hasMiddleware = model.middleware_chains.some((mc) => mc.owner_var === sourceVar);
616
+ const shape = (hasMounts || hasMiddleware) ? "full_app" : "route_group";
617
+ model.rpc_exports.push({
618
+ export_name: nameNode.text,
619
+ file,
620
+ line: node.startPosition.row + 1,
621
+ shape,
622
+ source_var: sourceVar,
623
+ });
624
+ });
625
+ }
626
+ /**
627
+ * Walk for createRoute() definitions and app.openapi(route, handler) registrations.
628
+ * - createRoute({method, path, ...}) → tracked in a local map by variable name
629
+ * - app.openapi(routeVar, handler) → emits OpenAPIRoute + adds to model.routes
630
+ */
631
+ walkOpenAPI(root, file, appVars, prefix, model) {
632
+ // First pass: collect createRoute() variable definitions
633
+ const routeDefs = new Map();
634
+ const cursor1 = root.walk();
635
+ walk(cursor1, (node) => {
636
+ if (node.type !== "variable_declarator")
637
+ return;
638
+ const nameNode = node.childForFieldName("name");
639
+ const valueNode = node.childForFieldName("value");
640
+ if (!nameNode || !valueNode || nameNode.type !== "identifier")
641
+ return;
642
+ if (valueNode.type !== "call_expression")
643
+ return;
644
+ const fn = valueNode.childForFieldName("function");
645
+ if (!fn || fn.text !== "createRoute")
646
+ return;
647
+ const args = valueNode.childForFieldName("arguments");
648
+ const objArg = args?.namedChildren[0];
649
+ if (!objArg || objArg.type !== "object")
650
+ return;
651
+ let method = "";
652
+ let routePath = "";
653
+ for (const prop of objArg.namedChildren) {
654
+ if (prop.type !== "pair")
655
+ continue;
656
+ const key = prop.childForFieldName("key");
657
+ const val = prop.childForFieldName("value");
658
+ if (!key || !val)
659
+ continue;
660
+ if (key.text === "method") {
661
+ method = stringLiteralValue(val) ?? "";
662
+ }
663
+ if (key.text === "path") {
664
+ routePath = stringLiteralValue(val) ?? "";
665
+ }
666
+ }
667
+ if (method && routePath) {
668
+ routeDefs.set(nameNode.text, {
669
+ method,
670
+ path: routePath,
671
+ line: node.startPosition.row + 1,
672
+ });
673
+ }
674
+ });
675
+ // Second pass: find app.openapi(routeVar, handler) calls
676
+ const cursor2 = root.walk();
677
+ walk(cursor2, (node) => {
678
+ if (node.type !== "call_expression")
679
+ return;
680
+ const fnNode = node.childForFieldName("function");
681
+ const argsNode = node.childForFieldName("arguments");
682
+ if (!fnNode || !argsNode || fnNode.type !== "member_expression")
683
+ return;
684
+ const obj = fnNode.childForFieldName("object");
685
+ const prop = fnNode.childForFieldName("property");
686
+ if (!obj || !prop || obj.type !== "identifier")
687
+ return;
688
+ if (!appVars[obj.text])
689
+ return;
690
+ if (prop.text !== "openapi")
691
+ return;
692
+ const argList = argsNode.namedChildren;
693
+ if (argList.length < 2)
694
+ return;
695
+ const routeRef = argList[0];
696
+ const handlerArg = argList[1];
697
+ if (!routeRef || !handlerArg)
698
+ return;
699
+ if (routeRef.type !== "identifier")
700
+ return;
701
+ const routeDef = routeDefs.get(routeRef.text);
702
+ if (!routeDef)
703
+ return;
704
+ const honoPath = routeDef.path.replace(/\{(\w+)\}/g, ":$1");
705
+ const openapiRoute = {
706
+ id: `openapi_${routeRef.text}`,
707
+ method: routeDef.method,
708
+ path: routeDef.path,
709
+ hono_path: honoPath,
710
+ request_schemas: {},
711
+ response_schemas: {},
712
+ middleware: [],
713
+ hidden: false,
714
+ file,
715
+ line: routeDef.line,
716
+ };
717
+ model.openapi_routes.push(openapiRoute);
718
+ // Also add as a regular route
719
+ const handler = buildHandler(handlerArg, file);
720
+ model.routes.push({
721
+ method: routeDef.method.toUpperCase(),
722
+ path: joinPaths(prefix, honoPath),
723
+ raw_path: honoPath,
724
+ file,
725
+ line: node.startPosition.row + 1,
726
+ owner_var: obj.text,
727
+ handler,
728
+ inline_middleware: [],
729
+ validators: [],
730
+ openapi_route_id: openapiRoute.id,
731
+ });
732
+ });
733
+ }
734
+ /**
735
+ * Lightweight scan of an imported file (e.g., middleware) for context flow only.
736
+ * Does not extract routes or mounts — just c.set/c.get/c.var patterns.
737
+ */
738
+ async scanFileForContextFlow(file, model) {
739
+ let source;
740
+ try {
741
+ source = await readFile(file, "utf-8");
742
+ }
743
+ catch {
744
+ return;
745
+ }
746
+ const parser = await getParser(pickLanguage(file));
747
+ if (!parser)
748
+ return;
749
+ const tree = parser.parse(source);
750
+ if (!tree)
751
+ return;
752
+ try {
753
+ this.walkContextFlow(tree.rootNode, file, model);
754
+ }
755
+ finally {
756
+ tree.delete();
757
+ }
758
+ }
759
+ /**
760
+ * Walk for context variable flow: c.set("key", val), c.get("key"), c.var.key, c.env.KEY.
761
+ * Detects conditional sets (inside if/try/switch blocks).
762
+ */
763
+ walkContextFlow(root, file, model) {
764
+ const varsMap = new Map();
765
+ const getOrCreate = (name, isEnv) => {
766
+ let cv = varsMap.get(name);
767
+ if (!cv) {
768
+ cv = { name, set_points: [], get_points: [], is_env_binding: isEnv };
769
+ varsMap.set(name, cv);
770
+ }
771
+ return cv;
772
+ };
773
+ const cursor = root.walk();
774
+ walk(cursor, (node) => {
775
+ // c.set("key", value)
776
+ if (node.type === "call_expression") {
777
+ const fn = node.childForFieldName("function");
778
+ if (fn?.type === "member_expression") {
779
+ const obj = fn.childForFieldName("object");
780
+ const prop = fn.childForFieldName("property");
781
+ if (obj?.text === "c" && prop?.text === "set") {
782
+ const args = node.childForFieldName("arguments");
783
+ const keyArg = args?.namedChildren[0];
784
+ if (keyArg) {
785
+ const key = stringLiteralValue(keyArg);
786
+ if (key) {
787
+ const cv = getOrCreate(key, false);
788
+ cv.set_points.push({
789
+ file,
790
+ line: node.startPosition.row + 1,
791
+ scope: "middleware",
792
+ via_context_storage: false,
793
+ condition: isInsideBranch(node) ? "conditional" : "always",
794
+ });
795
+ }
796
+ }
797
+ }
798
+ // c.get("key")
799
+ if (obj?.text === "c" && prop?.text === "get") {
800
+ const args = node.childForFieldName("arguments");
801
+ const keyArg = args?.namedChildren[0];
802
+ if (keyArg) {
803
+ const key = stringLiteralValue(keyArg);
804
+ if (key) {
805
+ const cv = getOrCreate(key, false);
806
+ cv.get_points.push({
807
+ file,
808
+ line: node.startPosition.row + 1,
809
+ scope: "handler",
810
+ via_context_storage: false,
811
+ condition: "always",
812
+ });
813
+ }
814
+ }
815
+ }
816
+ }
817
+ }
818
+ // c.var.key — member_expression chain: c.var.key → (c.var).key
819
+ if (node.type === "member_expression") {
820
+ const obj = node.childForFieldName("object");
821
+ const prop = node.childForFieldName("property");
822
+ if (obj?.type === "member_expression" && prop) {
823
+ const innerObj = obj.childForFieldName("object");
824
+ const innerProp = obj.childForFieldName("property");
825
+ if (innerObj?.text === "c" && innerProp?.text === "var") {
826
+ const cv = getOrCreate(prop.text, false);
827
+ cv.get_points.push({
828
+ file,
829
+ line: node.startPosition.row + 1,
830
+ scope: "handler",
831
+ via_context_storage: false,
832
+ condition: "always",
833
+ });
834
+ }
835
+ }
836
+ }
837
+ });
838
+ // Merge into model.context_vars
839
+ for (const cv of varsMap.values()) {
840
+ const existing = model.context_vars.find((e) => e.name === cv.name);
841
+ if (existing) {
842
+ existing.set_points.push(...cv.set_points);
843
+ existing.get_points.push(...cv.get_points);
844
+ }
845
+ else {
846
+ model.context_vars.push(cv);
847
+ }
848
+ }
849
+ }
850
+ /** Collect local array variable declarations: const adminChain = [authMw, tenantMw] */
851
+ collectArrayVars(root) {
852
+ const result = new Map();
853
+ const cursor = root.walk();
854
+ walk(cursor, (node) => {
855
+ if (node.type !== "variable_declarator")
856
+ return;
857
+ const nameNode = node.childForFieldName("name");
858
+ const valueNode = node.childForFieldName("value");
859
+ if (!nameNode || !valueNode || nameNode.type !== "identifier")
860
+ return;
861
+ if (valueNode.type !== "array")
862
+ return;
863
+ const items = [];
864
+ for (const el of valueNode.namedChildren) {
865
+ if (el.type === "identifier")
866
+ items.push(el.text);
867
+ }
868
+ if (items.length > 0)
869
+ result.set(nameNode.text, items);
870
+ });
871
+ return result;
872
+ }
873
+ /** Collect import source mapping: variableName → packageSpecifier */
874
+ collectImportSources(root) {
875
+ const result = new Map();
876
+ const cursor = root.walk();
877
+ walk(cursor, (node) => {
878
+ if (node.type !== "import_statement")
879
+ return;
880
+ const sourceNode = node.childForFieldName("source");
881
+ if (!sourceNode)
882
+ return;
883
+ const specifier = stringLiteralValue(sourceNode);
884
+ if (!specifier)
885
+ return;
886
+ const importClause = node.children.find((c) => c.type === "import_clause");
887
+ if (!importClause)
888
+ return;
889
+ for (const child of importClause.namedChildren) {
890
+ if (child.type === "identifier") {
891
+ result.set(child.text, specifier);
892
+ }
893
+ if (child.type === "named_imports") {
894
+ for (const spec of child.namedChildren) {
895
+ if (spec.type === "import_specifier") {
896
+ const alias = spec.childForFieldName("alias");
897
+ const name = spec.childForFieldName("name");
898
+ const varName = alias?.text ?? name?.text;
899
+ if (varName)
900
+ result.set(varName, specifier);
901
+ }
902
+ }
903
+ }
904
+ }
905
+ });
906
+ return result;
907
+ }
908
+ /**
909
+ * Walk for app.route("/prefix", subApp) calls.
910
+ * Resolves subApp import, recursively parses the file.
911
+ */
912
+ async walkRouteMounts(root, _file, parentPrefix, appVars, importMap, inFlight, parsedCache, model, localMounts) {
913
+ const mounts = [];
914
+ const cursor = root.walk();
915
+ walk(cursor, (node) => {
916
+ if (node.type !== "call_expression")
917
+ return;
918
+ const fnNode = node.childForFieldName("function");
919
+ const argsNode = node.childForFieldName("arguments");
920
+ if (!fnNode || !argsNode || fnNode.type !== "member_expression")
921
+ return;
922
+ const objectNode = fnNode.childForFieldName("object");
923
+ const propertyNode = fnNode.childForFieldName("property");
924
+ if (!objectNode || !propertyNode || objectNode.type !== "identifier")
925
+ return;
926
+ const ownerVar = objectNode.text;
927
+ if (!appVars[ownerVar])
928
+ return;
929
+ const methodName = propertyNode.text;
930
+ // app.mount("/legacy", handler) — non-Hono external handler mount
931
+ if (methodName === "mount") {
932
+ const args = argsNode.namedChildren;
933
+ if (args.length < 2)
934
+ return;
935
+ const pathArg = args[0];
936
+ if (!pathArg)
937
+ return;
938
+ const mountPath = stringLiteralValue(pathArg);
939
+ if (mountPath == null)
940
+ return;
941
+ const basePrefix = appVars[ownerVar]?.base_path || "";
942
+ const fullMountPath = joinPaths(parentPrefix || basePrefix, mountPath);
943
+ const mount = {
944
+ parent_var: ownerVar,
945
+ mount_path: fullMountPath,
946
+ child_var: "<external>",
947
+ child_file: "",
948
+ mount_type: "hono_mount",
949
+ };
950
+ model.mounts.push(mount);
951
+ localMounts.push(mount);
952
+ return;
953
+ }
954
+ if (methodName !== "route")
955
+ return;
956
+ const argList = argsNode.namedChildren;
957
+ if (argList.length < 2)
958
+ return;
959
+ const pathArg = argList[0];
960
+ const childArg = argList[1];
961
+ if (!pathArg || !childArg)
962
+ return;
963
+ const mountPath = stringLiteralValue(pathArg);
964
+ if (mountPath == null)
965
+ return;
966
+ const childVar = childArg.type === "identifier" ? childArg.text : null;
967
+ if (!childVar)
968
+ return;
969
+ mounts.push({
970
+ mountPath,
971
+ childVar,
972
+ line: node.startPosition.row + 1,
973
+ });
974
+ });
975
+ // Process mounts — async because recursive parse needs file I/O
976
+ for (const { mountPath, childVar } of mounts) {
977
+ // T6: Fallback for LOCAL sub-apps (declared in the same file, not
978
+ // imported). If the import map doesn't know this var but appVars does,
979
+ // the child lives in the parent file — use that path instead of "".
980
+ // Routes on the local sub-app were already captured by walkHttpRoutes
981
+ // on the same root, so we record the mount without re-parsing.
982
+ let childFile = importMap.get(childVar);
983
+ let resolvedViaLocal = false;
984
+ if (!childFile && appVars[childVar]) {
985
+ childFile = appVars[childVar].file;
986
+ resolvedViaLocal = true;
987
+ }
988
+ const fullMountPath = joinPaths(parentPrefix, mountPath);
989
+ const mount = {
990
+ parent_var: Object.keys(appVars)[0] ?? "app",
991
+ mount_path: fullMountPath,
992
+ child_var: childVar,
993
+ child_file: childFile ?? "",
994
+ mount_type: "hono_route",
995
+ };
996
+ model.mounts.push(mount);
997
+ localMounts.push(mount);
998
+ if (resolvedViaLocal) {
999
+ // Local sub-app — routes already live in the parent file, no recursion.
1000
+ continue;
1001
+ }
1002
+ if (childFile && existsSync(childFile)) {
1003
+ await this.parseFile(childFile, fullMountPath, inFlight, parsedCache, model);
1004
+ }
1005
+ else {
1006
+ model.skip_reasons.unresolved_import =
1007
+ (model.skip_reasons.unresolved_import ?? 0) + 1;
1008
+ }
1009
+ }
1010
+ }
1011
+ /** Detect Hono runtime from project files and source patterns. */
1012
+ async detectRuntime(entryFile) {
1013
+ const dir = path.dirname(entryFile);
1014
+ const projectRoot = path.dirname(dir); // assume src/ is one level down
1015
+ // Check for wrangler.toml → Cloudflare Workers
1016
+ if (existsSync(path.join(projectRoot, "wrangler.toml")) ||
1017
+ existsSync(path.join(dir, "wrangler.toml"))) {
1018
+ return "cloudflare";
1019
+ }
1020
+ let source;
1021
+ try {
1022
+ source = await readFile(entryFile, "utf-8");
1023
+ }
1024
+ catch {
1025
+ return "unknown";
1026
+ }
1027
+ if (source.includes("Deno.serve"))
1028
+ return "deno";
1029
+ if (source.includes("Bun.serve"))
1030
+ return "bun";
1031
+ if (source.includes("@hono/node-server") || source.includes("serve({ fetch"))
1032
+ return "node";
1033
+ if (source.includes("hono/aws-lambda") || source.includes("handle("))
1034
+ return "lambda";
1035
+ // T5: Advanced runtime detection — only runs when existing heuristics
1036
+ // produced "unknown". Checks Cloudflare Bindings type signals and
1037
+ // deployment-target config files in the project root.
1038
+ return this.detectRuntimeAdvanced(source, projectRoot);
1039
+ }
1040
+ /**
1041
+ * Fallback runtime detection. Runs after detectRuntime's primary signals
1042
+ * miss — additive by design so Phase 1 tests remain stable.
1043
+ *
1044
+ * Signal precedence (first match wins):
1045
+ * 1. Cloudflare Bindings type using KVNamespace/D1Database/R2Bucket/Queue
1046
+ * 2. vercel.json at project root
1047
+ * 3. netlify.toml OR netlify/functions/ dir at project root
1048
+ * 4. fly.toml at project root
1049
+ * 5. unknown
1050
+ */
1051
+ detectRuntimeAdvanced(source, projectRoot) {
1052
+ if (hasCloudflareBindingsType(source))
1053
+ return "cloudflare";
1054
+ if (existsSync(path.join(projectRoot, "vercel.json")))
1055
+ return "vercel";
1056
+ if (existsSync(path.join(projectRoot, "netlify.toml")) ||
1057
+ existsSync(path.join(projectRoot, "netlify", "functions"))) {
1058
+ return "netlify";
1059
+ }
1060
+ if (existsSync(path.join(projectRoot, "fly.toml")))
1061
+ return "fly";
1062
+ return "unknown";
1063
+ }
1064
+ /**
1065
+ * Extract env bindings from:
1066
+ * 1. c.env.IDENTIFIER member accesses
1067
+ * 2. Destructuring: const { A, B } = c.env
1068
+ * 3. Bindings type literal from Hono<{ Bindings: {...} }> or createFactory<{ Bindings: {...} }>
1069
+ */
1070
+ extractEnvBindings(root, source, model) {
1071
+ const bindings = new Set();
1072
+ // Pattern 1 & 2: Walk AST for c.env.X and const { X } = c.env
1073
+ const cursor = root.walk();
1074
+ walk(cursor, (node) => {
1075
+ // c.env.IDENTIFIER
1076
+ if (node.type === "member_expression") {
1077
+ const obj = node.childForFieldName("object");
1078
+ const prop = node.childForFieldName("property");
1079
+ if (obj?.type === "member_expression" && prop) {
1080
+ const innerObj = obj.childForFieldName("object");
1081
+ const innerProp = obj.childForFieldName("property");
1082
+ if (innerObj?.text === "c" && innerProp?.text === "env") {
1083
+ bindings.add(prop.text);
1084
+ }
1085
+ }
1086
+ }
1087
+ // const { DATABASE_URL, KV } = c.env
1088
+ if (node.type === "variable_declarator") {
1089
+ const nameNode = node.childForFieldName("name");
1090
+ const valueNode = node.childForFieldName("value");
1091
+ if (nameNode?.type === "object_pattern" && valueNode?.type === "member_expression") {
1092
+ const obj = valueNode.childForFieldName("object");
1093
+ const prop = valueNode.childForFieldName("property");
1094
+ if (obj?.text === "c" && prop?.text === "env") {
1095
+ for (const child of nameNode.namedChildren) {
1096
+ if (child.type === "shorthand_property_identifier_pattern" ||
1097
+ child.type === "shorthand_property_identifier") {
1098
+ bindings.add(child.text);
1099
+ }
1100
+ if (child.type === "pair_pattern") {
1101
+ const key = child.childForFieldName("key");
1102
+ if (key)
1103
+ bindings.add(key.text);
1104
+ }
1105
+ }
1106
+ }
1107
+ }
1108
+ }
1109
+ });
1110
+ // Pattern 3: Bindings type literal from source using regex (simpler than full AST type resolution)
1111
+ const bindingsMatch = source.match(/Bindings\s*:\s*\{([^}]+)\}/);
1112
+ if (bindingsMatch?.[1]) {
1113
+ const typeBody = bindingsMatch[1];
1114
+ const propRegex = /(\w+)\s*:/g;
1115
+ let m;
1116
+ while ((m = propRegex.exec(typeBody)) !== null) {
1117
+ if (m[1])
1118
+ bindings.add(m[1]);
1119
+ }
1120
+ }
1121
+ model.env_bindings = [...bindings].sort();
1122
+ }
1123
+ }
1124
+ // --- Utility functions ---
1125
+ function pickLanguage(file) {
1126
+ const ext = path.extname(file).toLowerCase();
1127
+ if (ext === ".tsx")
1128
+ return "tsx";
1129
+ if (ext === ".ts")
1130
+ return "typescript";
1131
+ if (ext === ".jsx" || ext === ".js")
1132
+ return "javascript";
1133
+ return "typescript";
1134
+ }
1135
+ function emptyModel(entryFile, skipReasons) {
1136
+ return {
1137
+ entry_file: entryFile,
1138
+ app_variables: {},
1139
+ routes: [],
1140
+ mounts: [],
1141
+ middleware_chains: [],
1142
+ context_vars: [],
1143
+ openapi_routes: [],
1144
+ rpc_exports: [],
1145
+ runtime: "unknown",
1146
+ env_bindings: [],
1147
+ files_used: [],
1148
+ extraction_status: Object.keys(skipReasons).length > 0 ? "partial" : "complete",
1149
+ skip_reasons: skipReasons,
1150
+ };
1151
+ }
1152
+ /**
1153
+ * Detect `<parentVar>.basePath("/prefix")` call expression.
1154
+ * Returns the parent variable name and prefix path, or null.
1155
+ */
1156
+ function extractBasePathCall(valueNode, knownVars) {
1157
+ if (valueNode.type !== "call_expression")
1158
+ return null;
1159
+ const fnNode = valueNode.childForFieldName("function");
1160
+ if (!fnNode || fnNode.type !== "member_expression")
1161
+ return null;
1162
+ const obj = fnNode.childForFieldName("object");
1163
+ const prop = fnNode.childForFieldName("property");
1164
+ if (!obj || !prop || obj.type !== "identifier")
1165
+ return null;
1166
+ if (prop.text !== "basePath")
1167
+ return null;
1168
+ if (!knownVars[obj.text])
1169
+ return null;
1170
+ const argsNode = valueNode.childForFieldName("arguments");
1171
+ const firstArg = argsNode?.namedChildren[0];
1172
+ if (!firstArg)
1173
+ return null;
1174
+ const prefix = stringLiteralValue(firstArg);
1175
+ if (prefix == null)
1176
+ return null;
1177
+ // Combine parent's base_path with the new prefix
1178
+ const parentBase = knownVars[obj.text]?.base_path || "";
1179
+ return { parentVar: obj.text, prefix: joinPaths(parentBase, prefix) };
1180
+ }
1181
+ /**
1182
+ * Parse regex constraints from Hono path parameters.
1183
+ * e.g., ":id{[0-9]+}" → { id: "[0-9]+" }
1184
+ */
1185
+ function parseRegexConstraints(rawPath) {
1186
+ const regex = /:(\w+)\{([^}]+)\}/g;
1187
+ let match;
1188
+ const constraints = {};
1189
+ let found = false;
1190
+ while ((match = regex.exec(rawPath)) !== null) {
1191
+ if (match[1] && match[2]) {
1192
+ constraints[match[1]] = match[2];
1193
+ found = true;
1194
+ }
1195
+ }
1196
+ return found ? constraints : undefined;
1197
+ }
1198
+ /** Check if a value is `createFactory(...)` or `createFactory<Env>(...)`. */
1199
+ function isCreateFactoryCall(valueNode) {
1200
+ if (valueNode.type !== "call_expression")
1201
+ return false;
1202
+ const fn = valueNode.childForFieldName("function");
1203
+ if (!fn)
1204
+ return false;
1205
+ // createFactory<...>()
1206
+ if (fn.type === "identifier" && fn.text === "createFactory")
1207
+ return true;
1208
+ // hono/factory.createFactory<...>()
1209
+ if (fn.type === "member_expression") {
1210
+ const prop = fn.childForFieldName("property");
1211
+ if (prop?.text === "createFactory")
1212
+ return true;
1213
+ }
1214
+ return false;
1215
+ }
1216
+ /** Check if a value is `<factoryVar>.createApp()`. */
1217
+ function isFactoryCreateApp(valueNode, factoryVars) {
1218
+ if (valueNode.type !== "call_expression")
1219
+ return false;
1220
+ const fn = valueNode.childForFieldName("function");
1221
+ if (!fn || fn.type !== "member_expression")
1222
+ return false;
1223
+ const obj = fn.childForFieldName("object");
1224
+ const prop = fn.childForFieldName("property");
1225
+ if (!obj || !prop || obj.type !== "identifier")
1226
+ return false;
1227
+ return factoryVars.has(obj.text) && prop.text === "createApp";
1228
+ }
1229
+ function classifyAppCreation(valueNode) {
1230
+ if (valueNode.type !== "new_expression")
1231
+ return null;
1232
+ const ctor = valueNode.childForFieldName("constructor");
1233
+ if (!ctor)
1234
+ return null;
1235
+ if (ctor.type === "identifier") {
1236
+ if (ctor.text === "Hono")
1237
+ return "new Hono";
1238
+ if (ctor.text === "OpenAPIHono")
1239
+ return "OpenAPIHono";
1240
+ }
1241
+ if (ctor.type === "member_expression") {
1242
+ const prop = ctor.childForFieldName("property");
1243
+ if (prop?.text === "Hono")
1244
+ return "new Hono";
1245
+ if (prop?.text === "OpenAPIHono")
1246
+ return "OpenAPIHono";
1247
+ }
1248
+ return null;
1249
+ }
1250
+ function stringLiteralValue(node) {
1251
+ if (node.type === "string") {
1252
+ const text = node.text;
1253
+ if (text.length < 2)
1254
+ return null;
1255
+ const quote = text[0];
1256
+ if (quote !== '"' && quote !== "'")
1257
+ return null;
1258
+ try {
1259
+ const normalized = quote === "'"
1260
+ ? `"${text.slice(1, -1).replace(/\\'/g, "'").replace(/"/g, '\\"')}"`
1261
+ : text;
1262
+ const parsed = JSON.parse(normalized);
1263
+ return typeof parsed === "string" ? parsed : null;
1264
+ }
1265
+ catch {
1266
+ return text.slice(1, -1);
1267
+ }
1268
+ }
1269
+ if (node.type === "template_string") {
1270
+ const hasInterpolation = node.namedChildren.some((c) => c.type === "template_substitution");
1271
+ if (hasInterpolation)
1272
+ return null;
1273
+ const text = node.text;
1274
+ if (text.length < 2)
1275
+ return null;
1276
+ return text.slice(1, -1);
1277
+ }
1278
+ return null;
1279
+ }
1280
+ function buildHandler(node, file) {
1281
+ const line = node.startPosition.row + 1;
1282
+ if (node.type === "arrow_function" ||
1283
+ node.type === "function_expression" ||
1284
+ node.type === "function") {
1285
+ return { name: "<inline>", inline: true, file, line };
1286
+ }
1287
+ if (node.type === "identifier") {
1288
+ return { name: node.text, inline: false, file, line };
1289
+ }
1290
+ return { name: "<inline>", inline: true, file, line };
1291
+ }
1292
+ /**
1293
+ * Resolve a relative import specifier to an absolute file path.
1294
+ * Tries: exact path, .ts, .js, /index.ts, /index.js extensions.
1295
+ */
1296
+ function resolveImportPath(fromFile, importSpecifier) {
1297
+ const dir = path.dirname(fromFile);
1298
+ const base = path.resolve(dir, importSpecifier);
1299
+ // Try with various extensions — .ts fixture files imported as .js in ESM
1300
+ const candidates = [
1301
+ base,
1302
+ base + ".ts",
1303
+ base + ".tsx",
1304
+ base.replace(/\.js$/, ".ts"),
1305
+ base.replace(/\.jsx$/, ".tsx"),
1306
+ path.join(base, "index.ts"),
1307
+ path.join(base, "index.js"),
1308
+ ];
1309
+ for (const candidate of candidates) {
1310
+ if (existsSync(candidate)) {
1311
+ return canonicalize(candidate);
1312
+ }
1313
+ }
1314
+ return null;
1315
+ }
1316
+ /**
1317
+ * Canonicalize path via realpath for consistent cache invalidation.
1318
+ * Falls back to the input path if the file doesn't exist.
1319
+ */
1320
+ function canonicalize(filePath) {
1321
+ try {
1322
+ return realpathSync.native(filePath);
1323
+ }
1324
+ catch {
1325
+ return filePath;
1326
+ }
1327
+ }
1328
+ /** Join a parent prefix with a child path, avoiding double/trailing slashes. */
1329
+ function joinPaths(prefix, childPath) {
1330
+ if (!prefix)
1331
+ return childPath;
1332
+ const p = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
1333
+ // In Hono, a sub-router's "/" matches the mount path exactly (no trailing slash)
1334
+ if (childPath === "/" || childPath === "")
1335
+ return p || "/";
1336
+ const c = childPath.startsWith("/") ? childPath : "/" + childPath;
1337
+ return p + c;
1338
+ }
1339
+ /**
1340
+ * Given the `consequence` of an if_statement, locate a middleware-call
1341
+ * expression of the form `mw(c, next)`. Walks the whole block (not just the
1342
+ * first statement) so patterns like
1343
+ *
1344
+ * if (cond) {
1345
+ * const auth = basicAuth({...});
1346
+ * return auth(c, next);
1347
+ * }
1348
+ *
1349
+ * are recognized. Only looks at top-level statements of the consequence —
1350
+ * does not descend into nested blocks. The candidate call must have >= 2
1351
+ * named arguments to heuristically match `mw(c, next)` shape.
1352
+ */
1353
+ function findMiddlewareCallInBlock(consequence) {
1354
+ const statements = consequence.type === "statement_block"
1355
+ ? consequence.namedChildren
1356
+ : [consequence];
1357
+ for (const stmt of statements) {
1358
+ let call = null;
1359
+ if (stmt.type === "return_statement") {
1360
+ const expr = stmt.namedChildren[0];
1361
+ if (expr)
1362
+ call = unwrapCallExpression(expr);
1363
+ }
1364
+ else if (stmt.type === "expression_statement") {
1365
+ const expr = stmt.namedChildren[0];
1366
+ if (expr)
1367
+ call = unwrapCallExpression(expr);
1368
+ }
1369
+ if (call && callHasAtLeastNArgs(call, 2))
1370
+ return call;
1371
+ }
1372
+ return null;
1373
+ }
1374
+ function callHasAtLeastNArgs(call, n) {
1375
+ const args = call.childForFieldName("arguments");
1376
+ return (args?.namedChildren.length ?? 0) >= n;
1377
+ }
1378
+ /**
1379
+ * Collect block-local alias declarations of the form
1380
+ * const X = <callee>(...)
1381
+ * into a Map<X, <callee>>. Used to resolve `const auth = basicAuth({...})` so
1382
+ * that `return auth(c, next)` reports "basicAuth" as the applied middleware.
1383
+ */
1384
+ function collectLocalAliases(consequence) {
1385
+ const map = new Map();
1386
+ const statements = consequence.type === "statement_block"
1387
+ ? consequence.namedChildren
1388
+ : [consequence];
1389
+ for (const stmt of statements) {
1390
+ if (stmt.type !== "lexical_declaration" && stmt.type !== "variable_declaration")
1391
+ continue;
1392
+ for (const declarator of stmt.namedChildren) {
1393
+ if (declarator.type !== "variable_declarator")
1394
+ continue;
1395
+ const nameNode = declarator.childForFieldName("name");
1396
+ const valueNode = declarator.childForFieldName("value");
1397
+ if (nameNode?.type !== "identifier" || !valueNode)
1398
+ continue;
1399
+ if (valueNode.type !== "call_expression")
1400
+ continue;
1401
+ const calleeName = extractCallCalleeName(valueNode);
1402
+ if (calleeName)
1403
+ map.set(nameNode.text, calleeName);
1404
+ }
1405
+ }
1406
+ return map;
1407
+ }
1408
+ /** Peel off `await ...` wrappers and return the underlying call_expression, if any. */
1409
+ function unwrapCallExpression(node) {
1410
+ let current = node;
1411
+ while (current.type === "await_expression") {
1412
+ const inner = current.namedChildren[0];
1413
+ if (!inner)
1414
+ return null;
1415
+ current = inner;
1416
+ }
1417
+ if (current.type === "call_expression")
1418
+ return current;
1419
+ return null;
1420
+ }
1421
+ /**
1422
+ * Extract the name of the middleware at the call site. Supports:
1423
+ * - `foo(c, next)` → "foo"
1424
+ * - `foo.bar(c, next)` → "bar"
1425
+ * - `auth(c, next)` where `auth` came from `const auth = basicAuth({...})`
1426
+ * → returns "auth"; T4 reports the local identifier, and a separate
1427
+ * def-use pass could resolve it further (out of scope here).
1428
+ * - `basicAuth({...})(c, next)` → "basicAuth" (outer callee of the inner call)
1429
+ */
1430
+ function extractCallCalleeName(callNode) {
1431
+ const fn = callNode.childForFieldName("function");
1432
+ if (!fn)
1433
+ return null;
1434
+ if (fn.type === "identifier")
1435
+ return fn.text;
1436
+ if (fn.type === "member_expression") {
1437
+ const prop = fn.childForFieldName("property");
1438
+ return prop?.text ?? null;
1439
+ }
1440
+ // `basicAuth({...})(c, next)` — fn itself is a call_expression
1441
+ if (fn.type === "call_expression") {
1442
+ const innerFn = fn.childForFieldName("function");
1443
+ if (innerFn?.type === "identifier")
1444
+ return innerFn.text;
1445
+ if (innerFn?.type === "member_expression") {
1446
+ const prop = innerFn.childForFieldName("property");
1447
+ return prop?.text ?? null;
1448
+ }
1449
+ }
1450
+ return null;
1451
+ }
1452
+ /**
1453
+ * Classify an if-condition into method / header / path / custom by looking at
1454
+ * the leftmost member_expression chain. Keeps the check cheap and deterministic.
1455
+ */
1456
+ function classifyConditionType(condition) {
1457
+ const text = condition.text;
1458
+ // Normalize for substring checks
1459
+ if (/c\.req\.method\b/.test(text))
1460
+ return "method";
1461
+ if (/c\.req\.header\s*\(/.test(text) || /c\.req\.headers\b/.test(text))
1462
+ return "header";
1463
+ if (/c\.req\.path\b/.test(text) || /c\.req\.url\b/.test(text))
1464
+ return "path";
1465
+ return "custom";
1466
+ }
1467
+ /**
1468
+ * T5: Cloudflare Workers signal. These types ship with
1469
+ * @cloudflare/workers-types and never appear in non-Worker codebases, so
1470
+ * a word-boundary match on the source is sufficient — we do NOT need to
1471
+ * correlate with a Bindings declaration (which may be `type Bindings = {}`,
1472
+ * `interface Bindings {}`, inline in `Hono<{Bindings: {}}>`, or split across
1473
+ * nested type bodies with inner `}`). Removing the Bindings-block coupling
1474
+ * also fixes the first-`}` truncation that the initial regex had.
1475
+ *
1476
+ * Generic names like `Fetcher` and `Service` were dropped — they collide
1477
+ * with common non-CF type names in application code.
1478
+ */
1479
+ const CF_WORKER_TYPES = [
1480
+ "KVNamespace",
1481
+ "D1Database",
1482
+ "R2Bucket",
1483
+ "DurableObjectNamespace",
1484
+ "AnalyticsEngineDataset",
1485
+ ];
1486
+ function hasCloudflareBindingsType(source) {
1487
+ for (const cfType of CF_WORKER_TYPES) {
1488
+ const re = new RegExp(`\\b${cfType}\\b`);
1489
+ if (re.test(source))
1490
+ return true;
1491
+ }
1492
+ return false;
1493
+ }
1494
+ /** Check if a node is inside a conditional branch (if/switch/try body). */
1495
+ function isInsideBranch(node) {
1496
+ let current = node.parent;
1497
+ while (current) {
1498
+ if (current.type === "if_statement" ||
1499
+ current.type === "switch_case" ||
1500
+ current.type === "catch_clause" ||
1501
+ current.type === "ternary_expression") {
1502
+ return true;
1503
+ }
1504
+ // Stop at function boundary — we only care about branches within the function
1505
+ if (current.type === "arrow_function" ||
1506
+ current.type === "function_declaration" ||
1507
+ current.type === "function_expression" ||
1508
+ current.type === "method_definition") {
1509
+ break;
1510
+ }
1511
+ current = current.parent;
1512
+ }
1513
+ return false;
1514
+ }
1515
+ const MAX_WALK_DEPTH = 500;
1516
+ function walk(cursor, visit, depth = 0) {
1517
+ if (depth > MAX_WALK_DEPTH)
1518
+ return;
1519
+ visit(cursor.currentNode);
1520
+ if (cursor.gotoFirstChild()) {
1521
+ do {
1522
+ walk(cursor, visit, depth + 1);
1523
+ } while (cursor.gotoNextSibling());
1524
+ cursor.gotoParent();
1525
+ }
1526
+ }
1527
+ //# sourceMappingURL=hono.js.map