github-to-mcp-monorepo 1.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 (388) hide show
  1. package/.env.example +8 -0
  2. package/.github/CODEOWNERS +6 -0
  3. package/.husky/pre-commit +1 -0
  4. package/.nvmrc +1 -0
  5. package/.prettierignore +5 -0
  6. package/.prettierrc +7 -0
  7. package/.vscode/settings.json +4 -0
  8. package/ARCHITECTURE.md +1429 -0
  9. package/CHANGELOG.md +167 -0
  10. package/CONTRIBUTING.md +327 -0
  11. package/LICENSE +201 -0
  12. package/README.md +1028 -0
  13. package/SECURITY.md +248 -0
  14. package/VISUAL_GUIDE.md +437 -0
  15. package/apps/vscode/IMPLEMENTATION.md +480 -0
  16. package/apps/vscode/README.md +248 -0
  17. package/apps/vscode/package.json +381 -0
  18. package/apps/vscode/resources/icon.png +0 -0
  19. package/apps/vscode/resources/icon.svg +5 -0
  20. package/apps/vscode/src/commands/browseRegistry.ts +211 -0
  21. package/apps/vscode/src/commands/configureClaudeDesktop.ts +332 -0
  22. package/apps/vscode/src/commands/convert.ts +82 -0
  23. package/apps/vscode/src/commands/convertCurrentRepo.ts +109 -0
  24. package/apps/vscode/src/commands/convertFromUrl.ts +138 -0
  25. package/apps/vscode/src/commands/index.ts +121 -0
  26. package/apps/vscode/src/commands/validate.ts +197 -0
  27. package/apps/vscode/src/extension.ts +464 -0
  28. package/apps/vscode/src/global.d.ts +36 -0
  29. package/apps/vscode/src/test/extension.test.ts +73 -0
  30. package/apps/vscode/src/utils/file-generator.ts +529 -0
  31. package/apps/vscode/src/utils/github-api.ts +335 -0
  32. package/apps/vscode/src/utils/index.ts +29 -0
  33. package/apps/vscode/src/utils/mcp-config.ts +334 -0
  34. package/apps/vscode/src/utils/storage.ts +87 -0
  35. package/apps/vscode/src/views/McpServersTreeView.ts +160 -0
  36. package/apps/vscode/src/views/OutputChannelView.ts +195 -0
  37. package/apps/vscode/src/views/StatusBarItem.ts +251 -0
  38. package/apps/vscode/src/views/ToolsExplorerView.ts +314 -0
  39. package/apps/vscode/src/views/historyProvider.ts +75 -0
  40. package/apps/vscode/src/views/index.ts +12 -0
  41. package/apps/vscode/src/views/resultsPanel.ts +330 -0
  42. package/apps/vscode/src/webviews/ConversionPanel.ts +350 -0
  43. package/apps/vscode/src/webviews/ToolDetailsPanel.ts +448 -0
  44. package/apps/vscode/src/webviews/index.ts +9 -0
  45. package/apps/vscode/src/webviews/webview-ui/styles.ts +492 -0
  46. package/apps/vscode/tsconfig.json +20 -0
  47. package/apps/web/PLAYGROUND_GUIDE.md +499 -0
  48. package/apps/web/README.md +505 -0
  49. package/apps/web/app/api/convert/route.ts +100 -0
  50. package/apps/web/app/api/convert/stream/route.ts +198 -0
  51. package/apps/web/app/api/deploy/route.ts +157 -0
  52. package/apps/web/app/api/edge/route.ts +308 -0
  53. package/apps/web/app/api/export-docker/route.ts +284 -0
  54. package/apps/web/app/api/generate-openapi/route.ts +119 -0
  55. package/apps/web/app/api/mcp/[serverId]/route.ts +263 -0
  56. package/apps/web/app/api/playground/connect/route.ts +143 -0
  57. package/apps/web/app/api/playground/disconnect/route.ts +78 -0
  58. package/apps/web/app/api/playground/execute/route.ts +135 -0
  59. package/apps/web/app/api/playground/sessions/route.ts +103 -0
  60. package/apps/web/app/api/playground/tools/route.ts +117 -0
  61. package/apps/web/app/api/playground/v2/connect/route.ts +96 -0
  62. package/apps/web/app/api/playground/v2/disconnect/route.ts +88 -0
  63. package/apps/web/app/api/playground/v2/health/route.ts +80 -0
  64. package/apps/web/app/api/playground/v2/prompts/route.ts +160 -0
  65. package/apps/web/app/api/playground/v2/resources/route.ts +159 -0
  66. package/apps/web/app/api/playground/v2/sessions/route.ts +184 -0
  67. package/apps/web/app/api/playground/v2/tools/route.ts +167 -0
  68. package/apps/web/app/api/stream/route.ts +232 -0
  69. package/apps/web/app/batch/BatchConvertClient.tsx +190 -0
  70. package/apps/web/app/batch/page.tsx +37 -0
  71. package/apps/web/app/convert/page.tsx +269 -0
  72. package/apps/web/app/dashboard/page.tsx +380 -0
  73. package/apps/web/app/globals.css +622 -0
  74. package/apps/web/app/layout.tsx +120 -0
  75. package/apps/web/app/manifest.ts +31 -0
  76. package/apps/web/app/opengraph-image.tsx +112 -0
  77. package/apps/web/app/page.old.tsx +924 -0
  78. package/apps/web/app/page.tsx +77 -0
  79. package/apps/web/app/playground/page.tsx +306 -0
  80. package/apps/web/app/playground/v2/error.tsx +163 -0
  81. package/apps/web/app/playground/v2/layout.tsx +58 -0
  82. package/apps/web/app/playground/v2/loading.tsx +152 -0
  83. package/apps/web/app/playground/v2/page.tsx +644 -0
  84. package/apps/web/app/playground/v2/providers.tsx +214 -0
  85. package/apps/web/app/playground/v2/use-shortcuts.ts +209 -0
  86. package/apps/web/app/playground/v2/use-url-state.ts +296 -0
  87. package/apps/web/app/providers.tsx +22 -0
  88. package/apps/web/app/sitemap.ts +32 -0
  89. package/apps/web/app/twitter-image.tsx +112 -0
  90. package/apps/web/components/BranchSelector.tsx +401 -0
  91. package/apps/web/components/ClaudeConfigExport.tsx +226 -0
  92. package/apps/web/components/Features.tsx +84 -0
  93. package/apps/web/components/Footer.tsx +119 -0
  94. package/apps/web/components/GenerationProgress.tsx +248 -0
  95. package/apps/web/components/GithubUrlInput.tsx +483 -0
  96. package/apps/web/components/Header.tsx +175 -0
  97. package/apps/web/components/Hero.tsx +117 -0
  98. package/apps/web/components/HowItWorks.tsx +119 -0
  99. package/apps/web/components/InstallBanner.tsx +158 -0
  100. package/apps/web/components/Logo.tsx +116 -0
  101. package/apps/web/components/ParticleBackground.tsx +105 -0
  102. package/apps/web/components/Playground.tsx +472 -0
  103. package/apps/web/components/PlaygroundToolTester.tsx +410 -0
  104. package/apps/web/components/ProductCards.tsx +179 -0
  105. package/apps/web/components/SplitView.tsx +194 -0
  106. package/apps/web/components/ToolFilter.tsx +260 -0
  107. package/apps/web/components/ToolList.tsx +325 -0
  108. package/apps/web/components/batch/BatchConvert.tsx +785 -0
  109. package/apps/web/components/batch/index.ts +7 -0
  110. package/apps/web/components/convert/ConfigTabs.tsx +230 -0
  111. package/apps/web/components/convert/ConversionResult.tsx +482 -0
  112. package/apps/web/components/convert/InlinePlayground.tsx +259 -0
  113. package/apps/web/components/convert/LoadingSteps.tsx +311 -0
  114. package/apps/web/components/convert/OneClickInstall.tsx +224 -0
  115. package/apps/web/components/convert/ToolCard.tsx +189 -0
  116. package/apps/web/components/convert/TryInPlayground.tsx +242 -0
  117. package/apps/web/components/convert/index.ts +12 -0
  118. package/apps/web/components/deploy/DeployButton.tsx +369 -0
  119. package/apps/web/components/deploy/index.ts +7 -0
  120. package/apps/web/components/docker/DockerExport.tsx +690 -0
  121. package/apps/web/components/docker/index.ts +7 -0
  122. package/apps/web/components/install/OneClickInstall.tsx +676 -0
  123. package/apps/web/components/install/index.ts +7 -0
  124. package/apps/web/components/playground/CapabilityTabs.tsx +150 -0
  125. package/apps/web/components/playground/ConnectionStatusV2.tsx +322 -0
  126. package/apps/web/components/playground/EmptyStates.tsx +305 -0
  127. package/apps/web/components/playground/ExecutionLog.tsx +260 -0
  128. package/apps/web/components/playground/ExecutionLogV2.tsx +378 -0
  129. package/apps/web/components/playground/JsonViewer.tsx +388 -0
  130. package/apps/web/components/playground/PlaygroundLayout.tsx +244 -0
  131. package/apps/web/components/playground/PromptsPanel.tsx +385 -0
  132. package/apps/web/components/playground/ResourcesPanel.tsx +378 -0
  133. package/apps/web/components/playground/SchemaForm.tsx +477 -0
  134. package/apps/web/components/playground/ServerStatus.tsx +151 -0
  135. package/apps/web/components/playground/ShareButton.tsx +239 -0
  136. package/apps/web/components/playground/ToolsPanel.tsx +309 -0
  137. package/apps/web/components/playground/TransportConfigurator.tsx +563 -0
  138. package/apps/web/components/playground/index.ts +74 -0
  139. package/apps/web/components/playground/types.ts +202 -0
  140. package/apps/web/components/streaming/StreamingProgress.tsx +441 -0
  141. package/apps/web/components/streaming/index.ts +7 -0
  142. package/apps/web/components/ui/badge.tsx +42 -0
  143. package/apps/web/components/ui/button.tsx +88 -0
  144. package/apps/web/components/ui/card.tsx +75 -0
  145. package/apps/web/components/ui/code-block.tsx +122 -0
  146. package/apps/web/components/ui/index.ts +12 -0
  147. package/apps/web/components/ui/input.tsx +55 -0
  148. package/apps/web/components/ui/tabs.tsx +61 -0
  149. package/apps/web/hooks/index.ts +85 -0
  150. package/apps/web/hooks/types.ts +1173 -0
  151. package/apps/web/hooks/use-conversion.ts +133 -0
  152. package/apps/web/hooks/use-execution-history.ts +376 -0
  153. package/apps/web/hooks/use-generation-progress.ts +147 -0
  154. package/apps/web/hooks/use-local-storage.ts +88 -0
  155. package/apps/web/hooks/use-mcp-client.ts +623 -0
  156. package/apps/web/hooks/use-mcp-connection.ts +500 -0
  157. package/apps/web/hooks/use-mcp-execution.ts +282 -0
  158. package/apps/web/hooks/use-mcp-prompts.ts +441 -0
  159. package/apps/web/hooks/use-mcp-resources.ts +430 -0
  160. package/apps/web/hooks/use-mcp-tools.ts +540 -0
  161. package/apps/web/hooks/use-playground-store.ts +299 -0
  162. package/apps/web/hooks/use-playground.ts +184 -0
  163. package/apps/web/hooks/use-streaming-conversion.ts +227 -0
  164. package/apps/web/hooks/useBatchConversion.ts +271 -0
  165. package/apps/web/hooks/useDockerConfig.ts +161 -0
  166. package/apps/web/hooks/usePlatformDetection.ts +80 -0
  167. package/apps/web/hooks/useStreaming.ts +199 -0
  168. package/apps/web/lib/api/errors.ts +386 -0
  169. package/apps/web/lib/api/index.ts +137 -0
  170. package/apps/web/lib/api/logger.ts +187 -0
  171. package/apps/web/lib/api/middleware.ts +364 -0
  172. package/apps/web/lib/api/openapi.ts +977 -0
  173. package/apps/web/lib/api/session-manager.ts +594 -0
  174. package/apps/web/lib/api/types.ts +433 -0
  175. package/apps/web/lib/api/validation.ts +523 -0
  176. package/apps/web/lib/constants.ts +114 -0
  177. package/apps/web/lib/mcp/client.ts +1137 -0
  178. package/apps/web/lib/mcp/events.ts +651 -0
  179. package/apps/web/lib/mcp/index.ts +347 -0
  180. package/apps/web/lib/mcp/logger.ts +428 -0
  181. package/apps/web/lib/mcp/metrics.ts +703 -0
  182. package/apps/web/lib/mcp/retry.ts +616 -0
  183. package/apps/web/lib/mcp/session-manager.ts +779 -0
  184. package/apps/web/lib/mcp/transports.ts +988 -0
  185. package/apps/web/lib/mcp/types.ts +594 -0
  186. package/apps/web/lib/mcp-client-enhanced.ts +871 -0
  187. package/apps/web/lib/mcp-client.ts +778 -0
  188. package/apps/web/lib/mcp-errors.ts +489 -0
  189. package/apps/web/lib/mcp-sandbox.ts +593 -0
  190. package/apps/web/lib/mcp-testing.ts +428 -0
  191. package/apps/web/lib/mcp-types.ts +448 -0
  192. package/apps/web/lib/playground-store.tsx +1147 -0
  193. package/apps/web/lib/utils.ts +439 -0
  194. package/apps/web/next-env.d.ts +5 -0
  195. package/apps/web/next.config.js +23 -0
  196. package/apps/web/package.json +55 -0
  197. package/apps/web/postcss.config.js +6 -0
  198. package/apps/web/public/.well-known/ai-plugin.json +17 -0
  199. package/apps/web/public/logo.svg +6 -0
  200. package/apps/web/public/robots.txt +22 -0
  201. package/apps/web/public/schema.json +27 -0
  202. package/apps/web/tailwind.config.js +26 -0
  203. package/apps/web/tailwind.config.ts +123 -0
  204. package/apps/web/tsconfig.json +20 -0
  205. package/apps/web/types/deploy.ts +139 -0
  206. package/apps/web/types/index.ts +247 -0
  207. package/apps/web/vercel.json +39 -0
  208. package/eslint.config.mjs +23 -0
  209. package/llms.txt +102 -0
  210. package/mkdocs/docs/api/core.md +318 -0
  211. package/mkdocs/docs/api/index.md +128 -0
  212. package/mkdocs/docs/api/mcp-server.md +301 -0
  213. package/mkdocs/docs/api/openapi-parser.md +254 -0
  214. package/mkdocs/docs/assets/logo.svg +7 -0
  215. package/mkdocs/docs/changelog.md +118 -0
  216. package/mkdocs/docs/cli/generate.md +148 -0
  217. package/mkdocs/docs/cli/index.md +52 -0
  218. package/mkdocs/docs/cli/inspect.md +164 -0
  219. package/mkdocs/docs/cli/serve.md +136 -0
  220. package/mkdocs/docs/concepts/classification.md +254 -0
  221. package/mkdocs/docs/concepts/how-it-works.md +299 -0
  222. package/mkdocs/docs/concepts/index.md +77 -0
  223. package/mkdocs/docs/concepts/mcp-protocol.md +362 -0
  224. package/mkdocs/docs/concepts/tool-types.md +382 -0
  225. package/mkdocs/docs/contributing/architecture.md +262 -0
  226. package/mkdocs/docs/contributing/development.md +245 -0
  227. package/mkdocs/docs/contributing/index.md +73 -0
  228. package/mkdocs/docs/contributing/testing.md +320 -0
  229. package/mkdocs/docs/getting-started/configuration.md +235 -0
  230. package/mkdocs/docs/getting-started/index.md +54 -0
  231. package/mkdocs/docs/getting-started/installation.md +145 -0
  232. package/mkdocs/docs/getting-started/quickstart.md +160 -0
  233. package/mkdocs/docs/guides/batch.md +375 -0
  234. package/mkdocs/docs/guides/claude-desktop.md +227 -0
  235. package/mkdocs/docs/guides/cursor.md +188 -0
  236. package/mkdocs/docs/guides/custom-tools.md +367 -0
  237. package/mkdocs/docs/guides/index.md +78 -0
  238. package/mkdocs/docs/guides/private-repos.md +221 -0
  239. package/mkdocs/docs/guides/vscode.md +247 -0
  240. package/mkdocs/docs/index.md +175 -0
  241. package/mkdocs/docs/reference/config.md +223 -0
  242. package/mkdocs/docs/reference/env.md +192 -0
  243. package/mkdocs/docs/reference/index.md +102 -0
  244. package/mkdocs/docs/reference/tools.md +309 -0
  245. package/mkdocs/docs/stylesheets/extra.css +231 -0
  246. package/mkdocs/mkdocs.yml +204 -0
  247. package/mkdocs/overrides/.gitkeep +1 -0
  248. package/mkdocs/overrides/main.html +7 -0
  249. package/mkdocs/python-deps.txt +7 -0
  250. package/mkdocs/vercel.json +11 -0
  251. package/package.json +63 -0
  252. package/packages/core/package.json +61 -0
  253. package/packages/core/src/__tests__/bitbucket-client.test.ts +366 -0
  254. package/packages/core/src/__tests__/cli.test.ts +235 -0
  255. package/packages/core/src/__tests__/code-extractor.test.ts +378 -0
  256. package/packages/core/src/__tests__/docker-generator.test.ts +255 -0
  257. package/packages/core/src/__tests__/github-client.test.ts +390 -0
  258. package/packages/core/src/__tests__/gitlab-client.test.ts +319 -0
  259. package/packages/core/src/__tests__/go-extractor.test.ts +351 -0
  260. package/packages/core/src/__tests__/graphql-extractor.test.ts +330 -0
  261. package/packages/core/src/__tests__/java-extractor.test.ts +497 -0
  262. package/packages/core/src/__tests__/plugins.test.ts +467 -0
  263. package/packages/core/src/__tests__/readme-extractor.test.ts +258 -0
  264. package/packages/core/src/__tests__/redis-cache.test.ts +307 -0
  265. package/packages/core/src/__tests__/rust-extractor.test.ts +252 -0
  266. package/packages/core/src/__tests__/streaming.test.ts +251 -0
  267. package/packages/core/src/additional-extractors.ts +333 -0
  268. package/packages/core/src/cache/cache-interface.ts +179 -0
  269. package/packages/core/src/cache/index.ts +210 -0
  270. package/packages/core/src/cache/redis-cache.ts +291 -0
  271. package/packages/core/src/cache/upstash-cache.ts +379 -0
  272. package/packages/core/src/cache.ts +251 -0
  273. package/packages/core/src/cli.ts +822 -0
  274. package/packages/core/src/code-extractor.ts +696 -0
  275. package/packages/core/src/docker-generator.ts +470 -0
  276. package/packages/core/src/edge-compatible.ts +491 -0
  277. package/packages/core/src/extractors/go-extractor.ts +791 -0
  278. package/packages/core/src/extractors/index.ts +9 -0
  279. package/packages/core/src/extractors/java-extractor.ts +937 -0
  280. package/packages/core/src/extractors/rust-extractor.ts +744 -0
  281. package/packages/core/src/github-client.ts +319 -0
  282. package/packages/core/src/go-generator.ts +356 -0
  283. package/packages/core/src/graphql-extractor.ts +358 -0
  284. package/packages/core/src/index.ts +797 -0
  285. package/packages/core/src/langchain-exporter.ts +617 -0
  286. package/packages/core/src/language-parsers.ts +1114 -0
  287. package/packages/core/src/mcp-introspector.ts +279 -0
  288. package/packages/core/src/monorepo-detector.ts +378 -0
  289. package/packages/core/src/plugins/index.ts +370 -0
  290. package/packages/core/src/plugins/registry.ts +404 -0
  291. package/packages/core/src/plugins/types.ts +215 -0
  292. package/packages/core/src/providers/base-provider.ts +246 -0
  293. package/packages/core/src/providers/bitbucket-client.ts +464 -0
  294. package/packages/core/src/providers/gitlab-client.ts +388 -0
  295. package/packages/core/src/providers/index.ts +176 -0
  296. package/packages/core/src/python-generator.ts +260 -0
  297. package/packages/core/src/queue/index.ts +100 -0
  298. package/packages/core/src/queue/memory-queue.ts +445 -0
  299. package/packages/core/src/queue/redis-queue.ts +578 -0
  300. package/packages/core/src/queue/types.ts +251 -0
  301. package/packages/core/src/readme-extractor.ts +409 -0
  302. package/packages/core/src/schema-generator.ts +638 -0
  303. package/packages/core/src/streaming.ts +999 -0
  304. package/packages/core/src/types.ts +289 -0
  305. package/packages/core/tsconfig.json +9 -0
  306. package/packages/core/tsup.config.ts +25 -0
  307. package/packages/mcp-server/README.md +297 -0
  308. package/packages/mcp-server/package.json +55 -0
  309. package/packages/mcp-server/src/__tests__/mcp-server.test.ts +177 -0
  310. package/packages/mcp-server/src/__tests__/tools.test.ts +217 -0
  311. package/packages/mcp-server/src/index.ts +1206 -0
  312. package/packages/mcp-server/src/prompts/index.ts +601 -0
  313. package/packages/mcp-server/src/tools/export-docker.ts +362 -0
  314. package/packages/mcp-server/src/tools/generate-openapi.ts +162 -0
  315. package/packages/mcp-server/src/tools/monitor-mcp-server.ts +448 -0
  316. package/packages/mcp-server/src/tools/stream-convert.ts +398 -0
  317. package/packages/mcp-server/src/tools/test-mcp-tool.ts +531 -0
  318. package/packages/mcp-server/tsconfig.json +12 -0
  319. package/packages/mcp-server/tsup.config.ts +14 -0
  320. package/packages/openapi-parser/package-lock.json +3028 -0
  321. package/packages/openapi-parser/package.json +41 -0
  322. package/packages/openapi-parser/src/analyzer.ts +700 -0
  323. package/packages/openapi-parser/src/asyncapi-parser.ts +475 -0
  324. package/packages/openapi-parser/src/cli.ts +302 -0
  325. package/packages/openapi-parser/src/generator.ts +570 -0
  326. package/packages/openapi-parser/src/generators/express-analyzer.ts +649 -0
  327. package/packages/openapi-parser/src/generators/fastapi-analyzer.ts +960 -0
  328. package/packages/openapi-parser/src/generators/index.ts +200 -0
  329. package/packages/openapi-parser/src/generators/nextjs-analyzer.ts +768 -0
  330. package/packages/openapi-parser/src/generators/openapi-builder.ts +527 -0
  331. package/packages/openapi-parser/src/generators/types.ts +298 -0
  332. package/packages/openapi-parser/src/graphql-parser.ts +462 -0
  333. package/packages/openapi-parser/src/grpc-parser.ts +649 -0
  334. package/packages/openapi-parser/src/har-parser.ts +723 -0
  335. package/packages/openapi-parser/src/index.ts +635 -0
  336. package/packages/openapi-parser/src/insomnia-parser.ts +614 -0
  337. package/packages/openapi-parser/src/parser.ts +231 -0
  338. package/packages/openapi-parser/src/postman-parser.ts +611 -0
  339. package/packages/openapi-parser/src/ref-resolver.ts +313 -0
  340. package/packages/openapi-parser/src/transformer.ts +459 -0
  341. package/packages/openapi-parser/tests/generators/express.test.ts +209 -0
  342. package/packages/openapi-parser/tests/generators/fastapi.test.ts +236 -0
  343. package/packages/openapi-parser/tests/generators/nextjs.test.ts +273 -0
  344. package/packages/openapi-parser/tests/parsers.test.ts +847 -0
  345. package/packages/openapi-parser/tsconfig.json +9 -0
  346. package/packages/openapi-parser/tsup.config.ts +11 -0
  347. package/packages/registry/package.json +59 -0
  348. package/packages/registry/src/cli.ts +456 -0
  349. package/packages/registry/src/index.ts +44 -0
  350. package/packages/registry/src/popular/github.json +47 -0
  351. package/packages/registry/src/popular/index.ts +55 -0
  352. package/packages/registry/src/popular/linear.json +42 -0
  353. package/packages/registry/src/popular/notion.json +42 -0
  354. package/packages/registry/src/popular/openai.json +40 -0
  355. package/packages/registry/src/popular/resend.json +38 -0
  356. package/packages/registry/src/popular/slack.json +42 -0
  357. package/packages/registry/src/popular/stripe.json +163 -0
  358. package/packages/registry/src/popular/supabase.json +42 -0
  359. package/packages/registry/src/popular/twilio.json +40 -0
  360. package/packages/registry/src/popular/vercel.json +40 -0
  361. package/packages/registry/src/registry.ts +492 -0
  362. package/packages/registry/src/storage.ts +334 -0
  363. package/packages/registry/src/types.ts +275 -0
  364. package/packages/registry/src/updater.ts +208 -0
  365. package/packages/registry/tsconfig.json +10 -0
  366. package/packages/registry/tsup.config.ts +11 -0
  367. package/pnpm-workspace.yaml +3 -0
  368. package/scripts/build-docs.sh +16 -0
  369. package/server.json +9 -0
  370. package/templates/Dockerfile.python.template +60 -0
  371. package/templates/Dockerfile.typescript.template +60 -0
  372. package/templates/docker-compose.template.yml +68 -0
  373. package/tests/fixtures/express-app/index.js +34 -0
  374. package/tests/fixtures/express-app/routes/posts.js +43 -0
  375. package/tests/fixtures/express-app/routes/users.js +58 -0
  376. package/tests/fixtures/fastapi-app/main.py +125 -0
  377. package/tests/fixtures/fastapi-app/routes/admin.py +42 -0
  378. package/tests/fixtures/graphql/simple-schema.graphql +65 -0
  379. package/tests/fixtures/mocks/github-api-responses.json +63 -0
  380. package/tests/fixtures/nextjs-app/app/api/posts/route.ts +55 -0
  381. package/tests/fixtures/nextjs-app/app/api/users/[id]/route.ts +63 -0
  382. package/tests/fixtures/nextjs-app/app/api/users/route.ts +44 -0
  383. package/tests/fixtures/nextjs-app/pages/api/health.ts +28 -0
  384. package/tests/fixtures/openapi/petstore.yaml +179 -0
  385. package/tests/integration/langchain-export.test.ts +405 -0
  386. package/tests/integration/openapi-conversion.test.ts +221 -0
  387. package/tsconfig.json +18 -0
  388. package/vitest.config.ts +32 -0
