openlore 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (634) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +268 -0
  3. package/dist/api/analyze.d.ts +17 -0
  4. package/dist/api/analyze.d.ts.map +1 -0
  5. package/dist/api/analyze.js +143 -0
  6. package/dist/api/analyze.js.map +1 -0
  7. package/dist/api/audit.d.ts +10 -0
  8. package/dist/api/audit.d.ts.map +1 -0
  9. package/dist/api/audit.js +117 -0
  10. package/dist/api/audit.js.map +1 -0
  11. package/dist/api/decisions.d.ts +55 -0
  12. package/dist/api/decisions.d.ts.map +1 -0
  13. package/dist/api/decisions.js +157 -0
  14. package/dist/api/decisions.js.map +1 -0
  15. package/dist/api/drift.d.ts +21 -0
  16. package/dist/api/drift.d.ts.map +1 -0
  17. package/dist/api/drift.js +152 -0
  18. package/dist/api/drift.js.map +1 -0
  19. package/dist/api/generate.d.ts +18 -0
  20. package/dist/api/generate.d.ts.map +1 -0
  21. package/dist/api/generate.js +259 -0
  22. package/dist/api/generate.js.map +1 -0
  23. package/dist/api/index.d.ts +41 -0
  24. package/dist/api/index.d.ts.map +1 -0
  25. package/dist/api/index.js +34 -0
  26. package/dist/api/index.js.map +1 -0
  27. package/dist/api/init.d.ts +18 -0
  28. package/dist/api/init.d.ts.map +1 -0
  29. package/dist/api/init.js +83 -0
  30. package/dist/api/init.js.map +1 -0
  31. package/dist/api/run.d.ts +19 -0
  32. package/dist/api/run.d.ts.map +1 -0
  33. package/dist/api/run.js +312 -0
  34. package/dist/api/run.js.map +1 -0
  35. package/dist/api/specs.d.ts +49 -0
  36. package/dist/api/specs.d.ts.map +1 -0
  37. package/dist/api/specs.js +137 -0
  38. package/dist/api/specs.js.map +1 -0
  39. package/dist/api/types.d.ts +201 -0
  40. package/dist/api/types.d.ts.map +1 -0
  41. package/dist/api/types.js +9 -0
  42. package/dist/api/types.js.map +1 -0
  43. package/dist/api/verify.d.ts +20 -0
  44. package/dist/api/verify.d.ts.map +1 -0
  45. package/dist/api/verify.js +117 -0
  46. package/dist/api/verify.js.map +1 -0
  47. package/dist/cli/commands/analyze.d.ts +30 -0
  48. package/dist/cli/commands/analyze.d.ts.map +1 -0
  49. package/dist/cli/commands/analyze.js +683 -0
  50. package/dist/cli/commands/analyze.js.map +1 -0
  51. package/dist/cli/commands/audit.d.ts +9 -0
  52. package/dist/cli/commands/audit.d.ts.map +1 -0
  53. package/dist/cli/commands/audit.js +98 -0
  54. package/dist/cli/commands/audit.js.map +1 -0
  55. package/dist/cli/commands/decisions.d.ts +16 -0
  56. package/dist/cli/commands/decisions.d.ts.map +1 -0
  57. package/dist/cli/commands/decisions.js +864 -0
  58. package/dist/cli/commands/decisions.js.map +1 -0
  59. package/dist/cli/commands/digest.d.ts +9 -0
  60. package/dist/cli/commands/digest.d.ts.map +1 -0
  61. package/dist/cli/commands/digest.js +61 -0
  62. package/dist/cli/commands/digest.js.map +1 -0
  63. package/dist/cli/commands/doctor.d.ts +9 -0
  64. package/dist/cli/commands/doctor.d.ts.map +1 -0
  65. package/dist/cli/commands/doctor.js +398 -0
  66. package/dist/cli/commands/doctor.js.map +1 -0
  67. package/dist/cli/commands/drift.d.ts +9 -0
  68. package/dist/cli/commands/drift.d.ts.map +1 -0
  69. package/dist/cli/commands/drift.js +550 -0
  70. package/dist/cli/commands/drift.js.map +1 -0
  71. package/dist/cli/commands/generate.d.ts +9 -0
  72. package/dist/cli/commands/generate.d.ts.map +1 -0
  73. package/dist/cli/commands/generate.js +565 -0
  74. package/dist/cli/commands/generate.js.map +1 -0
  75. package/dist/cli/commands/init.d.ts +9 -0
  76. package/dist/cli/commands/init.d.ts.map +1 -0
  77. package/dist/cli/commands/init.js +173 -0
  78. package/dist/cli/commands/init.js.map +1 -0
  79. package/dist/cli/commands/mcp.d.ts +2235 -0
  80. package/dist/cli/commands/mcp.d.ts.map +1 -0
  81. package/dist/cli/commands/mcp.js +1384 -0
  82. package/dist/cli/commands/mcp.js.map +1 -0
  83. package/dist/cli/commands/refresh-stories.d.ts +10 -0
  84. package/dist/cli/commands/refresh-stories.d.ts.map +1 -0
  85. package/dist/cli/commands/refresh-stories.js +314 -0
  86. package/dist/cli/commands/refresh-stories.js.map +1 -0
  87. package/dist/cli/commands/run.d.ts +9 -0
  88. package/dist/cli/commands/run.d.ts.map +1 -0
  89. package/dist/cli/commands/run.js +459 -0
  90. package/dist/cli/commands/run.js.map +1 -0
  91. package/dist/cli/commands/setup.d.ts +19 -0
  92. package/dist/cli/commands/setup.d.ts.map +1 -0
  93. package/dist/cli/commands/setup.js +355 -0
  94. package/dist/cli/commands/setup.js.map +1 -0
  95. package/dist/cli/commands/test.d.ts +22 -0
  96. package/dist/cli/commands/test.d.ts.map +1 -0
  97. package/dist/cli/commands/test.js +180 -0
  98. package/dist/cli/commands/test.js.map +1 -0
  99. package/dist/cli/commands/verify.d.ts +9 -0
  100. package/dist/cli/commands/verify.d.ts.map +1 -0
  101. package/dist/cli/commands/verify.js +383 -0
  102. package/dist/cli/commands/verify.js.map +1 -0
  103. package/dist/cli/commands/view.d.ts +13 -0
  104. package/dist/cli/commands/view.d.ts.map +1 -0
  105. package/dist/cli/commands/view.js +547 -0
  106. package/dist/cli/commands/view.js.map +1 -0
  107. package/dist/cli/index.d.ts +9 -0
  108. package/dist/cli/index.d.ts.map +1 -0
  109. package/dist/cli/index.js +118 -0
  110. package/dist/cli/index.js.map +1 -0
  111. package/dist/cli/tui-approval.d.ts +11 -0
  112. package/dist/cli/tui-approval.d.ts.map +1 -0
  113. package/dist/cli/tui-approval.js +129 -0
  114. package/dist/cli/tui-approval.js.map +1 -0
  115. package/dist/constants.d.ts +314 -0
  116. package/dist/constants.d.ts.map +1 -0
  117. package/dist/constants.js +382 -0
  118. package/dist/constants.js.map +1 -0
  119. package/dist/core/analyzer/ai-config-generator.d.ts +54 -0
  120. package/dist/core/analyzer/ai-config-generator.d.ts.map +1 -0
  121. package/dist/core/analyzer/ai-config-generator.js +98 -0
  122. package/dist/core/analyzer/ai-config-generator.js.map +1 -0
  123. package/dist/core/analyzer/architecture-writer.d.ts +67 -0
  124. package/dist/core/analyzer/architecture-writer.d.ts.map +1 -0
  125. package/dist/core/analyzer/architecture-writer.js +209 -0
  126. package/dist/core/analyzer/architecture-writer.js.map +1 -0
  127. package/dist/core/analyzer/artifact-generator.d.ts +261 -0
  128. package/dist/core/analyzer/artifact-generator.d.ts.map +1 -0
  129. package/dist/core/analyzer/artifact-generator.js +909 -0
  130. package/dist/core/analyzer/artifact-generator.js.map +1 -0
  131. package/dist/core/analyzer/ast-chunker.d.ts +24 -0
  132. package/dist/core/analyzer/ast-chunker.d.ts.map +1 -0
  133. package/dist/core/analyzer/ast-chunker.js +198 -0
  134. package/dist/core/analyzer/ast-chunker.js.map +1 -0
  135. package/dist/core/analyzer/call-graph.d.ts +162 -0
  136. package/dist/core/analyzer/call-graph.d.ts.map +1 -0
  137. package/dist/core/analyzer/call-graph.js +2040 -0
  138. package/dist/core/analyzer/call-graph.js.map +1 -0
  139. package/dist/core/analyzer/code-shaper.d.ts +33 -0
  140. package/dist/core/analyzer/code-shaper.d.ts.map +1 -0
  141. package/dist/core/analyzer/code-shaper.js +154 -0
  142. package/dist/core/analyzer/code-shaper.js.map +1 -0
  143. package/dist/core/analyzer/codebase-digest.d.ts +40 -0
  144. package/dist/core/analyzer/codebase-digest.d.ts.map +1 -0
  145. package/dist/core/analyzer/codebase-digest.js +195 -0
  146. package/dist/core/analyzer/codebase-digest.js.map +1 -0
  147. package/dist/core/analyzer/cpp-header-resolver.d.ts +30 -0
  148. package/dist/core/analyzer/cpp-header-resolver.d.ts.map +1 -0
  149. package/dist/core/analyzer/cpp-header-resolver.js +71 -0
  150. package/dist/core/analyzer/cpp-header-resolver.js.map +1 -0
  151. package/dist/core/analyzer/dependency-graph.d.ts +230 -0
  152. package/dist/core/analyzer/dependency-graph.d.ts.map +1 -0
  153. package/dist/core/analyzer/dependency-graph.js +752 -0
  154. package/dist/core/analyzer/dependency-graph.js.map +1 -0
  155. package/dist/core/analyzer/duplicate-detector.d.ts +52 -0
  156. package/dist/core/analyzer/duplicate-detector.d.ts.map +1 -0
  157. package/dist/core/analyzer/duplicate-detector.js +289 -0
  158. package/dist/core/analyzer/duplicate-detector.js.map +1 -0
  159. package/dist/core/analyzer/embedding-service.d.ts +56 -0
  160. package/dist/core/analyzer/embedding-service.d.ts.map +1 -0
  161. package/dist/core/analyzer/embedding-service.js +118 -0
  162. package/dist/core/analyzer/embedding-service.js.map +1 -0
  163. package/dist/core/analyzer/env-extractor.d.ts +33 -0
  164. package/dist/core/analyzer/env-extractor.d.ts.map +1 -0
  165. package/dist/core/analyzer/env-extractor.js +196 -0
  166. package/dist/core/analyzer/env-extractor.js.map +1 -0
  167. package/dist/core/analyzer/external-packages.d.ts +20 -0
  168. package/dist/core/analyzer/external-packages.d.ts.map +1 -0
  169. package/dist/core/analyzer/external-packages.js +175 -0
  170. package/dist/core/analyzer/external-packages.js.map +1 -0
  171. package/dist/core/analyzer/file-walker.d.ts +78 -0
  172. package/dist/core/analyzer/file-walker.d.ts.map +1 -0
  173. package/dist/core/analyzer/file-walker.js +532 -0
  174. package/dist/core/analyzer/file-walker.js.map +1 -0
  175. package/dist/core/analyzer/function-registry-trie.d.ts +21 -0
  176. package/dist/core/analyzer/function-registry-trie.d.ts.map +1 -0
  177. package/dist/core/analyzer/function-registry-trie.js +39 -0
  178. package/dist/core/analyzer/function-registry-trie.js.map +1 -0
  179. package/dist/core/analyzer/http-route-parser.d.ts +152 -0
  180. package/dist/core/analyzer/http-route-parser.d.ts.map +1 -0
  181. package/dist/core/analyzer/http-route-parser.js +971 -0
  182. package/dist/core/analyzer/http-route-parser.js.map +1 -0
  183. package/dist/core/analyzer/import-parser.d.ts +100 -0
  184. package/dist/core/analyzer/import-parser.d.ts.map +1 -0
  185. package/dist/core/analyzer/import-parser.js +952 -0
  186. package/dist/core/analyzer/import-parser.js.map +1 -0
  187. package/dist/core/analyzer/import-resolver-bridge.d.ts +25 -0
  188. package/dist/core/analyzer/import-resolver-bridge.d.ts.map +1 -0
  189. package/dist/core/analyzer/import-resolver-bridge.js +99 -0
  190. package/dist/core/analyzer/import-resolver-bridge.js.map +1 -0
  191. package/dist/core/analyzer/index.d.ts +10 -0
  192. package/dist/core/analyzer/index.d.ts.map +1 -0
  193. package/dist/core/analyzer/index.js +10 -0
  194. package/dist/core/analyzer/index.js.map +1 -0
  195. package/dist/core/analyzer/middleware-extractor.d.ts +29 -0
  196. package/dist/core/analyzer/middleware-extractor.d.ts.map +1 -0
  197. package/dist/core/analyzer/middleware-extractor.js +195 -0
  198. package/dist/core/analyzer/middleware-extractor.js.map +1 -0
  199. package/dist/core/analyzer/refactor-analyzer.d.ts +83 -0
  200. package/dist/core/analyzer/refactor-analyzer.d.ts.map +1 -0
  201. package/dist/core/analyzer/refactor-analyzer.js +351 -0
  202. package/dist/core/analyzer/refactor-analyzer.js.map +1 -0
  203. package/dist/core/analyzer/repository-mapper.d.ts +150 -0
  204. package/dist/core/analyzer/repository-mapper.d.ts.map +1 -0
  205. package/dist/core/analyzer/repository-mapper.js +740 -0
  206. package/dist/core/analyzer/repository-mapper.js.map +1 -0
  207. package/dist/core/analyzer/schema-extractor.d.ts +41 -0
  208. package/dist/core/analyzer/schema-extractor.d.ts.map +1 -0
  209. package/dist/core/analyzer/schema-extractor.js +229 -0
  210. package/dist/core/analyzer/schema-extractor.js.map +1 -0
  211. package/dist/core/analyzer/signature-extractor.d.ts +31 -0
  212. package/dist/core/analyzer/signature-extractor.d.ts.map +1 -0
  213. package/dist/core/analyzer/signature-extractor.js +675 -0
  214. package/dist/core/analyzer/signature-extractor.js.map +1 -0
  215. package/dist/core/analyzer/significance-scorer.d.ts +79 -0
  216. package/dist/core/analyzer/significance-scorer.d.ts.map +1 -0
  217. package/dist/core/analyzer/significance-scorer.js +407 -0
  218. package/dist/core/analyzer/significance-scorer.js.map +1 -0
  219. package/dist/core/analyzer/spec-snapshot-generator.d.ts +17 -0
  220. package/dist/core/analyzer/spec-snapshot-generator.d.ts.map +1 -0
  221. package/dist/core/analyzer/spec-snapshot-generator.js +201 -0
  222. package/dist/core/analyzer/spec-snapshot-generator.js.map +1 -0
  223. package/dist/core/analyzer/spec-vector-index.d.ts +68 -0
  224. package/dist/core/analyzer/spec-vector-index.d.ts.map +1 -0
  225. package/dist/core/analyzer/spec-vector-index.js +340 -0
  226. package/dist/core/analyzer/spec-vector-index.js.map +1 -0
  227. package/dist/core/analyzer/subgraph-extractor.d.ts +51 -0
  228. package/dist/core/analyzer/subgraph-extractor.d.ts.map +1 -0
  229. package/dist/core/analyzer/subgraph-extractor.js +147 -0
  230. package/dist/core/analyzer/subgraph-extractor.js.map +1 -0
  231. package/dist/core/analyzer/type-inference-engine.d.ts +23 -0
  232. package/dist/core/analyzer/type-inference-engine.d.ts.map +1 -0
  233. package/dist/core/analyzer/type-inference-engine.js +130 -0
  234. package/dist/core/analyzer/type-inference-engine.js.map +1 -0
  235. package/dist/core/analyzer/ui-component-extractor.d.ts +43 -0
  236. package/dist/core/analyzer/ui-component-extractor.d.ts.map +1 -0
  237. package/dist/core/analyzer/ui-component-extractor.js +245 -0
  238. package/dist/core/analyzer/ui-component-extractor.js.map +1 -0
  239. package/dist/core/analyzer/unified-search.d.ts +116 -0
  240. package/dist/core/analyzer/unified-search.d.ts.map +1 -0
  241. package/dist/core/analyzer/unified-search.js +231 -0
  242. package/dist/core/analyzer/unified-search.js.map +1 -0
  243. package/dist/core/analyzer/vector-index.d.ts +92 -0
  244. package/dist/core/analyzer/vector-index.d.ts.map +1 -0
  245. package/dist/core/analyzer/vector-index.js +451 -0
  246. package/dist/core/analyzer/vector-index.js.map +1 -0
  247. package/dist/core/decisions/consolidator.d.ts +14 -0
  248. package/dist/core/decisions/consolidator.d.ts.map +1 -0
  249. package/dist/core/decisions/consolidator.js +169 -0
  250. package/dist/core/decisions/consolidator.js.map +1 -0
  251. package/dist/core/decisions/extractor.d.ts +26 -0
  252. package/dist/core/decisions/extractor.d.ts.map +1 -0
  253. package/dist/core/decisions/extractor.js +156 -0
  254. package/dist/core/decisions/extractor.js.map +1 -0
  255. package/dist/core/decisions/index.d.ts +19 -0
  256. package/dist/core/decisions/index.d.ts.map +1 -0
  257. package/dist/core/decisions/index.js +16 -0
  258. package/dist/core/decisions/index.js.map +1 -0
  259. package/dist/core/decisions/store.d.ts +36 -0
  260. package/dist/core/decisions/store.d.ts.map +1 -0
  261. package/dist/core/decisions/store.js +109 -0
  262. package/dist/core/decisions/store.js.map +1 -0
  263. package/dist/core/decisions/syncer.d.ts +27 -0
  264. package/dist/core/decisions/syncer.d.ts.map +1 -0
  265. package/dist/core/decisions/syncer.js +214 -0
  266. package/dist/core/decisions/syncer.js.map +1 -0
  267. package/dist/core/decisions/verifier.d.ts +20 -0
  268. package/dist/core/decisions/verifier.d.ts.map +1 -0
  269. package/dist/core/decisions/verifier.js +115 -0
  270. package/dist/core/decisions/verifier.js.map +1 -0
  271. package/dist/core/digest/digest-generator.d.ts +29 -0
  272. package/dist/core/digest/digest-generator.d.ts.map +1 -0
  273. package/dist/core/digest/digest-generator.js +181 -0
  274. package/dist/core/digest/digest-generator.js.map +1 -0
  275. package/dist/core/drift/drift-detector.d.ts +102 -0
  276. package/dist/core/drift/drift-detector.d.ts.map +1 -0
  277. package/dist/core/drift/drift-detector.js +598 -0
  278. package/dist/core/drift/drift-detector.js.map +1 -0
  279. package/dist/core/drift/git-diff.d.ts +60 -0
  280. package/dist/core/drift/git-diff.d.ts.map +1 -0
  281. package/dist/core/drift/git-diff.js +383 -0
  282. package/dist/core/drift/git-diff.js.map +1 -0
  283. package/dist/core/drift/index.d.ts +12 -0
  284. package/dist/core/drift/index.d.ts.map +1 -0
  285. package/dist/core/drift/index.js +9 -0
  286. package/dist/core/drift/index.js.map +1 -0
  287. package/dist/core/drift/spec-mapper.d.ts +73 -0
  288. package/dist/core/drift/spec-mapper.d.ts.map +1 -0
  289. package/dist/core/drift/spec-mapper.js +353 -0
  290. package/dist/core/drift/spec-mapper.js.map +1 -0
  291. package/dist/core/drift/test-suggester.d.ts +18 -0
  292. package/dist/core/drift/test-suggester.d.ts.map +1 -0
  293. package/dist/core/drift/test-suggester.js +107 -0
  294. package/dist/core/drift/test-suggester.js.map +1 -0
  295. package/dist/core/generator/adr-generator.d.ts +32 -0
  296. package/dist/core/generator/adr-generator.d.ts.map +1 -0
  297. package/dist/core/generator/adr-generator.js +192 -0
  298. package/dist/core/generator/adr-generator.js.map +1 -0
  299. package/dist/core/generator/index.d.ts +9 -0
  300. package/dist/core/generator/index.d.ts.map +1 -0
  301. package/dist/core/generator/index.js +12 -0
  302. package/dist/core/generator/index.js.map +1 -0
  303. package/dist/core/generator/mapping-generator.d.ts +54 -0
  304. package/dist/core/generator/mapping-generator.d.ts.map +1 -0
  305. package/dist/core/generator/mapping-generator.js +240 -0
  306. package/dist/core/generator/mapping-generator.js.map +1 -0
  307. package/dist/core/generator/openspec-compat.d.ts +160 -0
  308. package/dist/core/generator/openspec-compat.d.ts.map +1 -0
  309. package/dist/core/generator/openspec-compat.js +524 -0
  310. package/dist/core/generator/openspec-compat.js.map +1 -0
  311. package/dist/core/generator/openspec-format-generator.d.ts +131 -0
  312. package/dist/core/generator/openspec-format-generator.d.ts.map +1 -0
  313. package/dist/core/generator/openspec-format-generator.js +963 -0
  314. package/dist/core/generator/openspec-format-generator.js.map +1 -0
  315. package/dist/core/generator/openspec-writer.d.ts +130 -0
  316. package/dist/core/generator/openspec-writer.d.ts.map +1 -0
  317. package/dist/core/generator/openspec-writer.js +404 -0
  318. package/dist/core/generator/openspec-writer.js.map +1 -0
  319. package/dist/core/generator/prompts.d.ts +35 -0
  320. package/dist/core/generator/prompts.d.ts.map +1 -0
  321. package/dist/core/generator/prompts.js +212 -0
  322. package/dist/core/generator/prompts.js.map +1 -0
  323. package/dist/core/generator/rag-manifest-generator.d.ts +37 -0
  324. package/dist/core/generator/rag-manifest-generator.d.ts.map +1 -0
  325. package/dist/core/generator/rag-manifest-generator.js +134 -0
  326. package/dist/core/generator/rag-manifest-generator.js.map +1 -0
  327. package/dist/core/generator/schemas.d.ts +365 -0
  328. package/dist/core/generator/schemas.d.ts.map +1 -0
  329. package/dist/core/generator/schemas.js +190 -0
  330. package/dist/core/generator/schemas.js.map +1 -0
  331. package/dist/core/generator/spec-pipeline.d.ts +123 -0
  332. package/dist/core/generator/spec-pipeline.d.ts.map +1 -0
  333. package/dist/core/generator/spec-pipeline.js +699 -0
  334. package/dist/core/generator/spec-pipeline.js.map +1 -0
  335. package/dist/core/generator/stages/stage1-survey.d.ts +19 -0
  336. package/dist/core/generator/stages/stage1-survey.d.ts.map +1 -0
  337. package/dist/core/generator/stages/stage1-survey.js +171 -0
  338. package/dist/core/generator/stages/stage1-survey.js.map +1 -0
  339. package/dist/core/generator/stages/stage2-entities.d.ts +11 -0
  340. package/dist/core/generator/stages/stage2-entities.d.ts.map +1 -0
  341. package/dist/core/generator/stages/stage2-entities.js +74 -0
  342. package/dist/core/generator/stages/stage2-entities.js.map +1 -0
  343. package/dist/core/generator/stages/stage3-services.d.ts +11 -0
  344. package/dist/core/generator/stages/stage3-services.d.ts.map +1 -0
  345. package/dist/core/generator/stages/stage3-services.js +85 -0
  346. package/dist/core/generator/stages/stage3-services.js.map +1 -0
  347. package/dist/core/generator/stages/stage4-api.d.ts +11 -0
  348. package/dist/core/generator/stages/stage4-api.d.ts.map +1 -0
  349. package/dist/core/generator/stages/stage4-api.js +72 -0
  350. package/dist/core/generator/stages/stage4-api.js.map +1 -0
  351. package/dist/core/generator/stages/stage5-architecture.d.ts +11 -0
  352. package/dist/core/generator/stages/stage5-architecture.d.ts.map +1 -0
  353. package/dist/core/generator/stages/stage5-architecture.js +75 -0
  354. package/dist/core/generator/stages/stage5-architecture.js.map +1 -0
  355. package/dist/core/generator/stages/stage6-adr.d.ts +8 -0
  356. package/dist/core/generator/stages/stage6-adr.d.ts.map +1 -0
  357. package/dist/core/generator/stages/stage6-adr.js +47 -0
  358. package/dist/core/generator/stages/stage6-adr.js.map +1 -0
  359. package/dist/core/services/chat-agent.d.ts +50 -0
  360. package/dist/core/services/chat-agent.d.ts.map +1 -0
  361. package/dist/core/services/chat-agent.js +369 -0
  362. package/dist/core/services/chat-agent.js.map +1 -0
  363. package/dist/core/services/chat-tools.d.ts +32 -0
  364. package/dist/core/services/chat-tools.d.ts.map +1 -0
  365. package/dist/core/services/chat-tools.js +494 -0
  366. package/dist/core/services/chat-tools.js.map +1 -0
  367. package/dist/core/services/config-manager.d.ts +61 -0
  368. package/dist/core/services/config-manager.d.ts.map +1 -0
  369. package/dist/core/services/config-manager.js +149 -0
  370. package/dist/core/services/config-manager.js.map +1 -0
  371. package/dist/core/services/edge-store.d.ts +57 -0
  372. package/dist/core/services/edge-store.d.ts.map +1 -0
  373. package/dist/core/services/edge-store.js +419 -0
  374. package/dist/core/services/edge-store.js.map +1 -0
  375. package/dist/core/services/gitignore-manager.d.ts +29 -0
  376. package/dist/core/services/gitignore-manager.d.ts.map +1 -0
  377. package/dist/core/services/gitignore-manager.js +95 -0
  378. package/dist/core/services/gitignore-manager.js.map +1 -0
  379. package/dist/core/services/index.d.ts +8 -0
  380. package/dist/core/services/index.d.ts.map +1 -0
  381. package/dist/core/services/index.js +8 -0
  382. package/dist/core/services/index.js.map +1 -0
  383. package/dist/core/services/llm-service.d.ts +379 -0
  384. package/dist/core/services/llm-service.d.ts.map +1 -0
  385. package/dist/core/services/llm-service.js +1553 -0
  386. package/dist/core/services/llm-service.js.map +1 -0
  387. package/dist/core/services/mcp-handlers/analysis.d.ts +127 -0
  388. package/dist/core/services/mcp-handlers/analysis.d.ts.map +1 -0
  389. package/dist/core/services/mcp-handlers/analysis.js +1185 -0
  390. package/dist/core/services/mcp-handlers/analysis.js.map +1 -0
  391. package/dist/core/services/mcp-handlers/change.d.ts +14 -0
  392. package/dist/core/services/mcp-handlers/change.d.ts.map +1 -0
  393. package/dist/core/services/mcp-handlers/change.js +416 -0
  394. package/dist/core/services/mcp-handlers/change.js.map +1 -0
  395. package/dist/core/services/mcp-handlers/decisions.d.ts +16 -0
  396. package/dist/core/services/mcp-handlers/decisions.d.ts.map +1 -0
  397. package/dist/core/services/mcp-handlers/decisions.js +239 -0
  398. package/dist/core/services/mcp-handlers/decisions.js.map +1 -0
  399. package/dist/core/services/mcp-handlers/graph.d.ts +94 -0
  400. package/dist/core/services/mcp-handlers/graph.d.ts.map +1 -0
  401. package/dist/core/services/mcp-handlers/graph.js +693 -0
  402. package/dist/core/services/mcp-handlers/graph.js.map +1 -0
  403. package/dist/core/services/mcp-handlers/orient.d.ts +17 -0
  404. package/dist/core/services/mcp-handlers/orient.d.ts.map +1 -0
  405. package/dist/core/services/mcp-handlers/orient.js +357 -0
  406. package/dist/core/services/mcp-handlers/orient.js.map +1 -0
  407. package/dist/core/services/mcp-handlers/semantic.d.ts +66 -0
  408. package/dist/core/services/mcp-handlers/semantic.d.ts.map +1 -0
  409. package/dist/core/services/mcp-handlers/semantic.js +432 -0
  410. package/dist/core/services/mcp-handlers/semantic.js.map +1 -0
  411. package/dist/core/services/mcp-handlers/utils.d.ts +85 -0
  412. package/dist/core/services/mcp-handlers/utils.d.ts.map +1 -0
  413. package/dist/core/services/mcp-handlers/utils.js +262 -0
  414. package/dist/core/services/mcp-handlers/utils.js.map +1 -0
  415. package/dist/core/services/mcp-watcher.d.ts +41 -0
  416. package/dist/core/services/mcp-watcher.d.ts.map +1 -0
  417. package/dist/core/services/mcp-watcher.js +254 -0
  418. package/dist/core/services/mcp-watcher.js.map +1 -0
  419. package/dist/core/services/project-detector.d.ts +32 -0
  420. package/dist/core/services/project-detector.d.ts.map +1 -0
  421. package/dist/core/services/project-detector.js +100 -0
  422. package/dist/core/services/project-detector.js.map +1 -0
  423. package/dist/core/test-generator/coverage-analyzer.d.ts +27 -0
  424. package/dist/core/test-generator/coverage-analyzer.d.ts.map +1 -0
  425. package/dist/core/test-generator/coverage-analyzer.js +285 -0
  426. package/dist/core/test-generator/coverage-analyzer.js.map +1 -0
  427. package/dist/core/test-generator/framework-detector.d.ts +17 -0
  428. package/dist/core/test-generator/framework-detector.d.ts.map +1 -0
  429. package/dist/core/test-generator/framework-detector.js +65 -0
  430. package/dist/core/test-generator/framework-detector.js.map +1 -0
  431. package/dist/core/test-generator/index.d.ts +14 -0
  432. package/dist/core/test-generator/index.d.ts.map +1 -0
  433. package/dist/core/test-generator/index.js +11 -0
  434. package/dist/core/test-generator/index.js.map +1 -0
  435. package/dist/core/test-generator/renderers/catch2.d.ts +8 -0
  436. package/dist/core/test-generator/renderers/catch2.d.ts.map +1 -0
  437. package/dist/core/test-generator/renderers/catch2.js +47 -0
  438. package/dist/core/test-generator/renderers/catch2.js.map +1 -0
  439. package/dist/core/test-generator/renderers/gtest.d.ts +8 -0
  440. package/dist/core/test-generator/renderers/gtest.d.ts.map +1 -0
  441. package/dist/core/test-generator/renderers/gtest.js +45 -0
  442. package/dist/core/test-generator/renderers/gtest.js.map +1 -0
  443. package/dist/core/test-generator/renderers/index.d.ts +20 -0
  444. package/dist/core/test-generator/renderers/index.d.ts.map +1 -0
  445. package/dist/core/test-generator/renderers/index.js +35 -0
  446. package/dist/core/test-generator/renderers/index.js.map +1 -0
  447. package/dist/core/test-generator/renderers/playwright.d.ts +8 -0
  448. package/dist/core/test-generator/renderers/playwright.d.ts.map +1 -0
  449. package/dist/core/test-generator/renderers/playwright.js +44 -0
  450. package/dist/core/test-generator/renderers/playwright.js.map +1 -0
  451. package/dist/core/test-generator/renderers/pytest.d.ts +8 -0
  452. package/dist/core/test-generator/renderers/pytest.d.ts.map +1 -0
  453. package/dist/core/test-generator/renderers/pytest.js +44 -0
  454. package/dist/core/test-generator/renderers/pytest.js.map +1 -0
  455. package/dist/core/test-generator/renderers/shared.d.ts +21 -0
  456. package/dist/core/test-generator/renderers/shared.d.ts.map +1 -0
  457. package/dist/core/test-generator/renderers/shared.js +56 -0
  458. package/dist/core/test-generator/renderers/shared.js.map +1 -0
  459. package/dist/core/test-generator/renderers/vitest.d.ts +8 -0
  460. package/dist/core/test-generator/renderers/vitest.d.ts.map +1 -0
  461. package/dist/core/test-generator/renderers/vitest.js +52 -0
  462. package/dist/core/test-generator/renderers/vitest.js.map +1 -0
  463. package/dist/core/test-generator/scenario-parser.d.ts +33 -0
  464. package/dist/core/test-generator/scenario-parser.d.ts.map +1 -0
  465. package/dist/core/test-generator/scenario-parser.js +244 -0
  466. package/dist/core/test-generator/scenario-parser.js.map +1 -0
  467. package/dist/core/test-generator/test-generator.d.ts +30 -0
  468. package/dist/core/test-generator/test-generator.d.ts.map +1 -0
  469. package/dist/core/test-generator/test-generator.js +174 -0
  470. package/dist/core/test-generator/test-generator.js.map +1 -0
  471. package/dist/core/test-generator/test-writer.d.ts +25 -0
  472. package/dist/core/test-generator/test-writer.d.ts.map +1 -0
  473. package/dist/core/test-generator/test-writer.js +128 -0
  474. package/dist/core/test-generator/test-writer.js.map +1 -0
  475. package/dist/core/test-generator/then-matchers.d.ts +35 -0
  476. package/dist/core/test-generator/then-matchers.d.ts.map +1 -0
  477. package/dist/core/test-generator/then-matchers.js +211 -0
  478. package/dist/core/test-generator/then-matchers.js.map +1 -0
  479. package/dist/core/verifier/index.d.ts +5 -0
  480. package/dist/core/verifier/index.d.ts.map +1 -0
  481. package/dist/core/verifier/index.js +5 -0
  482. package/dist/core/verifier/index.js.map +1 -0
  483. package/dist/core/verifier/verification-engine.d.ts +293 -0
  484. package/dist/core/verifier/verification-engine.d.ts.map +1 -0
  485. package/dist/core/verifier/verification-engine.js +919 -0
  486. package/dist/core/verifier/verification-engine.js.map +1 -0
  487. package/dist/types/index.d.ts +368 -0
  488. package/dist/types/index.d.ts.map +1 -0
  489. package/dist/types/index.js +5 -0
  490. package/dist/types/index.js.map +1 -0
  491. package/dist/types/pipeline.d.ts +167 -0
  492. package/dist/types/pipeline.d.ts.map +1 -0
  493. package/dist/types/pipeline.js +5 -0
  494. package/dist/types/pipeline.js.map +1 -0
  495. package/dist/types/test-generator.d.ts +103 -0
  496. package/dist/types/test-generator.d.ts.map +1 -0
  497. package/dist/types/test-generator.js +17 -0
  498. package/dist/types/test-generator.js.map +1 -0
  499. package/dist/utils/command-helpers.d.ts +68 -0
  500. package/dist/utils/command-helpers.d.ts.map +1 -0
  501. package/dist/utils/command-helpers.js +150 -0
  502. package/dist/utils/command-helpers.js.map +1 -0
  503. package/dist/utils/errors.d.ts +51 -0
  504. package/dist/utils/errors.d.ts.map +1 -0
  505. package/dist/utils/errors.js +129 -0
  506. package/dist/utils/errors.js.map +1 -0
  507. package/dist/utils/logger.d.ts +149 -0
  508. package/dist/utils/logger.d.ts.map +1 -0
  509. package/dist/utils/logger.js +342 -0
  510. package/dist/utils/logger.js.map +1 -0
  511. package/dist/utils/misc.d.ts +10 -0
  512. package/dist/utils/misc.d.ts.map +1 -0
  513. package/dist/utils/misc.js +21 -0
  514. package/dist/utils/misc.js.map +1 -0
  515. package/dist/utils/progress.d.ts +142 -0
  516. package/dist/utils/progress.d.ts.map +1 -0
  517. package/dist/utils/progress.js +283 -0
  518. package/dist/utils/progress.js.map +1 -0
  519. package/dist/utils/prompts.d.ts +53 -0
  520. package/dist/utils/prompts.d.ts.map +1 -0
  521. package/dist/utils/prompts.js +199 -0
  522. package/dist/utils/prompts.js.map +1 -0
  523. package/dist/utils/shutdown.d.ts +89 -0
  524. package/dist/utils/shutdown.d.ts.map +1 -0
  525. package/dist/utils/shutdown.js +238 -0
  526. package/dist/utils/shutdown.js.map +1 -0
  527. package/examples/bmad/README.md +113 -0
  528. package/examples/bmad/agents/architect.md +226 -0
  529. package/examples/bmad/agents/dev-brownfield.md +69 -0
  530. package/examples/bmad/setup/architect.customize.yaml +14 -0
  531. package/examples/bmad/tasks/implement-story.md +254 -0
  532. package/examples/bmad/tasks/onboarding.md +169 -0
  533. package/examples/bmad/tasks/refactor.md +178 -0
  534. package/examples/bmad/tasks/sprint-planning.md +168 -0
  535. package/examples/bmad/templates/story.md +108 -0
  536. package/examples/cline-workflows/openlore-analyze-codebase.md +101 -0
  537. package/examples/cline-workflows/openlore-check-spec-drift.md +102 -0
  538. package/examples/cline-workflows/openlore-execute-refactor.md +212 -0
  539. package/examples/cline-workflows/openlore-implement-feature.md +266 -0
  540. package/examples/cline-workflows/openlore-plan-refactor.md +279 -0
  541. package/examples/cline-workflows/openlore-refactor-codebase.md +16 -0
  542. package/examples/cline-workflows/openlore-write-tests.md +177 -0
  543. package/examples/drift-demo/openspec/config.yaml +14 -0
  544. package/examples/drift-demo/openspec/specs/architecture/spec.md +30 -0
  545. package/examples/drift-demo/openspec/specs/auth/spec.md +71 -0
  546. package/examples/drift-demo/openspec/specs/database/spec.md +33 -0
  547. package/examples/drift-demo/openspec/specs/overview/spec.md +20 -0
  548. package/examples/drift-demo/openspec/specs/projects/spec.md +55 -0
  549. package/examples/drift-demo/openspec/specs/tasks/spec.md +78 -0
  550. package/examples/drift-demo/package.json +21 -0
  551. package/examples/drift-demo/src/auth/auth-middleware.ts +30 -0
  552. package/examples/drift-demo/src/auth/auth-routes.ts +29 -0
  553. package/examples/drift-demo/src/auth/auth-service.ts +45 -0
  554. package/examples/drift-demo/src/database/connection.ts +27 -0
  555. package/examples/drift-demo/src/index.ts +16 -0
  556. package/examples/drift-demo/src/projects/project-model.ts +15 -0
  557. package/examples/drift-demo/src/projects/project-service.ts +34 -0
  558. package/examples/drift-demo/src/tasks/task-model.ts +37 -0
  559. package/examples/drift-demo/src/tasks/task-routes.ts +53 -0
  560. package/examples/drift-demo/src/tasks/task-service.ts +60 -0
  561. package/examples/drift-demo/src/utils/validation.ts +11 -0
  562. package/examples/drift-demo/tests/auth.test.ts +4 -0
  563. package/examples/drift-demo/tests/tasks.test.ts +4 -0
  564. package/examples/drift-demo/tsconfig.json +10 -0
  565. package/examples/drift-test/run-drift-test.sh +1087 -0
  566. package/examples/gsd/README.md +119 -0
  567. package/examples/gsd/commands/gsd/openlore-drift.md +111 -0
  568. package/examples/gsd/commands/gsd/openlore-orient.md +191 -0
  569. package/examples/mistral-vibe/README.md +101 -0
  570. package/examples/mistral-vibe/antipatterns-template.md +18 -0
  571. package/examples/mistral-vibe/skills/openlore-analyze-codebase/SKILL.md +124 -0
  572. package/examples/mistral-vibe/skills/openlore-brainstorm/SKILL.md +379 -0
  573. package/examples/mistral-vibe/skills/openlore-debug/SKILL.md +330 -0
  574. package/examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md +291 -0
  575. package/examples/mistral-vibe/skills/openlore-generate/SKILL.md +245 -0
  576. package/examples/mistral-vibe/skills/openlore-implement-story/SKILL.md +326 -0
  577. package/examples/mistral-vibe/skills/openlore-plan-refactor/SKILL.md +365 -0
  578. package/examples/mistral-vibe/skills/openlore-review-changes/SKILL.md +128 -0
  579. package/examples/mistral-vibe/skills/openlore-write-tests/SKILL.md +261 -0
  580. package/examples/opencode/agent-guard.ts +170 -0
  581. package/examples/opencode/plugins/anti-laziness.ts +202 -0
  582. package/examples/opencode/plugins/lib/openlore-context-injector-helpers.ts +116 -0
  583. package/examples/opencode/plugins/lib/openlore-decision-extractor-helpers.ts +65 -0
  584. package/examples/opencode/plugins/openlore-context-injector.test.ts +211 -0
  585. package/examples/opencode/plugins/openlore-context-injector.ts +165 -0
  586. package/examples/opencode/plugins/openlore-decision-extractor.test.ts +131 -0
  587. package/examples/opencode/plugins/openlore-decision-extractor.ts +322 -0
  588. package/examples/opencode/plugins/openlore-enforcer.ts +227 -0
  589. package/examples/opencode/prompts/sisyphus-sdd.md +150 -0
  590. package/examples/opencode-skills/openlore-analyze-codebase/SKILL.md +101 -0
  591. package/examples/opencode-skills/openlore-brainstorm/SKILL.md +354 -0
  592. package/examples/opencode-skills/openlore-debug/SKILL.md +291 -0
  593. package/examples/opencode-skills/openlore-execute-refactor/SKILL.md +241 -0
  594. package/examples/opencode-skills/openlore-generate/SKILL.md +236 -0
  595. package/examples/opencode-skills/openlore-implement-story/SKILL.md +251 -0
  596. package/examples/opencode-skills/openlore-plan-refactor/SKILL.md +298 -0
  597. package/examples/opencode-skills/openlore-review-changes/SKILL.md +134 -0
  598. package/examples/opencode-skills/openlore-write-tests/SKILL.md +230 -0
  599. package/examples/openspec-analysis/README.md +59 -0
  600. package/examples/openspec-analysis/SUMMARY.md +72 -0
  601. package/examples/openspec-analysis/config.json +16 -0
  602. package/examples/openspec-analysis/dependencies.mermaid +35 -0
  603. package/examples/openspec-analysis/dependency-graph.json +12116 -0
  604. package/examples/openspec-analysis/llm-context.json +119 -0
  605. package/examples/openspec-analysis/repo-structure.json +871 -0
  606. package/examples/openspec-cli/README.md +67 -0
  607. package/examples/openspec-cli/openspec/config.yaml +26 -0
  608. package/examples/openspec-cli/openspec/specs/architecture/spec.md +178 -0
  609. package/examples/openspec-cli/openspec/specs/artifact-graph/spec.md +143 -0
  610. package/examples/openspec-cli/openspec/specs/cli/spec.md +138 -0
  611. package/examples/openspec-cli/openspec/specs/overview/spec.md +60 -0
  612. package/examples/openspec-cli/openspec/specs/parsing/spec.md +123 -0
  613. package/examples/openspec-cli/openspec/specs/validation/spec.md +108 -0
  614. package/examples/spec-kit/README.md +104 -0
  615. package/examples/spec-kit/commands/drift.md +87 -0
  616. package/examples/spec-kit/commands/orient.md +138 -0
  617. package/examples/spec-kit/extension.yml +54 -0
  618. package/package.json +125 -0
  619. package/src/viewer/InteractiveGraphViewer.jsx +1600 -0
  620. package/src/viewer/app/index.html +17 -0
  621. package/src/viewer/app/main.jsx +13 -0
  622. package/src/viewer/components/ArchitectureView.jsx +177 -0
  623. package/src/viewer/components/ChatPanel.jsx +450 -0
  624. package/src/viewer/components/ClassGraph.jsx +782 -0
  625. package/src/viewer/components/ClusterGraph.jsx +469 -0
  626. package/src/viewer/components/FilterBar.jsx +179 -0
  627. package/src/viewer/components/FlatGraph.jsx +282 -0
  628. package/src/viewer/components/MicroComponents.jsx +85 -0
  629. package/src/viewer/hooks/usePanZoom.js +79 -0
  630. package/src/viewer/utils/constants.js +64 -0
  631. package/src/viewer/utils/graph-helpers.js +303 -0
  632. package/src/viewer/utils/graph-helpers.test.ts +39 -0
  633. package/src/viewer/utils/themes.js +206 -0
  634. package/stubs/tree-sitter-cli-stub/package.json +6 -0
