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.
- package/.env.example +8 -0
- package/.github/CODEOWNERS +6 -0
- package/.husky/pre-commit +1 -0
- package/.nvmrc +1 -0
- package/.prettierignore +5 -0
- package/.prettierrc +7 -0
- package/.vscode/settings.json +4 -0
- package/ARCHITECTURE.md +1429 -0
- package/CHANGELOG.md +167 -0
- package/CONTRIBUTING.md +327 -0
- package/LICENSE +201 -0
- package/README.md +1028 -0
- package/SECURITY.md +248 -0
- package/VISUAL_GUIDE.md +437 -0
- package/apps/vscode/IMPLEMENTATION.md +480 -0
- package/apps/vscode/README.md +248 -0
- package/apps/vscode/package.json +381 -0
- package/apps/vscode/resources/icon.png +0 -0
- package/apps/vscode/resources/icon.svg +5 -0
- package/apps/vscode/src/commands/browseRegistry.ts +211 -0
- package/apps/vscode/src/commands/configureClaudeDesktop.ts +332 -0
- package/apps/vscode/src/commands/convert.ts +82 -0
- package/apps/vscode/src/commands/convertCurrentRepo.ts +109 -0
- package/apps/vscode/src/commands/convertFromUrl.ts +138 -0
- package/apps/vscode/src/commands/index.ts +121 -0
- package/apps/vscode/src/commands/validate.ts +197 -0
- package/apps/vscode/src/extension.ts +464 -0
- package/apps/vscode/src/global.d.ts +36 -0
- package/apps/vscode/src/test/extension.test.ts +73 -0
- package/apps/vscode/src/utils/file-generator.ts +529 -0
- package/apps/vscode/src/utils/github-api.ts +335 -0
- package/apps/vscode/src/utils/index.ts +29 -0
- package/apps/vscode/src/utils/mcp-config.ts +334 -0
- package/apps/vscode/src/utils/storage.ts +87 -0
- package/apps/vscode/src/views/McpServersTreeView.ts +160 -0
- package/apps/vscode/src/views/OutputChannelView.ts +195 -0
- package/apps/vscode/src/views/StatusBarItem.ts +251 -0
- package/apps/vscode/src/views/ToolsExplorerView.ts +314 -0
- package/apps/vscode/src/views/historyProvider.ts +75 -0
- package/apps/vscode/src/views/index.ts +12 -0
- package/apps/vscode/src/views/resultsPanel.ts +330 -0
- package/apps/vscode/src/webviews/ConversionPanel.ts +350 -0
- package/apps/vscode/src/webviews/ToolDetailsPanel.ts +448 -0
- package/apps/vscode/src/webviews/index.ts +9 -0
- package/apps/vscode/src/webviews/webview-ui/styles.ts +492 -0
- package/apps/vscode/tsconfig.json +20 -0
- package/apps/web/PLAYGROUND_GUIDE.md +499 -0
- package/apps/web/README.md +505 -0
- package/apps/web/app/api/convert/route.ts +100 -0
- package/apps/web/app/api/convert/stream/route.ts +198 -0
- package/apps/web/app/api/deploy/route.ts +157 -0
- package/apps/web/app/api/edge/route.ts +308 -0
- package/apps/web/app/api/export-docker/route.ts +284 -0
- package/apps/web/app/api/generate-openapi/route.ts +119 -0
- package/apps/web/app/api/mcp/[serverId]/route.ts +263 -0
- package/apps/web/app/api/playground/connect/route.ts +143 -0
- package/apps/web/app/api/playground/disconnect/route.ts +78 -0
- package/apps/web/app/api/playground/execute/route.ts +135 -0
- package/apps/web/app/api/playground/sessions/route.ts +103 -0
- package/apps/web/app/api/playground/tools/route.ts +117 -0
- package/apps/web/app/api/playground/v2/connect/route.ts +96 -0
- package/apps/web/app/api/playground/v2/disconnect/route.ts +88 -0
- package/apps/web/app/api/playground/v2/health/route.ts +80 -0
- package/apps/web/app/api/playground/v2/prompts/route.ts +160 -0
- package/apps/web/app/api/playground/v2/resources/route.ts +159 -0
- package/apps/web/app/api/playground/v2/sessions/route.ts +184 -0
- package/apps/web/app/api/playground/v2/tools/route.ts +167 -0
- package/apps/web/app/api/stream/route.ts +232 -0
- package/apps/web/app/batch/BatchConvertClient.tsx +190 -0
- package/apps/web/app/batch/page.tsx +37 -0
- package/apps/web/app/convert/page.tsx +269 -0
- package/apps/web/app/dashboard/page.tsx +380 -0
- package/apps/web/app/globals.css +622 -0
- package/apps/web/app/layout.tsx +120 -0
- package/apps/web/app/manifest.ts +31 -0
- package/apps/web/app/opengraph-image.tsx +112 -0
- package/apps/web/app/page.old.tsx +924 -0
- package/apps/web/app/page.tsx +77 -0
- package/apps/web/app/playground/page.tsx +306 -0
- package/apps/web/app/playground/v2/error.tsx +163 -0
- package/apps/web/app/playground/v2/layout.tsx +58 -0
- package/apps/web/app/playground/v2/loading.tsx +152 -0
- package/apps/web/app/playground/v2/page.tsx +644 -0
- package/apps/web/app/playground/v2/providers.tsx +214 -0
- package/apps/web/app/playground/v2/use-shortcuts.ts +209 -0
- package/apps/web/app/playground/v2/use-url-state.ts +296 -0
- package/apps/web/app/providers.tsx +22 -0
- package/apps/web/app/sitemap.ts +32 -0
- package/apps/web/app/twitter-image.tsx +112 -0
- package/apps/web/components/BranchSelector.tsx +401 -0
- package/apps/web/components/ClaudeConfigExport.tsx +226 -0
- package/apps/web/components/Features.tsx +84 -0
- package/apps/web/components/Footer.tsx +119 -0
- package/apps/web/components/GenerationProgress.tsx +248 -0
- package/apps/web/components/GithubUrlInput.tsx +483 -0
- package/apps/web/components/Header.tsx +175 -0
- package/apps/web/components/Hero.tsx +117 -0
- package/apps/web/components/HowItWorks.tsx +119 -0
- package/apps/web/components/InstallBanner.tsx +158 -0
- package/apps/web/components/Logo.tsx +116 -0
- package/apps/web/components/ParticleBackground.tsx +105 -0
- package/apps/web/components/Playground.tsx +472 -0
- package/apps/web/components/PlaygroundToolTester.tsx +410 -0
- package/apps/web/components/ProductCards.tsx +179 -0
- package/apps/web/components/SplitView.tsx +194 -0
- package/apps/web/components/ToolFilter.tsx +260 -0
- package/apps/web/components/ToolList.tsx +325 -0
- package/apps/web/components/batch/BatchConvert.tsx +785 -0
- package/apps/web/components/batch/index.ts +7 -0
- package/apps/web/components/convert/ConfigTabs.tsx +230 -0
- package/apps/web/components/convert/ConversionResult.tsx +482 -0
- package/apps/web/components/convert/InlinePlayground.tsx +259 -0
- package/apps/web/components/convert/LoadingSteps.tsx +311 -0
- package/apps/web/components/convert/OneClickInstall.tsx +224 -0
- package/apps/web/components/convert/ToolCard.tsx +189 -0
- package/apps/web/components/convert/TryInPlayground.tsx +242 -0
- package/apps/web/components/convert/index.ts +12 -0
- package/apps/web/components/deploy/DeployButton.tsx +369 -0
- package/apps/web/components/deploy/index.ts +7 -0
- package/apps/web/components/docker/DockerExport.tsx +690 -0
- package/apps/web/components/docker/index.ts +7 -0
- package/apps/web/components/install/OneClickInstall.tsx +676 -0
- package/apps/web/components/install/index.ts +7 -0
- package/apps/web/components/playground/CapabilityTabs.tsx +150 -0
- package/apps/web/components/playground/ConnectionStatusV2.tsx +322 -0
- package/apps/web/components/playground/EmptyStates.tsx +305 -0
- package/apps/web/components/playground/ExecutionLog.tsx +260 -0
- package/apps/web/components/playground/ExecutionLogV2.tsx +378 -0
- package/apps/web/components/playground/JsonViewer.tsx +388 -0
- package/apps/web/components/playground/PlaygroundLayout.tsx +244 -0
- package/apps/web/components/playground/PromptsPanel.tsx +385 -0
- package/apps/web/components/playground/ResourcesPanel.tsx +378 -0
- package/apps/web/components/playground/SchemaForm.tsx +477 -0
- package/apps/web/components/playground/ServerStatus.tsx +151 -0
- package/apps/web/components/playground/ShareButton.tsx +239 -0
- package/apps/web/components/playground/ToolsPanel.tsx +309 -0
- package/apps/web/components/playground/TransportConfigurator.tsx +563 -0
- package/apps/web/components/playground/index.ts +74 -0
- package/apps/web/components/playground/types.ts +202 -0
- package/apps/web/components/streaming/StreamingProgress.tsx +441 -0
- package/apps/web/components/streaming/index.ts +7 -0
- package/apps/web/components/ui/badge.tsx +42 -0
- package/apps/web/components/ui/button.tsx +88 -0
- package/apps/web/components/ui/card.tsx +75 -0
- package/apps/web/components/ui/code-block.tsx +122 -0
- package/apps/web/components/ui/index.ts +12 -0
- package/apps/web/components/ui/input.tsx +55 -0
- package/apps/web/components/ui/tabs.tsx +61 -0
- package/apps/web/hooks/index.ts +85 -0
- package/apps/web/hooks/types.ts +1173 -0
- package/apps/web/hooks/use-conversion.ts +133 -0
- package/apps/web/hooks/use-execution-history.ts +376 -0
- package/apps/web/hooks/use-generation-progress.ts +147 -0
- package/apps/web/hooks/use-local-storage.ts +88 -0
- package/apps/web/hooks/use-mcp-client.ts +623 -0
- package/apps/web/hooks/use-mcp-connection.ts +500 -0
- package/apps/web/hooks/use-mcp-execution.ts +282 -0
- package/apps/web/hooks/use-mcp-prompts.ts +441 -0
- package/apps/web/hooks/use-mcp-resources.ts +430 -0
- package/apps/web/hooks/use-mcp-tools.ts +540 -0
- package/apps/web/hooks/use-playground-store.ts +299 -0
- package/apps/web/hooks/use-playground.ts +184 -0
- package/apps/web/hooks/use-streaming-conversion.ts +227 -0
- package/apps/web/hooks/useBatchConversion.ts +271 -0
- package/apps/web/hooks/useDockerConfig.ts +161 -0
- package/apps/web/hooks/usePlatformDetection.ts +80 -0
- package/apps/web/hooks/useStreaming.ts +199 -0
- package/apps/web/lib/api/errors.ts +386 -0
- package/apps/web/lib/api/index.ts +137 -0
- package/apps/web/lib/api/logger.ts +187 -0
- package/apps/web/lib/api/middleware.ts +364 -0
- package/apps/web/lib/api/openapi.ts +977 -0
- package/apps/web/lib/api/session-manager.ts +594 -0
- package/apps/web/lib/api/types.ts +433 -0
- package/apps/web/lib/api/validation.ts +523 -0
- package/apps/web/lib/constants.ts +114 -0
- package/apps/web/lib/mcp/client.ts +1137 -0
- package/apps/web/lib/mcp/events.ts +651 -0
- package/apps/web/lib/mcp/index.ts +347 -0
- package/apps/web/lib/mcp/logger.ts +428 -0
- package/apps/web/lib/mcp/metrics.ts +703 -0
- package/apps/web/lib/mcp/retry.ts +616 -0
- package/apps/web/lib/mcp/session-manager.ts +779 -0
- package/apps/web/lib/mcp/transports.ts +988 -0
- package/apps/web/lib/mcp/types.ts +594 -0
- package/apps/web/lib/mcp-client-enhanced.ts +871 -0
- package/apps/web/lib/mcp-client.ts +778 -0
- package/apps/web/lib/mcp-errors.ts +489 -0
- package/apps/web/lib/mcp-sandbox.ts +593 -0
- package/apps/web/lib/mcp-testing.ts +428 -0
- package/apps/web/lib/mcp-types.ts +448 -0
- package/apps/web/lib/playground-store.tsx +1147 -0
- package/apps/web/lib/utils.ts +439 -0
- package/apps/web/next-env.d.ts +5 -0
- package/apps/web/next.config.js +23 -0
- package/apps/web/package.json +55 -0
- package/apps/web/postcss.config.js +6 -0
- package/apps/web/public/.well-known/ai-plugin.json +17 -0
- package/apps/web/public/logo.svg +6 -0
- package/apps/web/public/robots.txt +22 -0
- package/apps/web/public/schema.json +27 -0
- package/apps/web/tailwind.config.js +26 -0
- package/apps/web/tailwind.config.ts +123 -0
- package/apps/web/tsconfig.json +20 -0
- package/apps/web/types/deploy.ts +139 -0
- package/apps/web/types/index.ts +247 -0
- package/apps/web/vercel.json +39 -0
- package/eslint.config.mjs +23 -0
- package/llms.txt +102 -0
- package/mkdocs/docs/api/core.md +318 -0
- package/mkdocs/docs/api/index.md +128 -0
- package/mkdocs/docs/api/mcp-server.md +301 -0
- package/mkdocs/docs/api/openapi-parser.md +254 -0
- package/mkdocs/docs/assets/logo.svg +7 -0
- package/mkdocs/docs/changelog.md +118 -0
- package/mkdocs/docs/cli/generate.md +148 -0
- package/mkdocs/docs/cli/index.md +52 -0
- package/mkdocs/docs/cli/inspect.md +164 -0
- package/mkdocs/docs/cli/serve.md +136 -0
- package/mkdocs/docs/concepts/classification.md +254 -0
- package/mkdocs/docs/concepts/how-it-works.md +299 -0
- package/mkdocs/docs/concepts/index.md +77 -0
- package/mkdocs/docs/concepts/mcp-protocol.md +362 -0
- package/mkdocs/docs/concepts/tool-types.md +382 -0
- package/mkdocs/docs/contributing/architecture.md +262 -0
- package/mkdocs/docs/contributing/development.md +245 -0
- package/mkdocs/docs/contributing/index.md +73 -0
- package/mkdocs/docs/contributing/testing.md +320 -0
- package/mkdocs/docs/getting-started/configuration.md +235 -0
- package/mkdocs/docs/getting-started/index.md +54 -0
- package/mkdocs/docs/getting-started/installation.md +145 -0
- package/mkdocs/docs/getting-started/quickstart.md +160 -0
- package/mkdocs/docs/guides/batch.md +375 -0
- package/mkdocs/docs/guides/claude-desktop.md +227 -0
- package/mkdocs/docs/guides/cursor.md +188 -0
- package/mkdocs/docs/guides/custom-tools.md +367 -0
- package/mkdocs/docs/guides/index.md +78 -0
- package/mkdocs/docs/guides/private-repos.md +221 -0
- package/mkdocs/docs/guides/vscode.md +247 -0
- package/mkdocs/docs/index.md +175 -0
- package/mkdocs/docs/reference/config.md +223 -0
- package/mkdocs/docs/reference/env.md +192 -0
- package/mkdocs/docs/reference/index.md +102 -0
- package/mkdocs/docs/reference/tools.md +309 -0
- package/mkdocs/docs/stylesheets/extra.css +231 -0
- package/mkdocs/mkdocs.yml +204 -0
- package/mkdocs/overrides/.gitkeep +1 -0
- package/mkdocs/overrides/main.html +7 -0
- package/mkdocs/python-deps.txt +7 -0
- package/mkdocs/vercel.json +11 -0
- package/package.json +63 -0
- package/packages/core/package.json +61 -0
- package/packages/core/src/__tests__/bitbucket-client.test.ts +366 -0
- package/packages/core/src/__tests__/cli.test.ts +235 -0
- package/packages/core/src/__tests__/code-extractor.test.ts +378 -0
- package/packages/core/src/__tests__/docker-generator.test.ts +255 -0
- package/packages/core/src/__tests__/github-client.test.ts +390 -0
- package/packages/core/src/__tests__/gitlab-client.test.ts +319 -0
- package/packages/core/src/__tests__/go-extractor.test.ts +351 -0
- package/packages/core/src/__tests__/graphql-extractor.test.ts +330 -0
- package/packages/core/src/__tests__/java-extractor.test.ts +497 -0
- package/packages/core/src/__tests__/plugins.test.ts +467 -0
- package/packages/core/src/__tests__/readme-extractor.test.ts +258 -0
- package/packages/core/src/__tests__/redis-cache.test.ts +307 -0
- package/packages/core/src/__tests__/rust-extractor.test.ts +252 -0
- package/packages/core/src/__tests__/streaming.test.ts +251 -0
- package/packages/core/src/additional-extractors.ts +333 -0
- package/packages/core/src/cache/cache-interface.ts +179 -0
- package/packages/core/src/cache/index.ts +210 -0
- package/packages/core/src/cache/redis-cache.ts +291 -0
- package/packages/core/src/cache/upstash-cache.ts +379 -0
- package/packages/core/src/cache.ts +251 -0
- package/packages/core/src/cli.ts +822 -0
- package/packages/core/src/code-extractor.ts +696 -0
- package/packages/core/src/docker-generator.ts +470 -0
- package/packages/core/src/edge-compatible.ts +491 -0
- package/packages/core/src/extractors/go-extractor.ts +791 -0
- package/packages/core/src/extractors/index.ts +9 -0
- package/packages/core/src/extractors/java-extractor.ts +937 -0
- package/packages/core/src/extractors/rust-extractor.ts +744 -0
- package/packages/core/src/github-client.ts +319 -0
- package/packages/core/src/go-generator.ts +356 -0
- package/packages/core/src/graphql-extractor.ts +358 -0
- package/packages/core/src/index.ts +797 -0
- package/packages/core/src/langchain-exporter.ts +617 -0
- package/packages/core/src/language-parsers.ts +1114 -0
- package/packages/core/src/mcp-introspector.ts +279 -0
- package/packages/core/src/monorepo-detector.ts +378 -0
- package/packages/core/src/plugins/index.ts +370 -0
- package/packages/core/src/plugins/registry.ts +404 -0
- package/packages/core/src/plugins/types.ts +215 -0
- package/packages/core/src/providers/base-provider.ts +246 -0
- package/packages/core/src/providers/bitbucket-client.ts +464 -0
- package/packages/core/src/providers/gitlab-client.ts +388 -0
- package/packages/core/src/providers/index.ts +176 -0
- package/packages/core/src/python-generator.ts +260 -0
- package/packages/core/src/queue/index.ts +100 -0
- package/packages/core/src/queue/memory-queue.ts +445 -0
- package/packages/core/src/queue/redis-queue.ts +578 -0
- package/packages/core/src/queue/types.ts +251 -0
- package/packages/core/src/readme-extractor.ts +409 -0
- package/packages/core/src/schema-generator.ts +638 -0
- package/packages/core/src/streaming.ts +999 -0
- package/packages/core/src/types.ts +289 -0
- package/packages/core/tsconfig.json +9 -0
- package/packages/core/tsup.config.ts +25 -0
- package/packages/mcp-server/README.md +297 -0
- package/packages/mcp-server/package.json +55 -0
- package/packages/mcp-server/src/__tests__/mcp-server.test.ts +177 -0
- package/packages/mcp-server/src/__tests__/tools.test.ts +217 -0
- package/packages/mcp-server/src/index.ts +1206 -0
- package/packages/mcp-server/src/prompts/index.ts +601 -0
- package/packages/mcp-server/src/tools/export-docker.ts +362 -0
- package/packages/mcp-server/src/tools/generate-openapi.ts +162 -0
- package/packages/mcp-server/src/tools/monitor-mcp-server.ts +448 -0
- package/packages/mcp-server/src/tools/stream-convert.ts +398 -0
- package/packages/mcp-server/src/tools/test-mcp-tool.ts +531 -0
- package/packages/mcp-server/tsconfig.json +12 -0
- package/packages/mcp-server/tsup.config.ts +14 -0
- package/packages/openapi-parser/package-lock.json +3028 -0
- package/packages/openapi-parser/package.json +41 -0
- package/packages/openapi-parser/src/analyzer.ts +700 -0
- package/packages/openapi-parser/src/asyncapi-parser.ts +475 -0
- package/packages/openapi-parser/src/cli.ts +302 -0
- package/packages/openapi-parser/src/generator.ts +570 -0
- package/packages/openapi-parser/src/generators/express-analyzer.ts +649 -0
- package/packages/openapi-parser/src/generators/fastapi-analyzer.ts +960 -0
- package/packages/openapi-parser/src/generators/index.ts +200 -0
- package/packages/openapi-parser/src/generators/nextjs-analyzer.ts +768 -0
- package/packages/openapi-parser/src/generators/openapi-builder.ts +527 -0
- package/packages/openapi-parser/src/generators/types.ts +298 -0
- package/packages/openapi-parser/src/graphql-parser.ts +462 -0
- package/packages/openapi-parser/src/grpc-parser.ts +649 -0
- package/packages/openapi-parser/src/har-parser.ts +723 -0
- package/packages/openapi-parser/src/index.ts +635 -0
- package/packages/openapi-parser/src/insomnia-parser.ts +614 -0
- package/packages/openapi-parser/src/parser.ts +231 -0
- package/packages/openapi-parser/src/postman-parser.ts +611 -0
- package/packages/openapi-parser/src/ref-resolver.ts +313 -0
- package/packages/openapi-parser/src/transformer.ts +459 -0
- package/packages/openapi-parser/tests/generators/express.test.ts +209 -0
- package/packages/openapi-parser/tests/generators/fastapi.test.ts +236 -0
- package/packages/openapi-parser/tests/generators/nextjs.test.ts +273 -0
- package/packages/openapi-parser/tests/parsers.test.ts +847 -0
- package/packages/openapi-parser/tsconfig.json +9 -0
- package/packages/openapi-parser/tsup.config.ts +11 -0
- package/packages/registry/package.json +59 -0
- package/packages/registry/src/cli.ts +456 -0
- package/packages/registry/src/index.ts +44 -0
- package/packages/registry/src/popular/github.json +47 -0
- package/packages/registry/src/popular/index.ts +55 -0
- package/packages/registry/src/popular/linear.json +42 -0
- package/packages/registry/src/popular/notion.json +42 -0
- package/packages/registry/src/popular/openai.json +40 -0
- package/packages/registry/src/popular/resend.json +38 -0
- package/packages/registry/src/popular/slack.json +42 -0
- package/packages/registry/src/popular/stripe.json +163 -0
- package/packages/registry/src/popular/supabase.json +42 -0
- package/packages/registry/src/popular/twilio.json +40 -0
- package/packages/registry/src/popular/vercel.json +40 -0
- package/packages/registry/src/registry.ts +492 -0
- package/packages/registry/src/storage.ts +334 -0
- package/packages/registry/src/types.ts +275 -0
- package/packages/registry/src/updater.ts +208 -0
- package/packages/registry/tsconfig.json +10 -0
- package/packages/registry/tsup.config.ts +11 -0
- package/pnpm-workspace.yaml +3 -0
- package/scripts/build-docs.sh +16 -0
- package/server.json +9 -0
- package/templates/Dockerfile.python.template +60 -0
- package/templates/Dockerfile.typescript.template +60 -0
- package/templates/docker-compose.template.yml +68 -0
- package/tests/fixtures/express-app/index.js +34 -0
- package/tests/fixtures/express-app/routes/posts.js +43 -0
- package/tests/fixtures/express-app/routes/users.js +58 -0
- package/tests/fixtures/fastapi-app/main.py +125 -0
- package/tests/fixtures/fastapi-app/routes/admin.py +42 -0
- package/tests/fixtures/graphql/simple-schema.graphql +65 -0
- package/tests/fixtures/mocks/github-api-responses.json +63 -0
- package/tests/fixtures/nextjs-app/app/api/posts/route.ts +55 -0
- package/tests/fixtures/nextjs-app/app/api/users/[id]/route.ts +63 -0
- package/tests/fixtures/nextjs-app/app/api/users/route.ts +44 -0
- package/tests/fixtures/nextjs-app/pages/api/health.ts +28 -0
- package/tests/fixtures/openapi/petstore.yaml +179 -0
- package/tests/integration/langchain-export.test.ts +405 -0
- package/tests/integration/openapi-conversion.test.ts +221 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +32 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Base provider interface for git hosting platforms
|
|
3
|
+
* @copyright Copyright (c) 2024-2026 nirholas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { RepoMetadata, FileContent, ApiSpec } from '../types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Repository metadata from the provider
|
|
11
|
+
*/
|
|
12
|
+
export interface ProviderRepoMetadata {
|
|
13
|
+
stars: number;
|
|
14
|
+
language: string;
|
|
15
|
+
license?: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
defaultBranch: string;
|
|
18
|
+
forksCount?: number;
|
|
19
|
+
openIssuesCount?: number;
|
|
20
|
+
createdAt?: string;
|
|
21
|
+
updatedAt?: string;
|
|
22
|
+
size?: number;
|
|
23
|
+
topics?: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Search result from code search
|
|
28
|
+
*/
|
|
29
|
+
export interface CodeSearchResult {
|
|
30
|
+
path: string;
|
|
31
|
+
content?: string;
|
|
32
|
+
matchedLines?: Array<{
|
|
33
|
+
lineNumber: number;
|
|
34
|
+
content: string;
|
|
35
|
+
}>;
|
|
36
|
+
sha?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* File tree item
|
|
41
|
+
*/
|
|
42
|
+
export interface FileTreeItem {
|
|
43
|
+
path: string;
|
|
44
|
+
type: 'file' | 'dir' | 'tree' | 'blob';
|
|
45
|
+
size?: number;
|
|
46
|
+
sha?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Base provider interface that all git hosting providers must implement
|
|
51
|
+
*/
|
|
52
|
+
export interface BaseProvider {
|
|
53
|
+
/**
|
|
54
|
+
* Provider name (e.g., 'github', 'gitlab', 'bitbucket')
|
|
55
|
+
*/
|
|
56
|
+
readonly name: string;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Parse a URL to extract repository metadata
|
|
60
|
+
*/
|
|
61
|
+
parseUrl(url: string): RepoMetadata;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get repository metadata
|
|
65
|
+
*/
|
|
66
|
+
getRepoMetadata(owner: string, repo: string): Promise<ProviderRepoMetadata>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get README content from the repository
|
|
70
|
+
*/
|
|
71
|
+
getReadme(owner: string, repo: string, branch?: string): Promise<string | null>;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get file content from the repository
|
|
75
|
+
*/
|
|
76
|
+
getFile(owner: string, repo: string, path: string, branch?: string): Promise<FileContent | null>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* List files in a directory
|
|
80
|
+
*/
|
|
81
|
+
listFiles(owner: string, repo: string, path?: string, branch?: string): Promise<FileTreeItem[]>;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Search for code in the repository
|
|
85
|
+
*/
|
|
86
|
+
searchCode(owner: string, repo: string, query: string, options?: SearchOptions): Promise<CodeSearchResult[]>;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Find API specification files
|
|
90
|
+
*/
|
|
91
|
+
findApiSpecs?(owner: string, repo: string, branch?: string): Promise<ApiSpec[]>;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get rate limit information
|
|
95
|
+
*/
|
|
96
|
+
getRateLimit?(): Promise<RateLimitInfo>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Options for code search
|
|
101
|
+
*/
|
|
102
|
+
export interface SearchOptions {
|
|
103
|
+
/** Maximum results to return */
|
|
104
|
+
maxResults?: number;
|
|
105
|
+
/** File extensions to filter */
|
|
106
|
+
extensions?: string[];
|
|
107
|
+
/** Paths to search in */
|
|
108
|
+
paths?: string[];
|
|
109
|
+
/** Branch to search in */
|
|
110
|
+
branch?: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Rate limit information
|
|
115
|
+
*/
|
|
116
|
+
export interface RateLimitInfo {
|
|
117
|
+
remaining: number;
|
|
118
|
+
limit: number;
|
|
119
|
+
reset: Date;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Provider configuration
|
|
124
|
+
*/
|
|
125
|
+
export interface ProviderConfig {
|
|
126
|
+
/** Authentication token */
|
|
127
|
+
token?: string;
|
|
128
|
+
/** Base URL for self-hosted instances */
|
|
129
|
+
baseUrl?: string;
|
|
130
|
+
/** Request timeout in milliseconds */
|
|
131
|
+
timeout?: number;
|
|
132
|
+
/** Custom headers */
|
|
133
|
+
headers?: Record<string, string>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Abstract base class with common functionality
|
|
138
|
+
*/
|
|
139
|
+
export abstract class AbstractProvider implements BaseProvider {
|
|
140
|
+
abstract readonly name: string;
|
|
141
|
+
protected config: ProviderConfig;
|
|
142
|
+
|
|
143
|
+
constructor(config: ProviderConfig = {}) {
|
|
144
|
+
this.config = {
|
|
145
|
+
timeout: 30000,
|
|
146
|
+
...config
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
abstract parseUrl(url: string): RepoMetadata;
|
|
151
|
+
abstract getRepoMetadata(owner: string, repo: string): Promise<ProviderRepoMetadata>;
|
|
152
|
+
abstract getReadme(owner: string, repo: string, branch?: string): Promise<string | null>;
|
|
153
|
+
abstract getFile(owner: string, repo: string, path: string, branch?: string): Promise<FileContent | null>;
|
|
154
|
+
abstract listFiles(owner: string, repo: string, path?: string, branch?: string): Promise<FileTreeItem[]>;
|
|
155
|
+
abstract searchCode(owner: string, repo: string, query: string, options?: SearchOptions): Promise<CodeSearchResult[]>;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Common method to fetch with timeout
|
|
159
|
+
*/
|
|
160
|
+
protected async fetchWithTimeout(url: string, options: RequestInit = {}): Promise<Response> {
|
|
161
|
+
const controller = new AbortController();
|
|
162
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const response = await fetch(url, {
|
|
166
|
+
...options,
|
|
167
|
+
signal: controller.signal,
|
|
168
|
+
headers: {
|
|
169
|
+
...this.config.headers,
|
|
170
|
+
...options.headers
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return response;
|
|
175
|
+
} finally {
|
|
176
|
+
clearTimeout(timeoutId);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get authorization header
|
|
182
|
+
*/
|
|
183
|
+
protected getAuthHeader(): Record<string, string> {
|
|
184
|
+
if (!this.config.token) {
|
|
185
|
+
return {};
|
|
186
|
+
}
|
|
187
|
+
return { Authorization: `Bearer ${this.config.token}` };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Common README file names to search
|
|
192
|
+
*/
|
|
193
|
+
protected readonly readmeFileNames = [
|
|
194
|
+
'README.md',
|
|
195
|
+
'README.MD',
|
|
196
|
+
'readme.md',
|
|
197
|
+
'Readme.md',
|
|
198
|
+
'README.rst',
|
|
199
|
+
'readme.rst',
|
|
200
|
+
'README.txt',
|
|
201
|
+
'readme.txt',
|
|
202
|
+
'README'
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Common API spec locations
|
|
207
|
+
*/
|
|
208
|
+
protected readonly apiSpecLocations = [
|
|
209
|
+
'openapi.json',
|
|
210
|
+
'openapi.yaml',
|
|
211
|
+
'openapi.yml',
|
|
212
|
+
'swagger.json',
|
|
213
|
+
'swagger.yaml',
|
|
214
|
+
'swagger.yml',
|
|
215
|
+
'api/openapi.json',
|
|
216
|
+
'api/openapi.yaml',
|
|
217
|
+
'api/swagger.json',
|
|
218
|
+
'api/swagger.yaml',
|
|
219
|
+
'spec/openapi.json',
|
|
220
|
+
'spec/openapi.yaml',
|
|
221
|
+
'docs/openapi.json',
|
|
222
|
+
'docs/openapi.yaml',
|
|
223
|
+
'.well-known/openapi.json'
|
|
224
|
+
];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Detect provider from URL
|
|
229
|
+
*/
|
|
230
|
+
export function detectProvider(url: string): 'github' | 'gitlab' | 'bitbucket' | 'unknown' {
|
|
231
|
+
const lowerUrl = url.toLowerCase();
|
|
232
|
+
|
|
233
|
+
if (lowerUrl.includes('github.com') || lowerUrl.includes('github.')) {
|
|
234
|
+
return 'github';
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (lowerUrl.includes('gitlab.com') || lowerUrl.includes('gitlab.')) {
|
|
238
|
+
return 'gitlab';
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (lowerUrl.includes('bitbucket.org') || lowerUrl.includes('bitbucket.')) {
|
|
242
|
+
return 'bitbucket';
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return 'unknown';
|
|
246
|
+
}
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Bitbucket API client implementing BaseProvider
|
|
3
|
+
* @copyright Copyright (c) 2024-2026 nirholas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
AbstractProvider,
|
|
9
|
+
ProviderConfig,
|
|
10
|
+
ProviderRepoMetadata,
|
|
11
|
+
CodeSearchResult,
|
|
12
|
+
FileTreeItem,
|
|
13
|
+
SearchOptions,
|
|
14
|
+
RateLimitInfo
|
|
15
|
+
} from './base-provider';
|
|
16
|
+
import type { RepoMetadata, FileContent, ApiSpec } from '../types';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Bitbucket API response interfaces
|
|
20
|
+
*/
|
|
21
|
+
interface BitbucketRepository {
|
|
22
|
+
uuid: string;
|
|
23
|
+
name: string;
|
|
24
|
+
full_name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
language: string;
|
|
27
|
+
mainbranch?: {
|
|
28
|
+
name: string;
|
|
29
|
+
type: string;
|
|
30
|
+
};
|
|
31
|
+
created_on: string;
|
|
32
|
+
updated_on: string;
|
|
33
|
+
size: number;
|
|
34
|
+
is_private: boolean;
|
|
35
|
+
fork_policy: string;
|
|
36
|
+
links: {
|
|
37
|
+
watchers: { href: string };
|
|
38
|
+
forks: { href: string };
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface BitbucketFile {
|
|
43
|
+
path: string;
|
|
44
|
+
type: 'commit_file' | 'commit_directory';
|
|
45
|
+
size?: number;
|
|
46
|
+
commit: {
|
|
47
|
+
hash: string;
|
|
48
|
+
};
|
|
49
|
+
links: {
|
|
50
|
+
self: { href: string };
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface BitbucketTreeResponse {
|
|
55
|
+
values: BitbucketFile[];
|
|
56
|
+
pagelen: number;
|
|
57
|
+
page?: number;
|
|
58
|
+
next?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface BitbucketSearchResult {
|
|
62
|
+
type: string;
|
|
63
|
+
content_match_count: number;
|
|
64
|
+
path_matches: Array<{
|
|
65
|
+
text: string;
|
|
66
|
+
match: boolean;
|
|
67
|
+
}>;
|
|
68
|
+
file: {
|
|
69
|
+
path: string;
|
|
70
|
+
type: string;
|
|
71
|
+
links: {
|
|
72
|
+
self: { href: string };
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
content_matches?: Array<{
|
|
76
|
+
lines: Array<{
|
|
77
|
+
line: number;
|
|
78
|
+
segments: Array<{
|
|
79
|
+
text: string;
|
|
80
|
+
match: boolean;
|
|
81
|
+
}>;
|
|
82
|
+
}>;
|
|
83
|
+
}>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Bitbucket provider implementation
|
|
88
|
+
*/
|
|
89
|
+
export class BitbucketClient extends AbstractProvider {
|
|
90
|
+
readonly name = 'bitbucket';
|
|
91
|
+
private baseUrl: string;
|
|
92
|
+
|
|
93
|
+
constructor(config: ProviderConfig = {}) {
|
|
94
|
+
super(config);
|
|
95
|
+
this.baseUrl = config.baseUrl || 'https://api.bitbucket.org/2.0';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Parse Bitbucket URL to extract workspace and repo
|
|
100
|
+
*/
|
|
101
|
+
parseUrl(url: string): RepoMetadata {
|
|
102
|
+
// Format: bitbucket.org/workspace/repo
|
|
103
|
+
// With path: bitbucket.org/workspace/repo/src/branch/path
|
|
104
|
+
|
|
105
|
+
// Handle src/branch/path pattern
|
|
106
|
+
const srcMatch = url.match(/bitbucket\.org\/([^\/]+)\/([^\/]+)\/src\/([^\/]+)(?:\/(.*))?$/);
|
|
107
|
+
if (srcMatch) {
|
|
108
|
+
return {
|
|
109
|
+
owner: srcMatch[1],
|
|
110
|
+
repo: srcMatch[2],
|
|
111
|
+
branch: srcMatch[3],
|
|
112
|
+
path: srcMatch[4]
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Simple format: bitbucket.org/workspace/repo
|
|
117
|
+
const simpleMatch = url.match(/bitbucket\.org\/([^\/]+)\/([^\/]+)(?:\.git)?(?:\/|$|\?|#)/);
|
|
118
|
+
if (simpleMatch) {
|
|
119
|
+
return {
|
|
120
|
+
owner: simpleMatch[1],
|
|
121
|
+
repo: simpleMatch[2].replace(/\.git$/, ''),
|
|
122
|
+
branch: 'main'
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
throw new Error(`Invalid Bitbucket URL: ${url}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get authorization header for Bitbucket
|
|
131
|
+
* Bitbucket uses different auth format for app passwords
|
|
132
|
+
*/
|
|
133
|
+
protected override getAuthHeader(): Record<string, string> {
|
|
134
|
+
if (!this.config.token) {
|
|
135
|
+
return {};
|
|
136
|
+
}
|
|
137
|
+
// Check if it's a username:app_password format
|
|
138
|
+
if (this.config.token.includes(':')) {
|
|
139
|
+
const encoded = Buffer.from(this.config.token).toString('base64');
|
|
140
|
+
return { Authorization: `Basic ${encoded}` };
|
|
141
|
+
}
|
|
142
|
+
return { Authorization: `Bearer ${this.config.token}` };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get repository metadata
|
|
147
|
+
*/
|
|
148
|
+
async getRepoMetadata(owner: string, repo: string): Promise<ProviderRepoMetadata> {
|
|
149
|
+
const url = `${this.baseUrl}/repositories/${owner}/${repo}`;
|
|
150
|
+
|
|
151
|
+
const response = await this.fetchWithTimeout(url, {
|
|
152
|
+
headers: {
|
|
153
|
+
...this.getAuthHeader(),
|
|
154
|
+
'Content-Type': 'application/json'
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
throw new Error(`Bitbucket API error: ${response.status} ${response.statusText}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const repository = await response.json() as BitbucketRepository;
|
|
163
|
+
|
|
164
|
+
// Get watchers count (requires separate request)
|
|
165
|
+
let stars = 0;
|
|
166
|
+
try {
|
|
167
|
+
const watchersResponse = await this.fetchWithTimeout(repository.links.watchers.href, {
|
|
168
|
+
headers: this.getAuthHeader()
|
|
169
|
+
});
|
|
170
|
+
if (watchersResponse.ok) {
|
|
171
|
+
const watchersData = await watchersResponse.json();
|
|
172
|
+
stars = (watchersData as { size?: number }).size || 0;
|
|
173
|
+
}
|
|
174
|
+
} catch {
|
|
175
|
+
// Ignore watchers count error
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
stars,
|
|
180
|
+
language: repository.language || 'unknown',
|
|
181
|
+
description: repository.description || undefined,
|
|
182
|
+
defaultBranch: repository.mainbranch?.name || 'main',
|
|
183
|
+
createdAt: repository.created_on,
|
|
184
|
+
updatedAt: repository.updated_on,
|
|
185
|
+
size: repository.size
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get README content
|
|
191
|
+
*/
|
|
192
|
+
async getReadme(owner: string, repo: string, branch?: string): Promise<string | null> {
|
|
193
|
+
for (const filename of this.readmeFileNames) {
|
|
194
|
+
const file = await this.getFile(owner, repo, filename, branch);
|
|
195
|
+
if (file) {
|
|
196
|
+
return file.content;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get file content
|
|
204
|
+
*/
|
|
205
|
+
async getFile(owner: string, repo: string, path: string, branch?: string): Promise<FileContent | null> {
|
|
206
|
+
const ref = branch || 'main';
|
|
207
|
+
const url = `${this.baseUrl}/repositories/${owner}/${repo}/src/${ref}/${path}`;
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
const response = await this.fetchWithTimeout(url, {
|
|
211
|
+
headers: {
|
|
212
|
+
...this.getAuthHeader(),
|
|
213
|
+
'Accept': 'text/plain'
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (!response.ok) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const content = await response.text();
|
|
222
|
+
|
|
223
|
+
// Get file metadata
|
|
224
|
+
const metaUrl = `${this.baseUrl}/repositories/${owner}/${repo}/src/${ref}/${path}?format=meta`;
|
|
225
|
+
let sha = '';
|
|
226
|
+
try {
|
|
227
|
+
const metaResponse = await this.fetchWithTimeout(metaUrl, {
|
|
228
|
+
headers: this.getAuthHeader()
|
|
229
|
+
});
|
|
230
|
+
if (metaResponse.ok) {
|
|
231
|
+
const meta = await metaResponse.json();
|
|
232
|
+
sha = (meta as { commit?: { hash?: string } }).commit?.hash || '';
|
|
233
|
+
}
|
|
234
|
+
} catch {
|
|
235
|
+
// Ignore metadata error
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
path,
|
|
240
|
+
content,
|
|
241
|
+
type: 'file',
|
|
242
|
+
sha
|
|
243
|
+
};
|
|
244
|
+
} catch (error) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* List files in a directory
|
|
251
|
+
*/
|
|
252
|
+
async listFiles(owner: string, repo: string, path: string = '', branch?: string): Promise<FileTreeItem[]> {
|
|
253
|
+
const ref = branch || 'main';
|
|
254
|
+
let url = `${this.baseUrl}/repositories/${owner}/${repo}/src/${ref}/`;
|
|
255
|
+
if (path) {
|
|
256
|
+
url += `${path}/`;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
const response = await this.fetchWithTimeout(url, {
|
|
261
|
+
headers: {
|
|
262
|
+
...this.getAuthHeader(),
|
|
263
|
+
'Content-Type': 'application/json'
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
if (!response.ok) {
|
|
268
|
+
return [];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const data = await response.json() as BitbucketTreeResponse;
|
|
272
|
+
|
|
273
|
+
return data.values.map(item => ({
|
|
274
|
+
path: item.path,
|
|
275
|
+
type: item.type === 'commit_directory' ? 'dir' : 'file',
|
|
276
|
+
size: item.size,
|
|
277
|
+
sha: item.commit?.hash
|
|
278
|
+
}));
|
|
279
|
+
} catch (error) {
|
|
280
|
+
return [];
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Search for code in the repository
|
|
286
|
+
*/
|
|
287
|
+
async searchCode(owner: string, repo: string, query: string, options: SearchOptions = {}): Promise<CodeSearchResult[]> {
|
|
288
|
+
// Bitbucket Code Search API
|
|
289
|
+
const maxResults = options.maxResults || 20;
|
|
290
|
+
const url = `${this.baseUrl}/repositories/${owner}/${repo}/search/code?search_query=${encodeURIComponent(query)}&pagelen=${maxResults}`;
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
const response = await this.fetchWithTimeout(url, {
|
|
294
|
+
headers: {
|
|
295
|
+
...this.getAuthHeader(),
|
|
296
|
+
'Content-Type': 'application/json'
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
if (!response.ok) {
|
|
301
|
+
// Fallback to manual search if code search API is not available
|
|
302
|
+
return this.manualSearch(owner, repo, query, options);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const data = await response.json();
|
|
306
|
+
const results = (data as { values?: BitbucketSearchResult[] }).values || [];
|
|
307
|
+
|
|
308
|
+
return results
|
|
309
|
+
.filter(result => {
|
|
310
|
+
const filePath = result.file.path;
|
|
311
|
+
|
|
312
|
+
// Filter by extensions if specified
|
|
313
|
+
if (options.extensions && options.extensions.length > 0) {
|
|
314
|
+
const ext = filePath.split('.').pop() || '';
|
|
315
|
+
if (!options.extensions.includes(ext)) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Filter by paths if specified
|
|
321
|
+
if (options.paths && options.paths.length > 0) {
|
|
322
|
+
if (!options.paths.some(p => filePath.startsWith(p))) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return true;
|
|
328
|
+
})
|
|
329
|
+
.map(result => ({
|
|
330
|
+
path: result.file.path,
|
|
331
|
+
matchedLines: result.content_matches?.flatMap(match =>
|
|
332
|
+
match.lines.map(line => ({
|
|
333
|
+
lineNumber: line.line,
|
|
334
|
+
content: line.segments.map(s => s.text).join('')
|
|
335
|
+
}))
|
|
336
|
+
)
|
|
337
|
+
}));
|
|
338
|
+
} catch (error) {
|
|
339
|
+
return [];
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Manual search fallback (searches through files)
|
|
345
|
+
*/
|
|
346
|
+
private async manualSearch(owner: string, repo: string, query: string, options: SearchOptions = {}): Promise<CodeSearchResult[]> {
|
|
347
|
+
const results: CodeSearchResult[] = [];
|
|
348
|
+
const maxResults = options.maxResults || 20;
|
|
349
|
+
const searchRegex = new RegExp(query, 'gi');
|
|
350
|
+
|
|
351
|
+
const searchDir = async (path: string) => {
|
|
352
|
+
if (results.length >= maxResults) return;
|
|
353
|
+
|
|
354
|
+
const files = await this.listFiles(owner, repo, path, options.branch);
|
|
355
|
+
|
|
356
|
+
for (const file of files) {
|
|
357
|
+
if (results.length >= maxResults) break;
|
|
358
|
+
|
|
359
|
+
if (file.type === 'file') {
|
|
360
|
+
// Check extensions filter
|
|
361
|
+
if (options.extensions && options.extensions.length > 0) {
|
|
362
|
+
const ext = file.path.split('.').pop() || '';
|
|
363
|
+
if (!options.extensions.includes(ext)) continue;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Check paths filter
|
|
367
|
+
if (options.paths && options.paths.length > 0) {
|
|
368
|
+
if (!options.paths.some(p => file.path.startsWith(p))) continue;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const content = await this.getFile(owner, repo, file.path, options.branch);
|
|
372
|
+
if (content && searchRegex.test(content.content)) {
|
|
373
|
+
const lines = content.content.split('\n');
|
|
374
|
+
const matchedLines: Array<{ lineNumber: number; content: string }> = [];
|
|
375
|
+
|
|
376
|
+
lines.forEach((line, index) => {
|
|
377
|
+
if (searchRegex.test(line)) {
|
|
378
|
+
matchedLines.push({ lineNumber: index + 1, content: line });
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
results.push({
|
|
383
|
+
path: file.path,
|
|
384
|
+
content: content.content,
|
|
385
|
+
matchedLines
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
} else if (file.type === 'dir' && (!options.paths || options.paths.some(p => file.path.startsWith(p) || p.startsWith(file.path)))) {
|
|
389
|
+
await searchDir(file.path);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
await searchDir('');
|
|
395
|
+
return results.slice(0, maxResults);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Find API specification files
|
|
400
|
+
*/
|
|
401
|
+
async findApiSpecs(owner: string, repo: string, branch?: string): Promise<ApiSpec[]> {
|
|
402
|
+
const specs: ApiSpec[] = [];
|
|
403
|
+
|
|
404
|
+
for (const location of this.apiSpecLocations) {
|
|
405
|
+
const file = await this.getFile(owner, repo, location, branch);
|
|
406
|
+
if (file) {
|
|
407
|
+
try {
|
|
408
|
+
const spec = this.parseSpec(file.content, location);
|
|
409
|
+
if (spec) {
|
|
410
|
+
specs.push(spec);
|
|
411
|
+
}
|
|
412
|
+
} catch (error) {
|
|
413
|
+
// Not a valid spec, continue
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return specs;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Parse API spec content
|
|
423
|
+
*/
|
|
424
|
+
private parseSpec(content: string, path: string): ApiSpec | null {
|
|
425
|
+
try {
|
|
426
|
+
const spec = JSON.parse(content);
|
|
427
|
+
|
|
428
|
+
let type: 'openapi' | 'swagger' = 'openapi';
|
|
429
|
+
let version = '3.0.0';
|
|
430
|
+
|
|
431
|
+
if (spec.swagger) {
|
|
432
|
+
type = 'swagger';
|
|
433
|
+
version = spec.swagger;
|
|
434
|
+
} else if (spec.openapi) {
|
|
435
|
+
type = 'openapi';
|
|
436
|
+
version = spec.openapi;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return { type, version, spec, path };
|
|
440
|
+
} catch {
|
|
441
|
+
// Not JSON, might be YAML - would need yaml parser
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Get rate limit information
|
|
448
|
+
* Bitbucket rate limiting is different - mainly based on IP/user
|
|
449
|
+
*/
|
|
450
|
+
async getRateLimit(): Promise<RateLimitInfo> {
|
|
451
|
+
// Bitbucket doesn't have a dedicated rate limit endpoint
|
|
452
|
+
// Return estimated values based on their documentation
|
|
453
|
+
return {
|
|
454
|
+
remaining: 1000,
|
|
455
|
+
limit: 1000,
|
|
456
|
+
reset: new Date(Date.now() + 3600000) // 1 hour from now
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Export singleton factory
|
|
462
|
+
export function createBitbucketClient(config?: ProviderConfig): BitbucketClient {
|
|
463
|
+
return new BitbucketClient(config);
|
|
464
|
+
}
|