@@ -0,0 +1,768 @@
1
+ /**
2
+ * @fileoverview Next.js API route analyzer
3
+ * Parses Next.js API routes from app/api and pages/api directories
4
+ * @copyright Copyright (c) 2024-2026 nirholas
5
+ * @license MIT
6
+ */
7
+
8
+ import {
9
+ RouteAnalyzer,
10
+ FileContent,
11
+ AnalysisResult,
12
+ AnalyzedRoute,
13
+ RouteParameter,
14
+ RouteBody,
15
+ RouteResponse,
16
+ JsonSchemaDefinition,
17
+ HttpMethod,
18
+ } from './types.js';
19
+ import { OpenAPIV3_1 } from 'openapi-types';
20
+
21
+ /**
22
+ * Regular expressions for Next.js route detection
23
+ */
24
+ const ROUTE_PATTERNS = {
25
+ // App Router: export async function GET/POST/PUT/PATCH/DELETE
26
+ appRouterHandler: /export\s+(?:async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s*\(/gi,
27
+
28
+ // App Router: export const GET/POST = async (req) =>
29
+ appRouterArrowHandler: /export\s+const\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s*=\s*(?:async\s*)?\(/gi,
30
+
31
+ // Pages Router: export default function handler
32
+ pagesRouterDefault: /export\s+default\s+(?:async\s+)?function\s+(\w+)?\s*\(/gi,
33
+
34
+ // Pages Router: handler with switch on method
35
+ methodSwitch: /(?:req|request)\.method\s*===?\s*['"](\w+)['"]/gi,
36
+
37
+ // NextRequest/NextResponse imports
38
+ nextImports: /import\s+{[^}]*(?:NextRequest|NextResponse)[^}]*}\s+from\s+['"]next\/server['"]/gi,
39
+
40
+ // Route segment config
41
+ routeConfig: /export\s+const\s+(dynamic|runtime|revalidate|fetchCache|preferredRegion)\s*=/gi,
42
+
43
+ // Zod schema
44
+ zodSchema: /(?:const|let|var)\s+(\w+)Schema?\s*=\s*z\.object\s*\((\{[\s\S]*?\})\)/gi,
45
+
46
+ // Request body parsing
47
+ bodyParsing: /(?:await\s+)?(?:req|request)\.json\s*\(\)/gi,
48
+
49
+ // URL search params
50
+ searchParams: /(?:searchParams|url\.searchParams)\.get\s*\(\s*['"](\w+)['"]\)/gi,
51
+
52
+ // Dynamic route params
53
+ dynamicParams: /params\.(\w+)/gi,
54
+
55
+ // Response with status
56
+ responseStatus: /(?:NextResponse\.json|Response\.json)\s*\([^,]+,\s*\{\s*status:\s*(\d+)/gi,
57
+
58
+ // TypeScript interface
59
+ tsInterface: /interface\s+(\w+)\s*{([^}]+)}/gi,
60
+
61
+ // Type annotation
62
+ typeAnnotation: /:\s*(\w+)(?:<[^>]+>)?/g,
63
+
64
+ // JSDoc comment
65
+ jsdocComment: /\/\*\*[\s\S]*?\*\//g,
66
+ };
67
+
68
+ /**
69
+ * Next.js API route analyzer implementation
70
+ */
71
+ export class NextJSAnalyzer implements RouteAnalyzer {
72
+ name = 'nextjs';
73
+
74
+ private schemas: Record<string, JsonSchemaDefinition> = {};
75
+ private warnings: string[] = [];
76
+ private errors: string[] = [];
77
+ private routerType: 'app' | 'pages' | 'unknown' = 'unknown';
78
+
79
+ /**
80
+ * Check if files contain Next.js API routes
81
+ */
82
+ canAnalyze(files: FileContent[]): boolean {
83
+ return files.some(file => {
84
+ const content = file.content;
85
+ const path = file.path.toLowerCase();
86
+
87
+ return (
88
+ // App Router
89
+ (path.includes('/app/') && path.includes('/api/') && path.includes('route.')) ||
90
+ // Pages Router
91
+ (path.includes('/pages/api/')) ||
92
+ // Next.js imports
93
+ content.includes("from 'next/server'") ||
94
+ content.includes('from "next/server"') ||
95
+ content.includes('NextApiRequest') ||
96
+ content.includes('NextRequest')
97
+ );
98
+ });
99
+ }
100
+
101
+ /**
102
+ * Analyze Next.js files and extract routes
103
+ */
104
+ async analyze(files: FileContent[]): Promise<AnalysisResult> {
105
+ this.schemas = {};
106
+ this.warnings = [];
107
+ this.errors = [];
108
+
109
+ const routes: AnalyzedRoute[] = [];
110
+ const filesAnalyzed: string[] = [];
111
+
112
+ // Detect router type
113
+ this.detectRouterType(files);
114
+
115
+ // First pass: extract schemas
116
+ for (const file of files) {
117
+ this.extractSchemas(file);
118
+ }
119
+
120
+ // Second pass: extract routes
121
+ for (const file of files) {
122
+ if (this.isApiRouteFile(file)) {
123
+ filesAnalyzed.push(file.path);
124
+ const fileRoutes = this.extractRoutes(file);
125
+ routes.push(...fileRoutes);
126
+ }
127
+ }
128
+
129
+ return {
130
+ routes,
131
+ schemas: this.schemas,
132
+ securitySchemes: this.detectSecuritySchemes(files),
133
+ warnings: this.warnings,
134
+ errors: this.errors,
135
+ framework: 'nextjs',
136
+ filesAnalyzed,
137
+ };
138
+ }
139
+
140
+ /**
141
+ * Detect whether it's App Router or Pages Router
142
+ */
143
+ private detectRouterType(files: FileContent[]): void {
144
+ for (const file of files) {
145
+ const path = file.path.toLowerCase();
146
+
147
+ if (path.includes('/app/') && path.includes('route.')) {
148
+ this.routerType = 'app';
149
+ return;
150
+ }
151
+ if (path.includes('/pages/api/')) {
152
+ this.routerType = 'pages';
153
+ return;
154
+ }
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Check if a file is an API route file
160
+ */
161
+ private isApiRouteFile(file: FileContent): boolean {
162
+ const path = file.path.toLowerCase();
163
+
164
+ // App Router: route.ts/js in app/api
165
+ if (path.includes('/api/') && /route\.(ts|js|tsx|jsx)$/.test(path)) {
166
+ return true;
167
+ }
168
+
169
+ // Pages Router: any file in pages/api
170
+ if (path.includes('/pages/api/') && /\.(ts|js|tsx|jsx)$/.test(path)) {
171
+ return true;
172
+ }
173
+
174
+ return false;
175
+ }
176
+
177
+ /**
178
+ * Extract routes from a file
179
+ */
180
+ private extractRoutes(file: FileContent): AnalyzedRoute[] {
181
+ const path = file.path.toLowerCase();
182
+
183
+ if (path.includes('/app/') && path.includes('route.')) {
184
+ return this.extractAppRouterRoutes(file);
185
+ }
186
+
187
+ return this.extractPagesRouterRoutes(file);
188
+ }
189
+
190
+ /**
191
+ * Extract routes from App Router file
192
+ */
193
+ private extractAppRouterRoutes(file: FileContent): AnalyzedRoute[] {
194
+ const routes: AnalyzedRoute[] = [];
195
+ const content = file.content;
196
+ const apiPath = this.getApiPathFromFilePath(file.path, 'app');
197
+
198
+ // Find all exported HTTP method handlers
199
+ ROUTE_PATTERNS.appRouterHandler.lastIndex = 0;
200
+ ROUTE_PATTERNS.appRouterArrowHandler.lastIndex = 0;
201
+
202
+ let match;
203
+
204
+ // Match function declarations
205
+ while ((match = ROUTE_PATTERNS.appRouterHandler.exec(content)) !== null) {
206
+ const method = match[1].toLowerCase() as HttpMethod;
207
+ const line = this.getLineNumber(content, match.index);
208
+ const jsDoc = this.findPrecedingJsDoc(content, match.index);
209
+
210
+ routes.push(this.createRoute(file, method, apiPath, line, jsDoc, content));
211
+ }
212
+
213
+ // Match arrow function exports
214
+ while ((match = ROUTE_PATTERNS.appRouterArrowHandler.exec(content)) !== null) {
215
+ const method = match[1].toLowerCase() as HttpMethod;
216
+ const line = this.getLineNumber(content, match.index);
217
+ const jsDoc = this.findPrecedingJsDoc(content, match.index);
218
+
219
+ routes.push(this.createRoute(file, method, apiPath, line, jsDoc, content));
220
+ }
221
+
222
+ return routes;
223
+ }
224
+
225
+ /**
226
+ * Extract routes from Pages Router file
227
+ */
228
+ private extractPagesRouterRoutes(file: FileContent): AnalyzedRoute[] {
229
+ const routes: AnalyzedRoute[] = [];
230
+ const content = file.content;
231
+ const apiPath = this.getApiPathFromFilePath(file.path, 'pages');
232
+
233
+ // Check for method switch pattern
234
+ ROUTE_PATTERNS.methodSwitch.lastIndex = 0;
235
+ const methods = new Set<HttpMethod>();
236
+
237
+ let match;
238
+ while ((match = ROUTE_PATTERNS.methodSwitch.exec(content)) !== null) {
239
+ methods.add(match[1].toLowerCase() as HttpMethod);
240
+ }
241
+
242
+ // If no specific methods found, assume it handles all common methods
243
+ if (methods.size === 0) {
244
+ // Check if there's a default export (handler)
245
+ if (/export\s+default/.test(content)) {
246
+ methods.add('get');
247
+ methods.add('post');
248
+ }
249
+ }
250
+
251
+ const jsDoc = this.findPrecedingJsDoc(content, 0);
252
+
253
+ for (const method of methods) {
254
+ routes.push(this.createRoute(file, method, apiPath, 1, jsDoc, content));
255
+ }
256
+
257
+ return routes;
258
+ }
259
+
260
+ /**
261
+ * Get API path from file path
262
+ */
263
+ private getApiPathFromFilePath(filePath: string, routerType: 'app' | 'pages'): string {
264
+ let path = filePath;
265
+
266
+ // Normalize path
267
+ path = path.replace(/\\/g, '/');
268
+
269
+ if (routerType === 'app') {
270
+ // Extract path from app/api/... /route.ts
271
+ const match = path.match(/\/app(\/api\/[^/]+(?:\/[^/]+)*?)\/route\.[^/]+$/i);
272
+ if (match) {
273
+ path = match[1];
274
+ }
275
+ } else {
276
+ // Extract path from pages/api/...
277
+ const match = path.match(/\/pages(\/api\/[^/]+(?:\/[^/]+)*?)\.[^/]+$/i);
278
+ if (match) {
279
+ path = match[1];
280
+ }
281
+ }
282
+
283
+ // Convert dynamic segments
284
+ // [param] -> {param}
285
+ // [[...slug]] -> {slug}
286
+ // [...slug] -> {slug}
287
+ path = path
288
+ .replace(/\[\[\.\.\.(\w+)\]\]/g, '{$1}')
289
+ .replace(/\[\.\.\.(\w+)\]/g, '{$1}')
290
+ .replace(/\[(\w+)\]/g, '{$1}');
291
+
292
+ return path;
293
+ }
294
+
295
+ /**
296
+ * Create an analyzed route
297
+ */
298
+ private createRoute(
299
+ file: FileContent,
300
+ method: HttpMethod,
301
+ path: string,
302
+ line: number,
303
+ jsDoc: string | undefined,
304
+ content: string
305
+ ): AnalyzedRoute {
306
+ const { summary, description, tags, deprecated } = this.parseJsDoc(jsDoc);
307
+ const pathParams = this.extractPathParameters(path);
308
+ const queryParams = this.extractQueryParameters(content);
309
+ const responses = this.extractResponses(content, method);
310
+
311
+ const route: AnalyzedRoute = {
312
+ method,
313
+ path,
314
+ openApiPath: path,
315
+ operationId: this.generateOperationId(method, path),
316
+ summary,
317
+ description,
318
+ tags: tags.length > 0 ? tags : this.inferTags(path),
319
+ pathParameters: pathParams,
320
+ queryParameters: queryParams,
321
+ headerParameters: this.extractHeaderParameters(content),
322
+ responses: responses.length > 0 ? responses : this.inferResponses(method),
323
+ deprecated,
324
+ sourceFile: file.path,
325
+ sourceLine: line,
326
+ };
327
+
328
+ // Add request body for mutation methods
329
+ if (['post', 'put', 'patch'].includes(method)) {
330
+ route.requestBody = this.extractRequestBody(content);
331
+ }
332
+
333
+ return route;
334
+ }
335
+
336
+ /**
337
+ * Extract path parameters from path
338
+ */
339
+ private extractPathParameters(path: string): RouteParameter[] {
340
+ const params: RouteParameter[] = [];
341
+ const matches = path.matchAll(/\{(\w+)\}/g);
342
+
343
+ for (const match of matches) {
344
+ params.push({
345
+ name: match[1],
346
+ location: 'path',
347
+ required: true,
348
+ type: 'string',
349
+ description: `Path parameter: ${match[1]}`,
350
+ });
351
+ }
352
+
353
+ return params;
354
+ }
355
+
356
+ /**
357
+ * Extract query parameters from code
358
+ */
359
+ private extractQueryParameters(content: string): RouteParameter[] {
360
+ const params: RouteParameter[] = [];
361
+ const paramNames = new Set<string>();
362
+
363
+ // Find searchParams.get() calls
364
+ ROUTE_PATTERNS.searchParams.lastIndex = 0;
365
+ let match;
366
+
367
+ while ((match = ROUTE_PATTERNS.searchParams.exec(content)) !== null) {
368
+ const name = match[1];
369
+ if (!paramNames.has(name)) {
370
+ paramNames.add(name);
371
+ params.push({
372
+ name,
373
+ location: 'query',
374
+ required: false,
375
+ type: 'string',
376
+ });
377
+ }
378
+ }
379
+
380
+ return params;
381
+ }
382
+
383
+ /**
384
+ * Extract header parameters from code
385
+ */
386
+ private extractHeaderParameters(content: string): RouteParameter[] {
387
+ const params: RouteParameter[] = [];
388
+
389
+ // Look for header access patterns
390
+ const headerPatterns = [
391
+ /headers\.get\s*\(\s*['"]([^'"]+)['"]\)/gi,
392
+ /request\.headers\.get\s*\(\s*['"]([^'"]+)['"]\)/gi,
393
+ ];
394
+
395
+ const headerNames = new Set<string>();
396
+
397
+ for (const pattern of headerPatterns) {
398
+ pattern.lastIndex = 0;
399
+ let match;
400
+ while ((match = pattern.exec(content)) !== null) {
401
+ const name = match[1].toLowerCase();
402
+ // Skip common headers that shouldn't be in the spec
403
+ if (!['content-type', 'content-length', 'host'].includes(name) && !headerNames.has(name)) {
404
+ headerNames.add(name);
405
+ params.push({
406
+ name: match[1],
407
+ location: 'header',
408
+ required: false,
409
+ type: 'string',
410
+ });
411
+ }
412
+ }
413
+ }
414
+
415
+ return params;
416
+ }
417
+
418
+ /**
419
+ * Extract request body schema from code
420
+ */
421
+ private extractRequestBody(content: string): RouteBody {
422
+ // Look for Zod schema usage
423
+ const zodMatch = content.match(/(\w+)Schema\.parse\s*\(/);
424
+ if (zodMatch && this.schemas[zodMatch[1]]) {
425
+ return {
426
+ contentType: 'application/json',
427
+ required: true,
428
+ schema: { $ref: `#/components/schemas/${zodMatch[1]}` },
429
+ description: 'Request body',
430
+ };
431
+ }
432
+
433
+ // Look for TypeScript type assertion
434
+ const typeMatch = content.match(/await\s+(?:req|request)\.json\s*\(\)\s*(?:as\s+(\w+))?/);
435
+ if (typeMatch && typeMatch[1] && this.schemas[typeMatch[1]]) {
436
+ return {
437
+ contentType: 'application/json',
438
+ required: true,
439
+ schema: { $ref: `#/components/schemas/${typeMatch[1]}` },
440
+ description: 'Request body',
441
+ };
442
+ }
443
+
444
+ // Default request body
445
+ return {
446
+ contentType: 'application/json',
447
+ required: true,
448
+ schema: { type: 'object' },
449
+ description: 'Request body',
450
+ };
451
+ }
452
+
453
+ /**
454
+ * Extract responses from code
455
+ */
456
+ private extractResponses(content: string, method: HttpMethod): RouteResponse[] {
457
+ const responses: RouteResponse[] = [];
458
+ const statusCodes = new Set<number>();
459
+
460
+ // Find NextResponse.json with status
461
+ ROUTE_PATTERNS.responseStatus.lastIndex = 0;
462
+ let match;
463
+
464
+ while ((match = ROUTE_PATTERNS.responseStatus.exec(content)) !== null) {
465
+ const status = parseInt(match[1], 10);
466
+ if (!statusCodes.has(status)) {
467
+ statusCodes.add(status);
468
+ responses.push({
469
+ statusCode: status,
470
+ description: this.getStatusDescription(status),
471
+ contentType: 'application/json',
472
+ schema: { type: 'object' },
473
+ });
474
+ }
475
+ }
476
+
477
+ // If no explicit status codes found, add defaults
478
+ if (responses.length === 0) {
479
+ return this.inferResponses(method);
480
+ }
481
+
482
+ return responses;
483
+ }
484
+
485
+ /**
486
+ * Infer responses based on method
487
+ */
488
+ private inferResponses(method: HttpMethod): RouteResponse[] {
489
+ const responses: RouteResponse[] = [];
490
+
491
+ switch (method) {
492
+ case 'get':
493
+ responses.push({
494
+ statusCode: 200,
495
+ description: 'Successful response',
496
+ contentType: 'application/json',
497
+ schema: { type: 'object' },
498
+ });
499
+ break;
500
+ case 'post':
501
+ responses.push({
502
+ statusCode: 201,
503
+ description: 'Resource created',
504
+ contentType: 'application/json',
505
+ schema: { type: 'object' },
506
+ });
507
+ break;
508
+ case 'put':
509
+ case 'patch':
510
+ responses.push({
511
+ statusCode: 200,
512
+ description: 'Resource updated',
513
+ contentType: 'application/json',
514
+ schema: { type: 'object' },
515
+ });
516
+ break;
517
+ case 'delete':
518
+ responses.push({
519
+ statusCode: 204,
520
+ description: 'Resource deleted',
521
+ });
522
+ break;
523
+ default:
524
+ responses.push({
525
+ statusCode: 200,
526
+ description: 'Successful response',
527
+ });
528
+ }
529
+
530
+ // Add common error responses
531
+ responses.push({
532
+ statusCode: 400,
533
+ description: 'Bad request',
534
+ contentType: 'application/json',
535
+ schema: {
536
+ type: 'object',
537
+ properties: {
538
+ error: { type: 'string' },
539
+ },
540
+ },
541
+ });
542
+ responses.push({
543
+ statusCode: 500,
544
+ description: 'Internal server error',
545
+ contentType: 'application/json',
546
+ schema: {
547
+ type: 'object',
548
+ properties: {
549
+ error: { type: 'string' },
550
+ },
551
+ },
552
+ });
553
+
554
+ return responses;
555
+ }
556
+
557
+ /**
558
+ * Get status description
559
+ */
560
+ private getStatusDescription(status: number): string {
561
+ const descriptions: Record<number, string> = {
562
+ 200: 'Successful response',
563
+ 201: 'Resource created',
564
+ 204: 'No content',
565
+ 400: 'Bad request',
566
+ 401: 'Unauthorized',
567
+ 403: 'Forbidden',
568
+ 404: 'Not found',
569
+ 405: 'Method not allowed',
570
+ 422: 'Unprocessable entity',
571
+ 429: 'Too many requests',
572
+ 500: 'Internal server error',
573
+ };
574
+ return descriptions[status] || `HTTP ${status}`;
575
+ }
576
+
577
+ /**
578
+ * Parse JSDoc comment
579
+ */
580
+ private parseJsDoc(jsDoc?: string): {
581
+ summary?: string;
582
+ description?: string;
583
+ tags: string[];
584
+ deprecated: boolean;
585
+ } {
586
+ const result = {
587
+ summary: undefined as string | undefined,
588
+ description: undefined as string | undefined,
589
+ tags: [] as string[],
590
+ deprecated: false,
591
+ };
592
+
593
+ if (!jsDoc) return result;
594
+
595
+ // Extract summary (first line)
596
+ const summaryMatch = jsDoc.match(/\/\*\*\s*\n?\s*\*\s*([^\n@]+)/);
597
+ if (summaryMatch) {
598
+ result.summary = summaryMatch[1].trim();
599
+ }
600
+
601
+ // Extract @tag
602
+ const tagMatches = jsDoc.matchAll(/@tag\s+(\w+)/gi);
603
+ for (const match of tagMatches) {
604
+ result.tags.push(match[1]);
605
+ }
606
+
607
+ // Check for @deprecated
608
+ if (jsDoc.includes('@deprecated')) {
609
+ result.deprecated = true;
610
+ }
611
+
612
+ return result;
613
+ }
614
+
615
+ /**
616
+ * Find preceding JSDoc comment
617
+ */
618
+ private findPrecedingJsDoc(content: string, position: number): string | undefined {
619
+ const before = content.slice(0, position);
620
+ const match = before.match(/\/\*\*[\s\S]*?\*\/\s*$/);
621
+ return match ? match[0] : undefined;
622
+ }
623
+
624
+ /**
625
+ * Extract schemas from file
626
+ */
627
+ private extractSchemas(file: FileContent): void {
628
+ const content = file.content;
629
+
630
+ // Extract TypeScript interfaces
631
+ ROUTE_PATTERNS.tsInterface.lastIndex = 0;
632
+ let match;
633
+
634
+ while ((match = ROUTE_PATTERNS.tsInterface.exec(content)) !== null) {
635
+ const name = match[1];
636
+ const body = match[2];
637
+ this.schemas[name] = this.parseInterfaceBody(body);
638
+ }
639
+
640
+ // Extract Zod schemas
641
+ ROUTE_PATTERNS.zodSchema.lastIndex = 0;
642
+ while ((match = ROUTE_PATTERNS.zodSchema.exec(content)) !== null) {
643
+ const name = match[1];
644
+ // Simple Zod parsing
645
+ this.schemas[name] = { type: 'object' };
646
+ }
647
+ }
648
+
649
+ /**
650
+ * Parse TypeScript interface body to JSON Schema
651
+ */
652
+ private parseInterfaceBody(body: string): JsonSchemaDefinition {
653
+ const properties: Record<string, JsonSchemaDefinition> = {};
654
+ const required: string[] = [];
655
+
656
+ const propMatches = body.matchAll(/(\w+)(\?)?:\s*([^;]+);?/g);
657
+ for (const match of propMatches) {
658
+ const propName = match[1];
659
+ const isOptional = !!match[2];
660
+ const propType = match[3].trim();
661
+
662
+ properties[propName] = this.tsTypeToJsonSchema(propType);
663
+ if (!isOptional) {
664
+ required.push(propName);
665
+ }
666
+ }
667
+
668
+ return {
669
+ type: 'object',
670
+ properties,
671
+ required: required.length > 0 ? required : undefined,
672
+ };
673
+ }
674
+
675
+ /**
676
+ * Convert TypeScript type to JSON Schema
677
+ */
678
+ private tsTypeToJsonSchema(tsType: string): JsonSchemaDefinition {
679
+ const type = tsType.toLowerCase().trim();
680
+
681
+ if (type === 'string') return { type: 'string' };
682
+ if (type === 'number') return { type: 'number' };
683
+ if (type === 'boolean') return { type: 'boolean' };
684
+ if (type.endsWith('[]')) {
685
+ const itemType = type.slice(0, -2);
686
+ return { type: 'array', items: this.tsTypeToJsonSchema(itemType) };
687
+ }
688
+ if (type === 'any' || type === 'unknown') return { type: 'object' };
689
+
690
+ // Check if it's a reference to a known schema
691
+ const originalType = tsType.trim();
692
+ if (this.schemas[originalType]) {
693
+ return { $ref: `#/components/schemas/${originalType}` };
694
+ }
695
+
696
+ return { type: 'object' };
697
+ }
698
+
699
+ /**
700
+ * Generate operation ID
701
+ */
702
+ private generateOperationId(method: HttpMethod, path: string): string {
703
+ const cleanPath = path
704
+ .replace(/\/api\//g, '_')
705
+ .replace(/[/{}]/g, '_')
706
+ .replace(/_+/g, '_')
707
+ .replace(/^_|_$/g, '');
708
+ return `${method}_${cleanPath || 'root'}`;
709
+ }
710
+
711
+ /**
712
+ * Infer tags from path
713
+ */
714
+ private inferTags(path: string): string[] {
715
+ const parts = path.split('/').filter(p => p && p !== 'api' && !p.startsWith('{'));
716
+ if (parts.length > 0) {
717
+ return [parts[0].charAt(0).toUpperCase() + parts[0].slice(1)];
718
+ }
719
+ return ['API'];
720
+ }
721
+
722
+ /**
723
+ * Get line number from character index
724
+ */
725
+ private getLineNumber(content: string, index: number): number {
726
+ return content.slice(0, index).split('\n').length;
727
+ }
728
+
729
+ /**
730
+ * Detect security schemes
731
+ */
732
+ private detectSecuritySchemes(files: FileContent[]): Record<string, OpenAPIV3_1.SecuritySchemeObject> {
733
+ const schemes: Record<string, OpenAPIV3_1.SecuritySchemeObject> = {};
734
+
735
+ for (const file of files) {
736
+ const content = file.content;
737
+
738
+ // Check for authorization header usage
739
+ if (content.includes('authorization') || content.includes('Authorization')) {
740
+ schemes.bearerAuth = {
741
+ type: 'http',
742
+ scheme: 'bearer',
743
+ bearerFormat: 'JWT',
744
+ };
745
+ }
746
+
747
+ // Check for API key usage
748
+ if (content.includes('x-api-key') || content.includes('X-API-Key')) {
749
+ schemes.apiKey = {
750
+ type: 'apiKey',
751
+ in: 'header',
752
+ name: 'X-API-Key',
753
+ };
754
+ }
755
+
756
+ // Check for NextAuth
757
+ if (content.includes('next-auth') || content.includes('getServerSession')) {
758
+ schemes.session = {
759
+ type: 'apiKey',
760
+ in: 'cookie',
761
+ name: 'next-auth.session-token',
762
+ };
763
+ }
764
+ }
765
+
766
+ return schemes;
767
+ }
768
+ }