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,700 @@
1
+ /**
2
+ * @fileoverview analyzer module implementation
3
+ * Enhanced with OpenAPI 3.1 support, example extraction, and smart grouping
4
+ * @copyright Copyright (c) 2024-2026 nirholas
5
+ * @license MIT
6
+ */
7
+
8
+ import { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
9
+
10
+ export interface EndpointInfo {
11
+ path: string;
12
+ method: string;
13
+ operationId?: string;
14
+ summary?: string;
15
+ description?: string;
16
+ tags?: string[];
17
+ parameters: ParameterInfo[];
18
+ requestBody?: RequestBodyInfo;
19
+ responses: ResponseInfo[];
20
+ security?: SecurityRequirement[];
21
+ deprecated?: boolean;
22
+ isWebhook?: boolean;
23
+ examples?: OperationExample[];
24
+ }
25
+
26
+ export interface ParameterInfo {
27
+ name: string;
28
+ in: 'path' | 'query' | 'header' | 'cookie';
29
+ required: boolean;
30
+ description?: string;
31
+ schema: any;
32
+ example?: any;
33
+ examples?: Record<string, any>;
34
+ }
35
+
36
+ export interface RequestBodyInfo {
37
+ required: boolean;
38
+ description?: string;
39
+ content: Record<string, {
40
+ schema: any;
41
+ example?: any;
42
+ examples?: Record<string, any>;
43
+ }>;
44
+ }
45
+
46
+ export interface ResponseInfo {
47
+ statusCode: string;
48
+ description?: string;
49
+ content?: Record<string, {
50
+ schema: any;
51
+ example?: any;
52
+ examples?: Record<string, any>;
53
+ }>;
54
+ }
55
+
56
+ export interface SecurityRequirement {
57
+ name: string;
58
+ scopes?: string[];
59
+ }
60
+
61
+ export interface PaginationPattern {
62
+ type: 'offset' | 'cursor' | 'page';
63
+ limitParam?: string;
64
+ offsetParam?: string;
65
+ cursorParam?: string;
66
+ pageParam?: string;
67
+ hasMoreField?: string;
68
+ nextCursorField?: string;
69
+ }
70
+
71
+ export interface OperationExample {
72
+ name?: string;
73
+ input?: any;
74
+ output?: any;
75
+ description?: string;
76
+ }
77
+
78
+ export interface GroupingOptions {
79
+ groupBy: 'tags' | 'paths' | 'none';
80
+ includeDeprecated?: boolean;
81
+ operationFilter?: (endpoint: EndpointInfo) => boolean;
82
+ }
83
+
84
+ export interface AuthDetectionResult {
85
+ type: 'apiKey' | 'bearer' | 'basic' | 'oauth2' | 'openIdConnect' | 'unknown';
86
+ name?: string;
87
+ in?: 'header' | 'query' | 'cookie';
88
+ headerName?: string;
89
+ envVar?: string;
90
+ flows?: Record<string, any>;
91
+ }
92
+
93
+ /**
94
+ * Analyzes OpenAPI specs to extract endpoint information
95
+ * Enhanced with OpenAPI 3.1 support
96
+ */
97
+ export class OpenApiAnalyzer {
98
+ constructor(private spec: OpenAPIV3.Document | OpenAPIV3_1.Document) {}
99
+
100
+ /**
101
+ * Extract all endpoints from the spec (including webhooks for 3.1)
102
+ */
103
+ extractEndpoints(filters?: {
104
+ tags?: string[];
105
+ paths?: string[];
106
+ methods?: string[];
107
+ includeWebhooks?: boolean;
108
+ }): EndpointInfo[] {
109
+ const endpoints: EndpointInfo[] = [];
110
+
111
+ // Extract from paths
112
+ const paths = this.spec.paths || {};
113
+ for (const [path, pathItem] of Object.entries(paths)) {
114
+ if (!pathItem) continue;
115
+
116
+ // Apply path filter
117
+ if (filters?.paths && !this.matchesPathFilter(path, filters.paths)) {
118
+ continue;
119
+ }
120
+
121
+ const pathEndpoints = this.extractPathEndpoints(path, pathItem, filters, false);
122
+ endpoints.push(...pathEndpoints);
123
+ }
124
+
125
+ // Extract from webhooks (OpenAPI 3.1)
126
+ if (filters?.includeWebhooks !== false) {
127
+ const webhooks = (this.spec as any).webhooks || {};
128
+ for (const [webhookName, pathItem] of Object.entries(webhooks)) {
129
+ if (!pathItem) continue;
130
+ const webhookEndpoints = this.extractPathEndpoints(
131
+ `webhook:${webhookName}`,
132
+ pathItem as any,
133
+ filters,
134
+ true
135
+ );
136
+ endpoints.push(...webhookEndpoints);
137
+ }
138
+ }
139
+
140
+ return endpoints;
141
+ }
142
+
143
+ /**
144
+ * Extract endpoints from a path item
145
+ */
146
+ private extractPathEndpoints(
147
+ path: string,
148
+ pathItem: OpenAPIV3.PathItemObject,
149
+ filters?: { tags?: string[]; methods?: string[] },
150
+ isWebhook: boolean = false
151
+ ): EndpointInfo[] {
152
+ const endpoints: EndpointInfo[] = [];
153
+ const methods = ['get', 'post', 'put', 'delete', 'patch', 'options', 'head'] as const;
154
+
155
+ for (const method of methods) {
156
+ const operation = pathItem[method];
157
+ if (!operation) continue;
158
+
159
+ // Apply method filter
160
+ if (filters?.methods && !filters.methods.includes(method.toUpperCase())) {
161
+ continue;
162
+ }
163
+
164
+ // Apply tag filter
165
+ if (filters?.tags && operation.tags) {
166
+ const hasMatchingTag = operation.tags.some(tag => filters.tags!.includes(tag));
167
+ if (!hasMatchingTag) continue;
168
+ }
169
+
170
+ const endpoint = this.extractEndpoint(path, method, operation, pathItem, isWebhook);
171
+ endpoints.push(endpoint);
172
+ }
173
+
174
+ return endpoints;
175
+ }
176
+
177
+ /**
178
+ * Extract single endpoint info
179
+ */
180
+ private extractEndpoint(
181
+ path: string,
182
+ method: string,
183
+ operation: OpenAPIV3.OperationObject,
184
+ pathItem: OpenAPIV3.PathItemObject,
185
+ isWebhook: boolean = false
186
+ ): EndpointInfo {
187
+ return {
188
+ path,
189
+ method: method.toUpperCase(),
190
+ operationId: operation.operationId,
191
+ summary: operation.summary,
192
+ description: operation.description,
193
+ tags: operation.tags,
194
+ parameters: this.extractParameters(operation, pathItem),
195
+ requestBody: this.extractRequestBody(operation),
196
+ responses: this.extractResponses(operation),
197
+ security: this.extractSecurity(operation),
198
+ deprecated: operation.deprecated,
199
+ isWebhook,
200
+ examples: this.extractExamples(operation),
201
+ };
202
+ }
203
+
204
+ /**
205
+ * Extract parameters from operation
206
+ */
207
+ private extractParameters(
208
+ operation: OpenAPIV3.OperationObject,
209
+ pathItem: OpenAPIV3.PathItemObject
210
+ ): ParameterInfo[] {
211
+ const params: ParameterInfo[] = [];
212
+
213
+ // Combine path-level and operation-level parameters
214
+ const allParams = [
215
+ ...(pathItem.parameters || []),
216
+ ...(operation.parameters || []),
217
+ ];
218
+
219
+ for (const param of allParams) {
220
+ if ('$ref' in param) {
221
+ // Skip unresolved refs (should be resolved by parser)
222
+ continue;
223
+ }
224
+
225
+ const paramObj = param as OpenAPIV3.ParameterObject;
226
+ params.push({
227
+ name: param.name,
228
+ in: param.in as any,
229
+ required: param.required || param.in === 'path',
230
+ description: param.description,
231
+ schema: paramObj.schema,
232
+ example: paramObj.example,
233
+ examples: paramObj.examples ? this.normalizeExamples(paramObj.examples) : undefined,
234
+ });
235
+ }
236
+
237
+ return params;
238
+ }
239
+
240
+ /**
241
+ * Extract request body info
242
+ */
243
+ private extractRequestBody(operation: OpenAPIV3.OperationObject): RequestBodyInfo | undefined {
244
+ if (!operation.requestBody || '$ref' in operation.requestBody) {
245
+ return undefined;
246
+ }
247
+
248
+ const requestBody = operation.requestBody;
249
+ const content: Record<string, { schema: any; example?: any; examples?: Record<string, any> }> = {};
250
+
251
+ for (const [mediaType, mediaTypeObj] of Object.entries(requestBody.content || {})) {
252
+ content[mediaType] = {
253
+ schema: mediaTypeObj.schema,
254
+ example: mediaTypeObj.example,
255
+ examples: mediaTypeObj.examples ? this.normalizeExamples(mediaTypeObj.examples) : undefined,
256
+ };
257
+ }
258
+
259
+ return {
260
+ required: requestBody.required || false,
261
+ description: requestBody.description,
262
+ content,
263
+ };
264
+ }
265
+
266
+ /**
267
+ * Extract response info
268
+ */
269
+ private extractResponses(operation: OpenAPIV3.OperationObject): ResponseInfo[] {
270
+ const responses: ResponseInfo[] = [];
271
+
272
+ for (const [statusCode, response] of Object.entries(operation.responses || {})) {
273
+ if ('$ref' in response) continue;
274
+
275
+ const content: Record<string, { schema: any; example?: any; examples?: Record<string, any> }> = {};
276
+
277
+ if (response.content) {
278
+ for (const [mediaType, mediaTypeObj] of Object.entries(response.content)) {
279
+ content[mediaType] = {
280
+ schema: mediaTypeObj.schema,
281
+ example: mediaTypeObj.example,
282
+ examples: mediaTypeObj.examples ? this.normalizeExamples(mediaTypeObj.examples) : undefined,
283
+ };
284
+ }
285
+ }
286
+
287
+ responses.push({
288
+ statusCode,
289
+ description: response.description,
290
+ content: Object.keys(content).length > 0 ? content : undefined,
291
+ });
292
+ }
293
+
294
+ return responses;
295
+ }
296
+
297
+ /**
298
+ * Normalize examples object (handle ExampleObject vs ReferenceObject)
299
+ */
300
+ private normalizeExamples(examples: Record<string, any>): Record<string, any> {
301
+ const normalized: Record<string, any> = {};
302
+ for (const [name, example] of Object.entries(examples)) {
303
+ if ('$ref' in example) continue;
304
+ normalized[name] = example.value !== undefined ? example.value : example;
305
+ }
306
+ return normalized;
307
+ }
308
+
309
+ /**
310
+ * Extract examples from operation for tool metadata
311
+ */
312
+ extractExamples(operation: OpenAPIV3.OperationObject): OperationExample[] {
313
+ const examples: OperationExample[] = [];
314
+
315
+ // Get request body examples
316
+ const requestExamples: Map<string, any> = new Map();
317
+ if (operation.requestBody && !('$ref' in operation.requestBody)) {
318
+ const jsonContent = operation.requestBody.content?.['application/json'];
319
+ if (jsonContent) {
320
+ if (jsonContent.example) {
321
+ requestExamples.set('default', jsonContent.example);
322
+ }
323
+ if (jsonContent.examples) {
324
+ for (const [name, ex] of Object.entries(jsonContent.examples)) {
325
+ if ('$ref' in ex) continue;
326
+ requestExamples.set(name, ex.value);
327
+ }
328
+ }
329
+ }
330
+ }
331
+
332
+ // Get response examples (from 200/201 responses)
333
+ const responseExamples: Map<string, any> = new Map();
334
+ for (const [statusCode, response] of Object.entries(operation.responses || {})) {
335
+ if ('$ref' in response) continue;
336
+ if (!statusCode.startsWith('2')) continue;
337
+
338
+ const jsonContent = response.content?.['application/json'];
339
+ if (jsonContent) {
340
+ if (jsonContent.example) {
341
+ responseExamples.set('default', jsonContent.example);
342
+ }
343
+ if (jsonContent.examples) {
344
+ for (const [name, ex] of Object.entries(jsonContent.examples)) {
345
+ if ('$ref' in ex) continue;
346
+ responseExamples.set(name, ex.value);
347
+ }
348
+ }
349
+ }
350
+ }
351
+
352
+ // Combine request and response examples
353
+ if (requestExamples.size > 0 || responseExamples.size > 0) {
354
+ // Try to match by name
355
+ for (const [name, input] of requestExamples) {
356
+ const output = responseExamples.get(name) || responseExamples.get('default');
357
+ examples.push({ name, input, output });
358
+ }
359
+
360
+ // Add any unmatched response examples
361
+ for (const [name, output] of responseExamples) {
362
+ if (!requestExamples.has(name) && name !== 'default') {
363
+ examples.push({ name, output });
364
+ }
365
+ }
366
+
367
+ // If we only have a default response example, add it
368
+ if (requestExamples.size === 0 && responseExamples.has('default')) {
369
+ examples.push({ name: 'default', output: responseExamples.get('default') });
370
+ }
371
+ }
372
+
373
+ return examples;
374
+ }
375
+
376
+ /**
377
+ * Extract security requirements
378
+ */
379
+ private extractSecurity(operation: OpenAPIV3.OperationObject): SecurityRequirement[] | undefined {
380
+ const security = operation.security || this.spec.security;
381
+ if (!security) return undefined;
382
+
383
+ const requirements: SecurityRequirement[] = [];
384
+
385
+ for (const requirement of security) {
386
+ for (const [name, scopes] of Object.entries(requirement)) {
387
+ requirements.push({ name, scopes });
388
+ }
389
+ }
390
+
391
+ return requirements.length > 0 ? requirements : undefined;
392
+ }
393
+
394
+ /**
395
+ * Detect pagination pattern in endpoint
396
+ */
397
+ detectPagination(endpoint: EndpointInfo): PaginationPattern | null {
398
+ const params = endpoint.parameters;
399
+
400
+ // Check for common pagination parameters
401
+ const limitParam = params.find(p =>
402
+ ['limit', 'per_page', 'page_size', 'size', 'count', 'max_results'].includes(p.name.toLowerCase())
403
+ );
404
+
405
+ const offsetParam = params.find(p =>
406
+ ['offset', 'skip', 'start', 'from'].includes(p.name.toLowerCase())
407
+ );
408
+
409
+ const cursorParam = params.find(p =>
410
+ ['cursor', 'next_cursor', 'starting_after', 'after', 'page_token', 'continuation_token'].includes(p.name.toLowerCase())
411
+ );
412
+
413
+ const pageParam = params.find(p =>
414
+ ['page', 'page_number', 'page_num', 'p'].includes(p.name.toLowerCase())
415
+ );
416
+
417
+ // Determine pagination type
418
+ if (cursorParam) {
419
+ return {
420
+ type: 'cursor',
421
+ limitParam: limitParam?.name,
422
+ cursorParam: cursorParam.name,
423
+ };
424
+ } else if (offsetParam) {
425
+ return {
426
+ type: 'offset',
427
+ limitParam: limitParam?.name,
428
+ offsetParam: offsetParam.name,
429
+ };
430
+ } else if (pageParam) {
431
+ return {
432
+ type: 'page',
433
+ limitParam: limitParam?.name,
434
+ pageParam: pageParam.name,
435
+ };
436
+ }
437
+
438
+ return null;
439
+ }
440
+
441
+ /**
442
+ * Smart operation grouping with multiple strategies
443
+ */
444
+ groupEndpoints(
445
+ endpoints: EndpointInfo[],
446
+ options: GroupingOptions = { groupBy: 'tags' }
447
+ ): Record<string, EndpointInfo[]> {
448
+ let filtered = endpoints;
449
+
450
+ // Apply deprecated filter
451
+ if (!options.includeDeprecated) {
452
+ filtered = filtered.filter(e => !e.deprecated);
453
+ }
454
+
455
+ // Apply custom filter
456
+ if (options.operationFilter) {
457
+ filtered = filtered.filter(options.operationFilter);
458
+ }
459
+
460
+ const groups: Record<string, EndpointInfo[]> = {};
461
+
462
+ if (options.groupBy === 'none') {
463
+ groups['all'] = filtered;
464
+ return groups;
465
+ }
466
+
467
+ for (const endpoint of filtered) {
468
+ let groupKey: string;
469
+
470
+ if (options.groupBy === 'tags') {
471
+ // Use first tag or 'default'
472
+ groupKey = endpoint.tags?.[0] || 'default';
473
+ } else {
474
+ // Smart path grouping: detect resource names
475
+ groupKey = this.getResourceGroupFromPath(endpoint.path);
476
+ }
477
+
478
+ if (!groups[groupKey]) {
479
+ groups[groupKey] = [];
480
+ }
481
+ groups[groupKey].push(endpoint);
482
+ }
483
+
484
+ return groups;
485
+ }
486
+
487
+ /**
488
+ * Extract resource group from path (for CRUD detection)
489
+ */
490
+ private getResourceGroupFromPath(path: string): string {
491
+ const segments = path.split('/').filter(Boolean);
492
+
493
+ // Find the first non-parameter segment
494
+ for (const segment of segments) {
495
+ if (!segment.startsWith('{') && !segment.startsWith(':')) {
496
+ // Skip common API prefixes
497
+ if (['api', 'v1', 'v2', 'v3', 'rest', 'graphql'].includes(segment.toLowerCase())) {
498
+ continue;
499
+ }
500
+ return segment;
501
+ }
502
+ }
503
+
504
+ return 'default';
505
+ }
506
+
507
+ /**
508
+ * Detect CRUD pattern for an endpoint
509
+ */
510
+ detectCrudOperation(endpoint: EndpointInfo): 'list' | 'get' | 'create' | 'update' | 'delete' | 'other' {
511
+ const method = endpoint.method.toUpperCase();
512
+ const hasPathParam = endpoint.path.includes('{') || endpoint.path.includes(':');
513
+
514
+ switch (method) {
515
+ case 'GET':
516
+ return hasPathParam ? 'get' : 'list';
517
+ case 'POST':
518
+ return 'create';
519
+ case 'PUT':
520
+ case 'PATCH':
521
+ return 'update';
522
+ case 'DELETE':
523
+ return 'delete';
524
+ default:
525
+ return 'other';
526
+ }
527
+ }
528
+
529
+ /**
530
+ * Generate smart tool name based on CRUD patterns
531
+ */
532
+ generateSmartToolName(endpoint: EndpointInfo): string {
533
+ const crud = this.detectCrudOperation(endpoint);
534
+ const resource = this.getResourceGroupFromPath(endpoint.path);
535
+
536
+ // Use singular form for single-resource operations
537
+ const resourceName = crud === 'list' ? resource : this.singularize(resource);
538
+
539
+ return `${resource}_${crud}`;
540
+ }
541
+
542
+ /**
543
+ * Simple singularize (remove trailing 's')
544
+ */
545
+ private singularize(word: string): string {
546
+ if (word.endsWith('ies')) {
547
+ return word.slice(0, -3) + 'y';
548
+ }
549
+ if (word.endsWith('es')) {
550
+ return word.slice(0, -2);
551
+ }
552
+ if (word.endsWith('s') && !word.endsWith('ss')) {
553
+ return word.slice(0, -1);
554
+ }
555
+ return word;
556
+ }
557
+
558
+ /**
559
+ * Detect authentication requirements with enhanced info
560
+ */
561
+ detectAuthentication(): AuthDetectionResult[] {
562
+ const securitySchemes = this.spec.components?.securitySchemes || {};
563
+ const results: AuthDetectionResult[] = [];
564
+
565
+ for (const [name, scheme] of Object.entries(securitySchemes)) {
566
+ if ('$ref' in scheme) continue;
567
+
568
+ const result: AuthDetectionResult = {
569
+ type: 'unknown',
570
+ name,
571
+ };
572
+
573
+ switch (scheme.type) {
574
+ case 'apiKey':
575
+ result.type = 'apiKey';
576
+ result.in = scheme.in as any;
577
+ result.headerName = scheme.name;
578
+ result.envVar = this.generateEnvVarName(name, 'API_KEY');
579
+ break;
580
+ case 'http':
581
+ if (scheme.scheme === 'bearer') {
582
+ result.type = 'bearer';
583
+ result.envVar = this.generateEnvVarName(name, 'TOKEN');
584
+ } else if (scheme.scheme === 'basic') {
585
+ result.type = 'basic';
586
+ result.envVar = this.generateEnvVarName(name, 'AUTH');
587
+ }
588
+ break;
589
+ case 'oauth2':
590
+ result.type = 'oauth2';
591
+ result.flows = scheme.flows;
592
+ result.envVar = this.generateEnvVarName(name, 'OAUTH_TOKEN');
593
+ break;
594
+ case 'openIdConnect':
595
+ result.type = 'openIdConnect';
596
+ result.envVar = this.generateEnvVarName(name, 'OIDC_TOKEN');
597
+ break;
598
+ }
599
+
600
+ results.push(result);
601
+ }
602
+
603
+ return results;
604
+ }
605
+
606
+ /**
607
+ * Generate environment variable name
608
+ */
609
+ private generateEnvVarName(schemeName: string, suffix: string): string {
610
+ return `${schemeName.toUpperCase().replace(/[^A-Z0-9_]/g, '_')}_${suffix}`;
611
+ }
612
+
613
+ /**
614
+ * Get authentication info (legacy method for compatibility)
615
+ */
616
+ getAuthenticationInfo() {
617
+ const securitySchemes = this.spec.components?.securitySchemes || {};
618
+ const auth: {
619
+ type: string;
620
+ scheme?: string;
621
+ in?: string;
622
+ name?: string;
623
+ flows?: any;
624
+ }[] = [];
625
+
626
+ for (const [name, scheme] of Object.entries(securitySchemes)) {
627
+ if ('$ref' in scheme) continue;
628
+
629
+ auth.push({
630
+ type: scheme.type,
631
+ scheme: 'scheme' in scheme ? scheme.scheme : undefined,
632
+ in: 'in' in scheme ? scheme.in : undefined,
633
+ name: 'name' in scheme ? scheme.name : undefined,
634
+ flows: 'flows' in scheme ? scheme.flows : undefined,
635
+ });
636
+ }
637
+
638
+ return auth;
639
+ }
640
+
641
+ /**
642
+ * Match path against filter patterns
643
+ */
644
+ private matchesPathFilter(path: string, patterns: string[]): boolean {
645
+ return patterns.some(pattern => {
646
+ // Convert glob-like pattern to regex
647
+ const regex = new RegExp(
648
+ '^' + pattern.replace(/\*/g, '.*').replace(/\//g, '\\/') + '$'
649
+ );
650
+ return regex.test(path);
651
+ });
652
+ }
653
+
654
+ /**
655
+ * Get analytics about the endpoints
656
+ */
657
+ getEndpointStats(endpoints: EndpointInfo[]) {
658
+ const methods = new Map<string, number>();
659
+ const tags = new Map<string, number>();
660
+ const crudOps = new Map<string, number>();
661
+ let withPagination = 0;
662
+ let withAuth = 0;
663
+ let deprecated = 0;
664
+ let webhooks = 0;
665
+ let withExamples = 0;
666
+
667
+ for (const endpoint of endpoints) {
668
+ // Count methods
669
+ methods.set(endpoint.method, (methods.get(endpoint.method) || 0) + 1);
670
+
671
+ // Count tags
672
+ endpoint.tags?.forEach(tag => {
673
+ tags.set(tag, (tags.get(tag) || 0) + 1);
674
+ });
675
+
676
+ // Count CRUD operations
677
+ const crud = this.detectCrudOperation(endpoint);
678
+ crudOps.set(crud, (crudOps.get(crud) || 0) + 1);
679
+
680
+ // Check features
681
+ if (this.detectPagination(endpoint)) withPagination++;
682
+ if (endpoint.security && endpoint.security.length > 0) withAuth++;
683
+ if (endpoint.deprecated) deprecated++;
684
+ if (endpoint.isWebhook) webhooks++;
685
+ if (endpoint.examples && endpoint.examples.length > 0) withExamples++;
686
+ }
687
+
688
+ return {
689
+ total: endpoints.length,
690
+ byMethod: Object.fromEntries(methods),
691
+ byTag: Object.fromEntries(tags),
692
+ byCrud: Object.fromEntries(crudOps),
693
+ withPagination,
694
+ withAuth,
695
+ deprecated,
696
+ webhooks,
697
+ withExamples,
698
+ };
699
+ }
700
+ }