@@ -0,0 +1,971 @@
1
+ /**
2
+ * HTTP Route Parser
3
+ *
4
+ * Extracts two complementary sets of data:
5
+ * 1. HTTP CALLS — fetch/axios/ky/got calls in JS/TS frontend files
6
+ * 2. ROUTE DEFS — FastAPI / Flask / Django route declarations in Python files
7
+ *
8
+ * These are then matched by `buildHttpEdges()` to create cross-language edges
9
+ * between the frontend files that call an endpoint and the Python handlers that
10
+ * serve it — filling the gap that static import analysis cannot reach.
11
+ *
12
+ * Matching strategy
13
+ * -----------------
14
+ * Routes are normalised to a canonical form before comparison:
15
+ * - Path parameters are replaced with a placeholder: /items/{id} → /items/:param
16
+ * - Leading slashes are normalised
17
+ * - Query strings are stripped from call-site URLs
18
+ * - Common API prefixes (/api, /api/v1, /v1, …) are tried both with and
19
+ * without the prefix so that a frontend call to /api/v1/search still matches
20
+ * a FastAPI router mounted at /search.
21
+ *
22
+ * Confidence levels
23
+ * -----------------
24
+ * exact — method + full path match
25
+ * path — path matches, method unknown on one side (e.g. bare fetch)
26
+ * fuzzy — normalised path matches after prefix stripping
27
+ */
28
+ import { readFile } from 'node:fs/promises';
29
+ import { extname } from 'node:path';
30
+ import { getSkeletonContent, detectLanguage } from './code-shaper.js';
31
+ // ============================================================================
32
+ // NORMALISATION HELPERS
33
+ // ============================================================================
34
+ /** Common API prefixes that frontends add but backends may not declare */
35
+ const API_PREFIXES = [
36
+ '/api/v1', '/api/v2', '/api/v3',
37
+ '/api',
38
+ '/v1', '/v2', '/v3',
39
+ ];
40
+ /**
41
+ * Reduce a URL/path to a comparable canonical form:
42
+ * - Strip protocol + host if present (https://example.com/foo → /foo)
43
+ * - Strip query string and fragment
44
+ * - Replace path parameters with :param
45
+ * {id}, :id, <int:id>, <id> → :param
46
+ * - Collapse duplicate slashes
47
+ * - Remove trailing slash (except root)
48
+ */
49
+ export function normalizeUrl(raw) {
50
+ // Remove template-literal variable parts: ${...}
51
+ let url = raw.replace(/\$\{[^}]+\}/g, ':param');
52
+ // Strip protocol + host
53
+ url = url.replace(/^https?:\/\/[^/]+/, '');
54
+ // Strip query string and fragment
55
+ url = url.replace(/[?#].*$/, '');
56
+ // Replace FastAPI / Flask style path params
57
+ url = url.replace(/\{[^}]+\}/g, ':param'); // {item_id}
58
+ url = url.replace(/<[^>]+>/g, ':param'); // <int:item_id>
59
+ url = url.replace(/:[\w]+/g, ':param'); // :item_id (Express style)
60
+ // Collapse duplicate slashes, ensure leading slash
61
+ url = ('/' + url).replace(/\/+/g, '/');
62
+ // Remove trailing slash unless it IS the root
63
+ if (url.length > 1 && url.endsWith('/'))
64
+ url = url.slice(0, -1);
65
+ return url.toLowerCase();
66
+ }
67
+ /**
68
+ * Return all candidate normalised paths to try for a frontend URL.
69
+ * We try both the full path and the path with each known prefix stripped,
70
+ * to handle cases where the backend router is mounted without the prefix.
71
+ */
72
+ function candidatePaths(normalizedUrl) {
73
+ const candidates = new Set([normalizedUrl]);
74
+ for (const prefix of API_PREFIXES) {
75
+ if (normalizedUrl.startsWith(prefix + '/') || normalizedUrl === prefix) {
76
+ candidates.add(normalizedUrl.slice(prefix.length) || '/');
77
+ }
78
+ }
79
+ return Array.from(candidates);
80
+ }
81
+ // ============================================================================
82
+ // HTTP CALL EXTRACTION (JS / TS)
83
+ // ============================================================================
84
+ /**
85
+ * Extract all HTTP calls from a JavaScript or TypeScript source file.
86
+ */
87
+ export async function extractHttpCalls(filePath) {
88
+ const ext = extname(filePath).toLowerCase();
89
+ if (!['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs'].includes(ext))
90
+ return [];
91
+ let content;
92
+ try {
93
+ content = await readFile(filePath, 'utf8');
94
+ }
95
+ catch {
96
+ return [];
97
+ }
98
+ const calls = [];
99
+ // Strip comments to avoid false matches.
100
+ // The line-comment regex must NOT match `://` inside URLs — we only strip
101
+ // `//` that is preceded by whitespace, punctuation, brackets, or the start
102
+ // of the line (i.e. genuine JS/TS comments, not protocol separators).
103
+ // The character class intentionally includes ) and ] so that patterns like
104
+ // `fetch('/api/items') // comment` are correctly stripped.
105
+ const clean = content
106
+ .replace(/\/\*[\s\S]*?\*\//g, '')
107
+ .replace(/(^|[\s,;()[\]{}])\/\/.*$/gm, '$1');
108
+ const lines = content.split('\n'); // keep original for line numbers
109
+ // ── fetch ──────────────────────────────────────────────────────────────────
110
+ // fetch('/api/search')
111
+ // fetch(`/api/search/${id}`, { method: 'POST' })
112
+ const fetchRegex = /\bfetch\s*\(\s*(`[^`]+`|'[^']+'|"[^"]+")\s*(?:,\s*\{([^}]*)\})?\s*\)/g;
113
+ let m;
114
+ while ((m = fetchRegex.exec(clean)) !== null) {
115
+ const rawUrl = m[1].replace(/^[`'"]/, '').replace(/[`'"]$/, '');
116
+ const optionsBlock = m[2] ?? '';
117
+ const methodMatch = optionsBlock.match(/method\s*:\s*['"`](\w+)['"`]/i);
118
+ const method = methodMatch ? methodMatch[1].toUpperCase() : 'GET';
119
+ calls.push({
120
+ file: filePath,
121
+ method,
122
+ url: rawUrl,
123
+ normalizedUrl: normalizeUrl(rawUrl),
124
+ line: getLine(lines, m.index),
125
+ client: 'fetch',
126
+ });
127
+ }
128
+ // ── axios (method shorthands + generic) ────────────────────────────────────
129
+ // axios.get('/api/items')
130
+ // axios.post('/api/items', data)
131
+ // axios({ method: 'post', url: '/api/items' })
132
+ // axios.request({ method: 'DELETE', url: '/api/items/1' })
133
+ const axiosMethodRegex = /\baxios\.(get|post|put|patch|delete|head|options)\s*\(\s*(`[^`]+`|'[^']+'|"[^"]+")/g;
134
+ while ((m = axiosMethodRegex.exec(clean)) !== null) {
135
+ const method = m[1].toUpperCase();
136
+ const rawUrl = m[2].replace(/^[`'"]/, '').replace(/[`'"]$/, '');
137
+ calls.push({
138
+ file: filePath,
139
+ method,
140
+ url: rawUrl,
141
+ normalizedUrl: normalizeUrl(rawUrl),
142
+ line: getLine(lines, m.index),
143
+ client: 'axios',
144
+ });
145
+ }
146
+ // axios({ url: '...', method: '...' }) or axios.request({ ... })
147
+ const axiosConfigRegex = /\baxios(?:\.request)?\s*\(\s*\{([^}]{0,400})\}/g;
148
+ while ((m = axiosConfigRegex.exec(clean)) !== null) {
149
+ const block = m[1];
150
+ const urlMatch = block.match(/url\s*:\s*(`[^`]+`|'[^']+'|"[^"]+")/);
151
+ if (!urlMatch)
152
+ continue;
153
+ const rawUrl = urlMatch[1].replace(/^[`'"]/, '').replace(/[`'"]$/, '');
154
+ const methodMatch = block.match(/method\s*:\s*['"`](\w+)['"`]/i);
155
+ const method = methodMatch ? methodMatch[1].toUpperCase() : 'UNKNOWN';
156
+ calls.push({
157
+ file: filePath,
158
+ method,
159
+ url: rawUrl,
160
+ normalizedUrl: normalizeUrl(rawUrl),
161
+ line: getLine(lines, m.index),
162
+ client: 'axios',
163
+ });
164
+ }
165
+ // ── ky ─────────────────────────────────────────────────────────────────────
166
+ // ky.get('/api/items') ky.post('/api/items', { json: data })
167
+ const kyRegex = /\bky\.(get|post|put|patch|delete|head)\s*\(\s*(`[^`]+`|'[^']+'|"[^"]+")/g;
168
+ while ((m = kyRegex.exec(clean)) !== null) {
169
+ const rawUrl = m[2].replace(/^[`'"]/, '').replace(/[`'"]$/, '');
170
+ calls.push({
171
+ file: filePath,
172
+ method: m[1].toUpperCase(),
173
+ url: rawUrl,
174
+ normalizedUrl: normalizeUrl(rawUrl),
175
+ line: getLine(lines, m.index),
176
+ client: 'ky',
177
+ });
178
+ }
179
+ // ── got ────────────────────────────────────────────────────────────────────
180
+ // got.get('/api/items')
181
+ const gotRegex = /\bgot\.(get|post|put|patch|delete|head)\s*\(\s*(`[^`]+`|'[^']+'|"[^"]+")/g;
182
+ while ((m = gotRegex.exec(clean)) !== null) {
183
+ const rawUrl = m[2].replace(/^[`'"]/, '').replace(/[`'"]$/, '');
184
+ calls.push({
185
+ file: filePath,
186
+ method: m[1].toUpperCase(),
187
+ url: rawUrl,
188
+ normalizedUrl: normalizeUrl(rawUrl),
189
+ line: getLine(lines, m.index),
190
+ client: 'got',
191
+ });
192
+ }
193
+ // ── React Query / SWR convenience wrappers ─────────────────────────────────
194
+ // useQuery(['key', id], () => fetch('/api/items')) — already caught above
195
+ // useMutation(() => axios.post('/api/items')) — already caught above
196
+ return calls;
197
+ }
198
+ // ============================================================================
199
+ // ROUTE DEFINITION EXTRACTION (Python)
200
+ // ============================================================================
201
+ /**
202
+ * Extract all route definitions from a Python source file.
203
+ * Supports FastAPI, Starlette, Flask, and Django (urls.py path/re_path).
204
+ */
205
+ export async function extractRouteDefinitions(filePath) {
206
+ const ext = extname(filePath).toLowerCase();
207
+ if (!['.py', '.pyw'].includes(ext))
208
+ return [];
209
+ let content;
210
+ try {
211
+ content = await readFile(filePath, 'utf8');
212
+ }
213
+ catch {
214
+ return [];
215
+ }
216
+ const routes = [];
217
+ const lines = content.split('\n');
218
+ // Remove comments for cleaner matching
219
+ const clean = content.replace(/#.*$/gm, '');
220
+ // ── FastAPI / Starlette decorators ─────────────────────────────────────────
221
+ // @app.get("/items/{item_id}")
222
+ // @router.post("/search", ...)
223
+ // @app.api_route("/multi", methods=["GET","POST"])
224
+ const fastapiDecoratorRegex = /@(?:app|router|api_router)\.(get|post|put|patch|delete|head|options|trace)\s*\(\s*(['"/][^'")\n]+['"])/gm;
225
+ let m;
226
+ while ((m = fastapiDecoratorRegex.exec(clean)) !== null) {
227
+ const method = m[1].toUpperCase();
228
+ const path = m[2].replace(/^['"]/, '').replace(/['"]$/, '');
229
+ const lineNum = getLine(lines, m.index);
230
+ // The handler name is on the `def` line right after the decorator block
231
+ const handlerName = extractNextDefName(lines, lineNum);
232
+ routes.push({
233
+ file: filePath,
234
+ method,
235
+ path,
236
+ normalizedPath: normalizeUrl(path),
237
+ handlerName,
238
+ framework: 'fastapi',
239
+ line: lineNum,
240
+ contractSource: 'none',
241
+ });
242
+ }
243
+ // @app.api_route("/path", methods=["GET", "POST"])
244
+ const apiRouteRegex = /@(?:app|router|api_router)\.api_route\s*\(\s*(['"/][^'")\n]+['"]),\s*methods\s*=\s*\[([^\]]+)\]/gm;
245
+ while ((m = apiRouteRegex.exec(clean)) !== null) {
246
+ const path = m[1].replace(/^['"]/, '').replace(/['"]$/, '');
247
+ const lineNum = getLine(lines, m.index);
248
+ const handlerName = extractNextDefName(lines, lineNum);
249
+ // Parse the methods list
250
+ const methodMatches = m[2].matchAll(/['"](\w+)['"]/g);
251
+ for (const mm of methodMatches) {
252
+ routes.push({
253
+ file: filePath,
254
+ method: mm[1].toUpperCase(),
255
+ path,
256
+ normalizedPath: normalizeUrl(path),
257
+ handlerName,
258
+ framework: 'fastapi',
259
+ line: lineNum,
260
+ contractSource: 'none',
261
+ });
262
+ }
263
+ }
264
+ // ── Flask ──────────────────────────────────────────────────────────────────
265
+ // @app.route("/items", methods=["GET", "POST"])
266
+ // @bp.route("/items/<int:item_id>", methods=["DELETE"])
267
+ const flaskRouteRegex = /@(?:\w+)\.route\s*\(\s*(['"/][^'")\n]+['"]),?\s*(?:methods\s*=\s*\[([^\]]*)\])?\s*\)/gm;
268
+ while ((m = flaskRouteRegex.exec(clean)) !== null) {
269
+ const path = m[1].replace(/^['"]/, '').replace(/['"]$/, '');
270
+ const lineNum = getLine(lines, m.index);
271
+ const handlerName = extractNextDefName(lines, lineNum);
272
+ const rawMethods = m[2];
273
+ if (rawMethods) {
274
+ const methodMatches = rawMethods.matchAll(/['"](\w+)['"]/g);
275
+ for (const mm of methodMatches) {
276
+ routes.push({
277
+ file: filePath,
278
+ method: mm[1].toUpperCase(),
279
+ path,
280
+ normalizedPath: normalizeUrl(path),
281
+ handlerName,
282
+ framework: 'flask',
283
+ line: lineNum,
284
+ contractSource: 'none',
285
+ });
286
+ }
287
+ }
288
+ else {
289
+ // Flask default is GET when no methods specified
290
+ routes.push({
291
+ file: filePath,
292
+ method: 'GET',
293
+ path,
294
+ normalizedPath: normalizeUrl(path),
295
+ handlerName,
296
+ framework: 'flask',
297
+ line: lineNum,
298
+ contractSource: 'none',
299
+ });
300
+ }
301
+ }
302
+ // ── Django urls.py ─────────────────────────────────────────────────────────
303
+ // path('api/items/', views.ItemListView.as_view(), name='item-list'),
304
+ // re_path(r'^api/items/(?P<pk>[0-9]+)/$', views.ItemDetailView.as_view()),
305
+ //
306
+ // NOTE: Django views handle HTTP method dispatch internally (via class-based
307
+ // views or decorators), so no method is declared in urls.py. All Django
308
+ // routes are stored with method='UNKNOWN', which means any frontend call
309
+ // matched against a Django route will receive confidence='path' at best —
310
+ // never 'exact'. This may produce false-positive edges when multiple HTTP
311
+ // methods share the same URL pattern. Filter by confidence if this matters.
312
+ const djangoPathRegex = /\bpath\s*\(\s*r?(['"])(.*?)\1\s*,\s*([\w.]+)/gm;
313
+ while ((m = djangoPathRegex.exec(clean)) !== null) {
314
+ const path = '/' + m[2].replace(/\$$/, '').replace(/^\^/, '');
315
+ const handlerName = m[3].split('.').pop() ?? m[3];
316
+ const lineNum = getLine(lines, m.index);
317
+ routes.push({
318
+ file: filePath,
319
+ method: 'UNKNOWN', // Django views handle method internally
320
+ path,
321
+ normalizedPath: normalizeUrl(path),
322
+ handlerName,
323
+ framework: 'django',
324
+ line: lineNum,
325
+ contractSource: 'none',
326
+ });
327
+ }
328
+ return routes;
329
+ }
330
+ // ============================================================================
331
+ // ROUTE DEFINITION EXTRACTION (Java — Spring MVC / JAX-RS)
332
+ // ============================================================================
333
+ const SPRING_METHOD_ANNOTATIONS = [
334
+ ['GetMapping', 'GET'],
335
+ ['PostMapping', 'POST'],
336
+ ['PutMapping', 'PUT'],
337
+ ['DeleteMapping', 'DELETE'],
338
+ ['PatchMapping', 'PATCH'],
339
+ ];
340
+ const JAXRS_METHOD_ANNOTATIONS = [
341
+ ['GET', 'GET'],
342
+ ['POST', 'POST'],
343
+ ['PUT', 'PUT'],
344
+ ['DELETE', 'DELETE'],
345
+ ['PATCH', 'PATCH'],
346
+ ['HEAD', 'HEAD'],
347
+ ['OPTIONS', 'OPTIONS'],
348
+ ];
349
+ /**
350
+ * Extract a path string from a Spring annotation argument blob.
351
+ * ("/foo") → /foo
352
+ * (value = "/foo") → /foo
353
+ * (path = "/foo") → /foo
354
+ * (value = {"/foo", "/bar"}) → /foo (first only)
355
+ * ("/foo", method=…) → /foo
356
+ */
357
+ function extractSpringPath(argsBlob) {
358
+ // Positional string: first quoted literal at start, possibly preceded by `{`
359
+ const positional = argsBlob.match(/^\s*\{?\s*"([^"]*)"/);
360
+ if (positional)
361
+ return positional[1];
362
+ const named = argsBlob.match(/(?:value|path)\s*=\s*\{?\s*"([^"]*)"/);
363
+ if (named)
364
+ return named[1];
365
+ return null;
366
+ }
367
+ /**
368
+ * Extract HTTP method from a Spring @RequestMapping argument blob.
369
+ * (method = RequestMethod.GET) → GET
370
+ * (method = {RequestMethod.GET, RequestMethod.POST}) → [GET, POST]
371
+ */
372
+ function extractSpringMethods(argsBlob) {
373
+ const match = argsBlob.match(/method\s*=\s*\{?([^}]+)\}?/);
374
+ if (!match)
375
+ return [];
376
+ const methods = [];
377
+ const methodRegex = /(?:RequestMethod\.)?([A-Z]+)/g;
378
+ let m;
379
+ while ((m = methodRegex.exec(match[1])) !== null) {
380
+ methods.push(m[1].toUpperCase());
381
+ }
382
+ return methods;
383
+ }
384
+ function combineSpringPaths(prefix, path) {
385
+ const normalizedPrefix = prefix.replace(/\/+$/, '');
386
+ const normalizedPath = path ? '/' + path.replace(/^\/+/, '') : '';
387
+ const combined = normalizedPrefix + normalizedPath;
388
+ return combined || '/';
389
+ }
390
+ /**
391
+ * Scan forward from an annotation line to find the handler method name.
392
+ * Java method signatures: `public ReturnType methodName(args) ...`
393
+ */
394
+ function extractNextJavaMethodName(lines, annotationLine) {
395
+ const start = annotationLine - 1;
396
+ const maxLook = Math.min(lines.length, start + 20);
397
+ const skipNames = new Set(['if', 'for', 'while', 'switch', 'return', 'class', 'interface', 'enum', 'record', 'new']);
398
+ for (let i = start; i < maxLook; i++) {
399
+ const l = lines[i] ?? '';
400
+ // Skip further annotation lines
401
+ if (l.trim().startsWith('@'))
402
+ continue;
403
+ // Match `[modifiers] ReturnType methodName(` — return type can include
404
+ // generics, arrays, and dotted names.
405
+ const match = l.match(/\b(?:public|private|protected)\s+(?:static\s+|final\s+|abstract\s+|synchronized\s+|default\s+|native\s+)*(?:<[^>]+>\s+)?[\w<>[\], ?.]+?\s+(\w+)\s*\(/);
406
+ if (match && !skipNames.has(match[1]))
407
+ return match[1];
408
+ }
409
+ return 'unknown';
410
+ }
411
+ /**
412
+ * Extract all HTTP route definitions from a Java source file.
413
+ * Supports Spring MVC (@RestController / @Controller + @RequestMapping and the
414
+ * shorthand @GetMapping / @PostMapping / …) and JAX-RS (@Path + @GET / @POST).
415
+ */
416
+ export async function extractJavaRouteDefinitions(filePath) {
417
+ const ext = extname(filePath).toLowerCase();
418
+ if (ext !== '.java')
419
+ return [];
420
+ let content;
421
+ try {
422
+ content = await readFile(filePath, 'utf8');
423
+ }
424
+ catch {
425
+ return [];
426
+ }
427
+ const routes = [];
428
+ const lines = content.split('\n');
429
+ // Strip comments but preserve offsets so line numbers stay accurate.
430
+ const clean = content
431
+ .replace(/\/\*[\s\S]*?\*\//g, m => ' '.repeat(m.length))
432
+ .replace(/\/\/.*$/gm, m => ' '.repeat(m.length));
433
+ // ── Detect framework and compute class-level path prefix ───────────────────
434
+ // Spring: class-level @RequestMapping(...) | JAX-RS: class-level @Path(...)
435
+ let springPrefix = '';
436
+ let jaxrsPrefix = '';
437
+ // Find the first class declaration and look at annotations preceding it.
438
+ const classMatch = clean.match(/\bclass\s+\w+/);
439
+ if (classMatch && classMatch.index !== undefined) {
440
+ const preamble = clean.slice(0, classMatch.index);
441
+ const springClassMapping = preamble.match(/@RequestMapping\s*\(([^)]*)\)(?![^@]*@RequestMapping)/);
442
+ if (springClassMapping) {
443
+ const p = extractSpringPath(springClassMapping[1]);
444
+ if (p)
445
+ springPrefix = '/' + p.replace(/^\//, '');
446
+ }
447
+ const jaxrsClassPath = preamble.match(/@Path\s*\(\s*"([^"]+)"\s*\)(?![^@]*@Path)/);
448
+ if (jaxrsClassPath) {
449
+ jaxrsPrefix = '/' + jaxrsClassPath[1].replace(/^\//, '');
450
+ }
451
+ }
452
+ const isSpring = /@(?:Rest)?Controller\b|@(?:Get|Post|Put|Delete|Patch)Mapping\b|@RequestMapping\b/.test(clean);
453
+ const isJaxrs = /@Path\b/.test(clean) && /@(?:GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\b/.test(clean);
454
+ // ── Spring: shorthand mappings (@GetMapping, @PostMapping, …) ──────────────
455
+ if (isSpring) {
456
+ for (const [annotation, method] of SPRING_METHOD_ANNOTATIONS) {
457
+ const re = new RegExp(`@${annotation}\\s*(?:\\(([^)]*)\\))?`, 'g');
458
+ let m;
459
+ while ((m = re.exec(clean)) !== null) {
460
+ const argsBlob = m[1] ?? '';
461
+ const path = extractSpringPath(argsBlob) ?? '';
462
+ const fullPath = combineSpringPaths(springPrefix, path);
463
+ const lineNum = getLine(lines, m.index);
464
+ const handlerName = extractNextJavaMethodName(lines, lineNum);
465
+ routes.push({
466
+ file: filePath,
467
+ method,
468
+ path: fullPath,
469
+ normalizedPath: normalizeUrl(fullPath),
470
+ handlerName,
471
+ framework: 'spring',
472
+ line: lineNum,
473
+ contractSource: 'none',
474
+ });
475
+ }
476
+ }
477
+ // @RequestMapping(method = RequestMethod.GET, value = "/foo") on a method.
478
+ // The class-level @RequestMapping is skipped because the class declaration
479
+ // immediately follows it — we detect that by checking whether the nearest
480
+ // forward token after the annotation is `class`.
481
+ const reqMappingRegex = /@RequestMapping\s*\(([^)]*)\)/g;
482
+ let m;
483
+ while ((m = reqMappingRegex.exec(clean)) !== null) {
484
+ const argsBlob = m[1];
485
+ const methods = extractSpringMethods(argsBlob);
486
+ if (methods.length === 0)
487
+ continue; // no method= → class-level or unhandled
488
+ // Ensure this annotation is on a method, not on the class. Peek forward
489
+ // past any subsequent annotations and check that we don't hit `class`
490
+ // before a method-like signature.
491
+ const afterIdx = m.index + m[0].length;
492
+ const ahead = clean.slice(afterIdx, afterIdx + 400);
493
+ const nextClass = ahead.search(/\bclass\s+\w+/);
494
+ const nextMethod = ahead.search(/\b(?:public|private|protected)\s+(?:static\s+|final\s+|abstract\s+|synchronized\s+|default\s+|native\s+)*(?:<[^>]+>\s+)?[\w<>[\], ?.]+?\s+\w+\s*\(/);
495
+ if (nextClass >= 0 && (nextMethod < 0 || nextClass < nextMethod))
496
+ continue;
497
+ const path = extractSpringPath(argsBlob) ?? '';
498
+ const fullPath = combineSpringPaths(springPrefix, path);
499
+ const lineNum = getLine(lines, m.index);
500
+ const handlerName = extractNextJavaMethodName(lines, lineNum);
501
+ for (const method of methods) {
502
+ routes.push({
503
+ file: filePath,
504
+ method,
505
+ path: fullPath,
506
+ normalizedPath: normalizeUrl(fullPath),
507
+ handlerName,
508
+ framework: 'spring',
509
+ line: lineNum,
510
+ contractSource: 'none',
511
+ });
512
+ }
513
+ }
514
+ }
515
+ // ── JAX-RS: @GET / @POST / @Path on methods ────────────────────────────────
516
+ if (isJaxrs) {
517
+ // Regex for a Java method signature; used to bound the annotation block
518
+ // of the current method so we don't pick up a @Path from a later method.
519
+ const methodSigRegex = /\b(?:public|private|protected)\s+[^{;]+?\s+\w+\s*\(/;
520
+ for (const [annotation, method] of JAXRS_METHOD_ANNOTATIONS) {
521
+ // Bare annotation with no argument list; path comes from class @Path
522
+ // prefix combined with any @Path on the same method.
523
+ const re = new RegExp(`@${annotation}\\b\\s*(?!\\()`, 'g');
524
+ let m;
525
+ while ((m = re.exec(clean)) !== null) {
526
+ // Only look for a method-level @Path *within this method's annotation
527
+ // block* — i.e. between the @GET and the next method signature.
528
+ const afterIdx = m.index + m[0].length;
529
+ const ahead = clean.slice(afterIdx, afterIdx + 400);
530
+ const sigMatch = methodSigRegex.exec(ahead);
531
+ const window = sigMatch ? ahead.slice(0, sigMatch.index) : ahead;
532
+ const methodPathMatch = window.match(/@Path\s*\(\s*"([^"]+)"\s*\)/);
533
+ const methodPath = methodPathMatch ? '/' + methodPathMatch[1].replace(/^\//, '') : '';
534
+ const fullPath = combineSpringPaths(jaxrsPrefix, methodPath);
535
+ const lineNum = getLine(lines, m.index);
536
+ const handlerName = extractNextJavaMethodName(lines, lineNum);
537
+ routes.push({
538
+ file: filePath,
539
+ method,
540
+ path: fullPath,
541
+ normalizedPath: normalizeUrl(fullPath),
542
+ handlerName,
543
+ framework: 'jaxrs',
544
+ line: lineNum,
545
+ contractSource: 'none',
546
+ });
547
+ }
548
+ }
549
+ }
550
+ return routes;
551
+ }
552
+ // ============================================================================
553
+ // EDGE BUILDER
554
+ // ============================================================================
555
+ /**
556
+ * Match HTTP calls from JS/TS files against route definitions from Python files
557
+ * and return cross-language edges.
558
+ *
559
+ * Pass in pre-extracted calls and routes (so callers can cache them across
560
+ * multiple graph builds without re-parsing).
561
+ */
562
+ export function buildHttpEdges(calls, routes) {
563
+ const edges = [];
564
+ // Index routes by normalised path for O(1) lookup
565
+ const routesByPath = new Map();
566
+ for (const route of routes) {
567
+ const existing = routesByPath.get(route.normalizedPath) ?? [];
568
+ existing.push(route);
569
+ routesByPath.set(route.normalizedPath, existing);
570
+ }
571
+ for (const call of calls) {
572
+ // Build all candidate paths (handles /api/v1 prefix stripping)
573
+ const candidates = candidatePaths(call.normalizedUrl);
574
+ let matched = false;
575
+ for (const candidate of candidates) {
576
+ const matchingRoutes = routesByPath.get(candidate);
577
+ if (!matchingRoutes)
578
+ continue;
579
+ for (const route of matchingRoutes) {
580
+ // Determine confidence
581
+ let confidence;
582
+ const methodsKnown = call.method !== 'UNKNOWN' && route.method !== 'UNKNOWN';
583
+ const methodsMatch = call.method === route.method;
584
+ if (methodsKnown && methodsMatch && candidate === call.normalizedUrl) {
585
+ confidence = 'exact';
586
+ }
587
+ else if (!methodsKnown || !methodsMatch) {
588
+ confidence = candidate !== call.normalizedUrl ? 'fuzzy' : 'path';
589
+ }
590
+ else {
591
+ confidence = candidate !== call.normalizedUrl ? 'fuzzy' : 'exact';
592
+ }
593
+ edges.push({
594
+ callerFile: call.file,
595
+ handlerFile: route.file,
596
+ method: methodsKnown ? call.method : route.method,
597
+ path: candidate,
598
+ call,
599
+ route,
600
+ confidence,
601
+ });
602
+ matched = true;
603
+ }
604
+ }
605
+ // If no match found via exact/prefix logic, try fuzzy segment comparison
606
+ if (!matched) {
607
+ const callSegments = call.normalizedUrl.replace(/:param/g, '*').split('/');
608
+ for (const [routePath, routeList] of routesByPath) {
609
+ const routeSegments = routePath.replace(/:param/g, '*').split('/');
610
+ if (callSegments.length !== routeSegments.length)
611
+ continue;
612
+ const allMatch = callSegments.every((seg, i) => seg === routeSegments[i] || seg === '*' || routeSegments[i] === '*');
613
+ if (!allMatch)
614
+ continue;
615
+ for (const route of routeList) {
616
+ edges.push({
617
+ callerFile: call.file,
618
+ handlerFile: route.file,
619
+ method: call.method !== 'UNKNOWN' ? call.method : route.method,
620
+ path: routePath,
621
+ call,
622
+ route,
623
+ confidence: 'fuzzy',
624
+ });
625
+ }
626
+ }
627
+ }
628
+ }
629
+ // Deduplicate: same caller file + handler file + method + path
630
+ const seen = new Set();
631
+ return edges.filter(e => {
632
+ const key = `${e.callerFile}|${e.handlerFile}|${e.method}|${e.path}`;
633
+ if (seen.has(key))
634
+ return false;
635
+ seen.add(key);
636
+ return true;
637
+ });
638
+ }
639
+ // ============================================================================
640
+ // BATCH HELPERS
641
+ // ============================================================================
642
+ /**
643
+ * Parse all files in a mixed JS+Python codebase and return HTTP edges.
644
+ * Intended to be called once per graph build and its result merged into
645
+ * the DependencyGraphResult edges.
646
+ */
647
+ export async function extractAllHttpEdges(filePaths) {
648
+ const allCalls = [];
649
+ const allRoutes = [];
650
+ await Promise.all(filePaths.map(async (fp) => {
651
+ const ext = extname(fp).toLowerCase();
652
+ if (['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs'].includes(ext)) {
653
+ const calls = await extractHttpCalls(fp);
654
+ allCalls.push(...calls);
655
+ }
656
+ else if (['.py', '.pyw'].includes(ext)) {
657
+ const routes = await extractRouteDefinitions(fp);
658
+ allRoutes.push(...routes);
659
+ }
660
+ else if (ext === '.java') {
661
+ const routes = await extractJavaRouteDefinitions(fp);
662
+ allRoutes.push(...routes);
663
+ }
664
+ }));
665
+ const edges = buildHttpEdges(allCalls, allRoutes);
666
+ return { calls: allCalls, routes: allRoutes, edges };
667
+ }
668
+ // ============================================================================
669
+ // PRIVATE UTILITIES
670
+ // ============================================================================
671
+ /** Convert a character offset in `content` to a 1-based line number */
672
+ function getLine(lines, charOffset) {
673
+ let accumulated = 0;
674
+ for (let i = 0; i < lines.length; i++) {
675
+ accumulated += lines[i].length + 1; // +1 for newline
676
+ if (accumulated > charOffset)
677
+ return i + 1;
678
+ }
679
+ return lines.length;
680
+ }
681
+ /**
682
+ * Given the line of a decorator, scan forward to find the next `def` name.
683
+ * Handles multi-line decorators with up to 10 lines of lookahead.
684
+ *
685
+ * `decoratorLine` is 1-based (from getLine()), so we convert to a 0-based
686
+ * index before indexing into the `lines` array.
687
+ */
688
+ function extractNextDefName(lines, decoratorLine) {
689
+ const start = decoratorLine - 1; // convert 1-based → 0-based
690
+ const maxLook = Math.min(lines.length, start + 10);
691
+ for (let i = start; i < maxLook; i++) {
692
+ const defMatch = lines[i]?.match(/^\s*(?:async\s+)?def\s+(\w+)/);
693
+ if (defMatch)
694
+ return defMatch[1];
695
+ }
696
+ return 'unknown';
697
+ }
698
+ // ============================================================================
699
+ // CONTRACT / TYPE EXTRACTION HELPERS
700
+ // ============================================================================
701
+ /**
702
+ * Extract contract information from a handler function body or surrounding context.
703
+ *
704
+ * Strategies:
705
+ * 1. TypeScript Request<P, ResBody, ReqBody, Q> generic → requestBodyType = ReqBody
706
+ * 2. NestJS @Body() dto: Type → requestBodyType = Type
707
+ * 3. Zod .parse( / .parseAsync( → contractSource = 'validator'
708
+ * 4. Promise<ResponseType> return annotation
709
+ */
710
+ function extractContractFromHandler(handlerSource) {
711
+ let requestBodyType;
712
+ let responseType;
713
+ let contractSource = 'none';
714
+ // 1. TypeScript Request<P, ResBody, ReqBody, Q> generic
715
+ // handler(req: Request<Params, ResBody, Body, Query>)
716
+ const reqGenericRe = /:\s*Request\s*<[^,>]+,\s*([^,>]+),\s*([^,>]+)/;
717
+ const reqGenericMatch = reqGenericRe.exec(handlerSource);
718
+ if (reqGenericMatch) {
719
+ const resBodyType = reqGenericMatch[1].trim();
720
+ const reqBodyType = reqGenericMatch[2].trim();
721
+ if (reqBodyType && reqBodyType !== 'unknown' && reqBodyType !== 'any') {
722
+ requestBodyType = reqBodyType;
723
+ contractSource = 'annotation';
724
+ }
725
+ if (resBodyType && resBodyType !== 'unknown' && resBodyType !== 'any') {
726
+ responseType = resBodyType;
727
+ }
728
+ }
729
+ // 2. NestJS @Body() dto: CreateUserDto
730
+ const bodyParamRe = /@Body\s*\(\s*\)\s+\w+\s*:\s*(\w+)/;
731
+ const bodyParamMatch = bodyParamRe.exec(handlerSource);
732
+ if (bodyParamMatch) {
733
+ requestBodyType = bodyParamMatch[1];
734
+ contractSource = 'annotation';
735
+ }
736
+ // 3. Zod validators: schema.parse( / schema.parseAsync( / z.infer<typeof
737
+ const zodRe = /\b\w+\.parse(?:Async)?\s*\(|z\.infer\s*<\s*typeof\s+(\w+)/;
738
+ const zodMatch = zodRe.exec(handlerSource);
739
+ if (zodMatch) {
740
+ contractSource = 'validator';
741
+ if (zodMatch[1]) {
742
+ requestBodyType = `z.infer<typeof ${zodMatch[1]}>`;
743
+ }
744
+ else {
745
+ // Extract schema variable name from .parse( call
746
+ const parseVarRe = /(\w+)\.parse(?:Async)?\s*\(/;
747
+ const parseVarMatch = parseVarRe.exec(handlerSource);
748
+ if (parseVarMatch) {
749
+ requestBodyType = parseVarMatch[1];
750
+ }
751
+ }
752
+ }
753
+ // 4. Promise<ResponseType> return type annotation
754
+ const returnTypeRe = /\):\s*Promise\s*<\s*([^>]+)>/;
755
+ const returnTypeMatch = returnTypeRe.exec(handlerSource);
756
+ if (returnTypeMatch && !responseType) {
757
+ const rType = returnTypeMatch[1].trim();
758
+ if (rType && rType !== 'void' && rType !== 'unknown' && rType !== 'any') {
759
+ responseType = rType;
760
+ }
761
+ }
762
+ return { requestBodyType, responseType, contractSource };
763
+ }
764
+ // ============================================================================
765
+ // TS/JS SERVER ROUTE EXTRACTION
766
+ // ============================================================================
767
+ // Express / Hono / Fastify / Koa / Elysia style:
768
+ // app.get('/path', handler)
769
+ // router.post('/path', ...)
770
+ // app.use('/prefix', router) ← prefix accumulation
771
+ const EXPRESS_ROUTE_RE = /(?:^|[\s;(,])(?:app|router|server|api|r)\.(get|post|put|delete|patch|head|options|all)\s*\(\s*['"`]([^'"`]+)['"`]/gm;
772
+ const EXPRESS_USE_RE = /(?:^|[\s;(,])(?:app|router|server|api|r)\.use\s*\(\s*['"`]([^'"`]+)['"`]/gm;
773
+ // NestJS decorator-based:
774
+ // @Controller('prefix') → class methods with @Get / @Post etc.
775
+ const NESTJS_CONTROLLER_RE = /@Controller\s*\(\s*['"`]([^'"`]*)['"`]\s*\)/g;
776
+ const NESTJS_METHOD_RE = /@(Get|Post|Put|Delete|Patch|Head|Options|All)\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g;
777
+ const NESTJS_HANDLER_RE = /(?:async\s+)?(\w+)\s*\(/;
778
+ // Next.js App Router: export (async) function GET(...) in app/**/route.ts
779
+ const NEXTJS_APP_ROUTER_RE = /^export\s+(?:async\s+)?function\s+(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s*\(/gm;
780
+ /** Detected framework from a file's content */
781
+ function detectTsFramework(source, filePath) {
782
+ if (/@Controller\s*\(/.test(source) && /@(Get|Post|Put|Delete|Patch)\s*\(/.test(source))
783
+ return 'nestjs';
784
+ if (/app\/.*\/route\.[jt]sx?$/.test(filePath.replace(/\\/g, '/')))
785
+ return 'nextjs-app';
786
+ if (/pages\/api\//.test(filePath.replace(/\\/g, '/')))
787
+ return 'nextjs-pages';
788
+ if (/from\s+['"]hono['"]/.test(source) || /new\s+Hono\s*[(<]/.test(source))
789
+ return 'hono';
790
+ if (/from\s+['"]fastify['"]/.test(source) || /fastify\s*\(/.test(source))
791
+ return 'fastify';
792
+ if (/from\s+['"]express['"]/.test(source) || /require\s*\(\s*['"]express['"]\s*\)/.test(source))
793
+ return 'express';
794
+ if (/from\s+['"]koa['"]/.test(source))
795
+ return 'koa';
796
+ if (/from\s+['"]elysia['"]/.test(source))
797
+ return 'elysia';
798
+ if (new RegExp(EXPRESS_ROUTE_RE.source).test(source))
799
+ return 'express';
800
+ return 'unknown';
801
+ }
802
+ /**
803
+ * Extract HTTP route definitions from a TypeScript/JavaScript server file.
804
+ * Handles Express-style, NestJS decorators, and Next.js App Router.
805
+ */
806
+ export async function extractTsRouteDefinitions(filePath) {
807
+ let source;
808
+ try {
809
+ const { readFile } = await import('node:fs/promises');
810
+ // Use skeleton to strip comments — prevents false positives from comment
811
+ // examples inside parser/extractor files that contain route pattern strings.
812
+ // Line numbers in the result are approximate (skeleton line positions).
813
+ source = getSkeletonContent(await readFile(filePath, 'utf-8'), detectLanguage(filePath));
814
+ }
815
+ catch {
816
+ return [];
817
+ }
818
+ const framework = detectTsFramework(source, filePath);
819
+ const routes = [];
820
+ const lines = source.split('\n');
821
+ function lineOf(index) {
822
+ return source.slice(0, index).split('\n').length;
823
+ }
824
+ // ── Next.js App Router ────────────────────────────────────────────────────
825
+ if (framework === 'nextjs-app') {
826
+ // Derive path from file location: app/users/route.ts → /users
827
+ const rel = filePath.replace(/\\/g, '/');
828
+ const appIdx = rel.lastIndexOf('/app/');
829
+ let routePath = '/';
830
+ if (appIdx >= 0) {
831
+ routePath = rel.slice(appIdx + 4).replace(/\/route\.[jt]sx?$/, '') || '/';
832
+ // Remove dynamic segments brackets for display: [id] → :id
833
+ routePath = routePath.replace(/\[([^\]]+)\]/g, ':$1');
834
+ }
835
+ const re = new RegExp(NEXTJS_APP_ROUTER_RE.source, NEXTJS_APP_ROUTER_RE.flags);
836
+ let m;
837
+ while ((m = re.exec(source)) !== null) {
838
+ // Extract handler body for contract detection (scan next 500 chars)
839
+ const handlerBody = source.slice(m.index, m.index + 500);
840
+ const contract = extractContractFromHandler(handlerBody);
841
+ routes.push({
842
+ file: filePath,
843
+ method: m[1].toUpperCase(),
844
+ path: routePath,
845
+ normalizedPath: normalizeUrl(routePath),
846
+ handlerName: m[1],
847
+ framework: 'nextjs-app',
848
+ line: lineOf(m.index),
849
+ ...contract,
850
+ });
851
+ }
852
+ return routes;
853
+ }
854
+ // ── NestJS ────────────────────────────────────────────────────────────────
855
+ if (framework === 'nestjs') {
856
+ // Collect controller prefixes
857
+ const ctrlRe = new RegExp(NESTJS_CONTROLLER_RE.source, NESTJS_CONTROLLER_RE.flags);
858
+ let ctrlPrefix = '';
859
+ const ctrlMatch = ctrlRe.exec(source);
860
+ if (ctrlMatch) {
861
+ ctrlPrefix = ctrlMatch[1] ? `/${ctrlMatch[1].replace(/^\//, '')}` : '';
862
+ }
863
+ const methodRe = new RegExp(NESTJS_METHOD_RE.source, NESTJS_METHOD_RE.flags);
864
+ let m;
865
+ while ((m = methodRe.exec(source)) !== null) {
866
+ const httpMethod = m[1].toUpperCase();
867
+ const subPath = m[2] ? `/${m[2].replace(/^\//, '')}` : '';
868
+ const fullPath = `${ctrlPrefix}${subPath}` || '/';
869
+ // Find handler function name on subsequent lines
870
+ const afterDecorator = source.slice(m.index + m[0].length);
871
+ const handlerMatch = NESTJS_HANDLER_RE.exec(afterDecorator.slice(0, 200));
872
+ const handlerName = handlerMatch?.[1] ?? 'unknown';
873
+ // Extract contract from decorator + handler context (scan next 400 chars)
874
+ const handlerContext = source.slice(m.index, m.index + 400);
875
+ const contract = extractContractFromHandler(handlerContext);
876
+ routes.push({
877
+ file: filePath,
878
+ method: httpMethod,
879
+ path: fullPath,
880
+ normalizedPath: normalizeUrl(fullPath),
881
+ handlerName,
882
+ framework: 'nestjs',
883
+ line: lineOf(m.index),
884
+ ...contract,
885
+ });
886
+ }
887
+ return routes;
888
+ }
889
+ // ── Express / Hono / Fastify / Koa / Elysia ───────────────────────────────
890
+ // Collect prefix map from .use() calls (best-effort)
891
+ const prefixes = [];
892
+ const useRe = new RegExp(EXPRESS_USE_RE.source, EXPRESS_USE_RE.flags);
893
+ let um;
894
+ while ((um = useRe.exec(source)) !== null) {
895
+ prefixes.push(um[1]);
896
+ }
897
+ const routeRe = new RegExp(EXPRESS_ROUTE_RE.source, EXPRESS_ROUTE_RE.flags);
898
+ let m;
899
+ while ((m = routeRe.exec(source)) !== null) {
900
+ const method = m[1].toUpperCase();
901
+ let path = m[2];
902
+ // Apply a prefix if the route is relative (no leading slash)
903
+ if (!path.startsWith('/') && prefixes.length > 0) {
904
+ path = `${prefixes[0]}/${path}`;
905
+ }
906
+ // Find the handler name from the same line
907
+ const lineText = lines[lineOf(m.index) - 1] ?? '';
908
+ const handlerMatch = lineText.match(/,\s*(?:async\s+)?(?:function\s+)?(\w+)\s*[,)]/);
909
+ const handlerName = handlerMatch?.[1] ?? 'handler';
910
+ // Extract contract from route context (scan next 600 chars)
911
+ const routeContext = source.slice(m.index, m.index + 600);
912
+ const contract = extractContractFromHandler(routeContext);
913
+ routes.push({
914
+ file: filePath,
915
+ method,
916
+ path,
917
+ normalizedPath: normalizeUrl(path),
918
+ handlerName,
919
+ framework,
920
+ line: lineOf(m.index),
921
+ ...contract,
922
+ });
923
+ }
924
+ return routes;
925
+ }
926
+ /**
927
+ * Build a complete route inventory from all source files.
928
+ * Combines Python routes (extractRouteDefinitions) and TS/JS routes
929
+ * (extractTsRouteDefinitions) into a single summary.
930
+ *
931
+ * @param filePaths - Absolute paths to all source files in the project
932
+ * @param rootDir - Project root for computing relative paths
933
+ */
934
+ export async function buildRouteInventory(filePaths, rootDir) {
935
+ const { relative } = await import('node:path');
936
+ const allRoutes = [];
937
+ await Promise.all(filePaths.map(async (fp) => {
938
+ const ext = extname(fp).toLowerCase();
939
+ if (['.py', '.pyw'].includes(ext)) {
940
+ allRoutes.push(...await extractRouteDefinitions(fp));
941
+ }
942
+ else if (['.ts', '.tsx', '.js', '.jsx', '.mjs'].includes(ext)) {
943
+ allRoutes.push(...await extractTsRouteDefinitions(fp));
944
+ }
945
+ else if (ext === '.java') {
946
+ allRoutes.push(...await extractJavaRouteDefinitions(fp));
947
+ }
948
+ }));
949
+ const byMethod = {};
950
+ const byFramework = {};
951
+ for (const r of allRoutes) {
952
+ byMethod[r.method] = (byMethod[r.method] ?? 0) + 1;
953
+ byFramework[r.framework] = (byFramework[r.framework] ?? 0) + 1;
954
+ }
955
+ return {
956
+ total: allRoutes.length,
957
+ byMethod,
958
+ byFramework,
959
+ routes: allRoutes.map(r => ({
960
+ method: r.method,
961
+ path: r.path,
962
+ framework: r.framework,
963
+ file: relative(rootDir, r.file),
964
+ handler: r.handlerName,
965
+ requestBodyType: r.requestBodyType,
966
+ responseType: r.responseType,
967
+ contractSource: r.contractSource,
968
+ })),
969
+ };
970
+ }
971
+ //# sourceMappingURL=http-route-parser.js.map