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,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Error Classes and Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides custom error classes for different API error scenarios
|
|
5
|
+
* and a unified error handler for converting errors to NextResponse.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { NextResponse } from 'next/server';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Base API Error class
|
|
12
|
+
*/
|
|
13
|
+
export abstract class ApiError extends Error {
|
|
14
|
+
abstract readonly statusCode: number;
|
|
15
|
+
abstract readonly code: string;
|
|
16
|
+
|
|
17
|
+
constructor(message: string) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = this.constructor.name;
|
|
20
|
+
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
|
21
|
+
if (Error.captureStackTrace) {
|
|
22
|
+
Error.captureStackTrace(this, this.constructor);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
toJSON() {
|
|
27
|
+
return {
|
|
28
|
+
error: this.code,
|
|
29
|
+
message: this.message,
|
|
30
|
+
statusCode: this.statusCode,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Validation Error - thrown when request validation fails
|
|
37
|
+
* Status Code: 400 Bad Request
|
|
38
|
+
*/
|
|
39
|
+
export class ValidationError extends ApiError {
|
|
40
|
+
readonly statusCode = 400;
|
|
41
|
+
readonly code = 'VALIDATION_ERROR';
|
|
42
|
+
readonly field?: string;
|
|
43
|
+
|
|
44
|
+
constructor(message: string, field?: string) {
|
|
45
|
+
super(message);
|
|
46
|
+
this.field = field;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
toJSON() {
|
|
50
|
+
return {
|
|
51
|
+
...super.toJSON(),
|
|
52
|
+
...(this.field && { field: this.field }),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Not Found Error - thrown when a requested resource doesn't exist
|
|
59
|
+
* Status Code: 404 Not Found
|
|
60
|
+
*/
|
|
61
|
+
export class NotFoundError extends ApiError {
|
|
62
|
+
readonly statusCode = 404;
|
|
63
|
+
readonly code = 'NOT_FOUND';
|
|
64
|
+
readonly resource?: string;
|
|
65
|
+
|
|
66
|
+
constructor(message: string, resource?: string) {
|
|
67
|
+
super(message);
|
|
68
|
+
this.resource = resource;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
toJSON() {
|
|
72
|
+
return {
|
|
73
|
+
...super.toJSON(),
|
|
74
|
+
...(this.resource && { resource: this.resource }),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* MCP Connection Error - thrown when connection to MCP server fails
|
|
81
|
+
* Status Code: 502 Bad Gateway
|
|
82
|
+
*/
|
|
83
|
+
export class McpConnectionError extends ApiError {
|
|
84
|
+
readonly statusCode = 502;
|
|
85
|
+
readonly code = 'MCP_CONNECTION_ERROR';
|
|
86
|
+
readonly transportType?: string;
|
|
87
|
+
readonly cause?: string;
|
|
88
|
+
|
|
89
|
+
constructor(message: string, options?: { transportType?: string; cause?: string }) {
|
|
90
|
+
super(message);
|
|
91
|
+
this.transportType = options?.transportType;
|
|
92
|
+
this.cause = options?.cause;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
toJSON() {
|
|
96
|
+
return {
|
|
97
|
+
...super.toJSON(),
|
|
98
|
+
...(this.transportType && { transportType: this.transportType }),
|
|
99
|
+
...(this.cause && { cause: this.cause }),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* MCP Execution Error - thrown when tool/resource/prompt execution fails
|
|
106
|
+
* Status Code: 422 Unprocessable Entity
|
|
107
|
+
*/
|
|
108
|
+
export class McpExecutionError extends ApiError {
|
|
109
|
+
readonly statusCode = 422;
|
|
110
|
+
readonly code = 'MCP_EXECUTION_ERROR';
|
|
111
|
+
readonly executionType?: 'tool' | 'resource' | 'prompt';
|
|
112
|
+
readonly entityName?: string;
|
|
113
|
+
|
|
114
|
+
constructor(
|
|
115
|
+
message: string,
|
|
116
|
+
options?: { executionType?: 'tool' | 'resource' | 'prompt'; name?: string }
|
|
117
|
+
) {
|
|
118
|
+
super(message);
|
|
119
|
+
this.executionType = options?.executionType;
|
|
120
|
+
this.entityName = options?.name;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
toJSON() {
|
|
124
|
+
return {
|
|
125
|
+
...super.toJSON(),
|
|
126
|
+
...(this.executionType && { executionType: this.executionType }),
|
|
127
|
+
...(this.entityName && { name: this.entityName }),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Session Expired Error - thrown when a session has expired or is invalid
|
|
134
|
+
* Status Code: 410 Gone
|
|
135
|
+
*/
|
|
136
|
+
export class SessionExpiredError extends ApiError {
|
|
137
|
+
readonly statusCode = 410;
|
|
138
|
+
readonly code = 'SESSION_EXPIRED';
|
|
139
|
+
readonly sessionId?: string;
|
|
140
|
+
|
|
141
|
+
constructor(message: string, sessionId?: string) {
|
|
142
|
+
super(message);
|
|
143
|
+
this.sessionId = sessionId;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
toJSON() {
|
|
147
|
+
return {
|
|
148
|
+
...super.toJSON(),
|
|
149
|
+
...(this.sessionId && { sessionId: this.sessionId }),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Rate Limit Error - thrown when rate limit is exceeded
|
|
156
|
+
* Status Code: 429 Too Many Requests
|
|
157
|
+
*/
|
|
158
|
+
export class RateLimitError extends ApiError {
|
|
159
|
+
readonly statusCode = 429;
|
|
160
|
+
readonly code = 'RATE_LIMIT_EXCEEDED';
|
|
161
|
+
readonly retryAfter?: number;
|
|
162
|
+
|
|
163
|
+
constructor(message: string, retryAfter?: number) {
|
|
164
|
+
super(message);
|
|
165
|
+
this.retryAfter = retryAfter;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
toJSON() {
|
|
169
|
+
return {
|
|
170
|
+
...super.toJSON(),
|
|
171
|
+
...(this.retryAfter && { retryAfter: this.retryAfter }),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Internal Server Error - thrown for unexpected errors
|
|
178
|
+
* Status Code: 500 Internal Server Error
|
|
179
|
+
*/
|
|
180
|
+
export class InternalServerError extends ApiError {
|
|
181
|
+
readonly statusCode = 500;
|
|
182
|
+
readonly code = 'INTERNAL_SERVER_ERROR';
|
|
183
|
+
|
|
184
|
+
constructor(message = 'An unexpected error occurred') {
|
|
185
|
+
super(message);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Converts any error to an appropriate NextResponse
|
|
191
|
+
*/
|
|
192
|
+
export function handleApiError(error: unknown): NextResponse {
|
|
193
|
+
// Log the error for debugging
|
|
194
|
+
console.error('[API Error]', error);
|
|
195
|
+
|
|
196
|
+
// Handle known API errors
|
|
197
|
+
if (error instanceof ApiError) {
|
|
198
|
+
return NextResponse.json(
|
|
199
|
+
{
|
|
200
|
+
success: false,
|
|
201
|
+
error: error.toJSON(),
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
status: error.statusCode,
|
|
205
|
+
headers: getErrorHeaders(error),
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Handle standard Error objects
|
|
211
|
+
if (error instanceof Error) {
|
|
212
|
+
// Check for specific error types by name or message
|
|
213
|
+
if (
|
|
214
|
+
error.name === 'AbortError' ||
|
|
215
|
+
error.message.includes('aborted')
|
|
216
|
+
) {
|
|
217
|
+
return NextResponse.json(
|
|
218
|
+
{
|
|
219
|
+
success: false,
|
|
220
|
+
error: {
|
|
221
|
+
code: 'REQUEST_ABORTED',
|
|
222
|
+
message: 'Request was aborted',
|
|
223
|
+
statusCode: 499,
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
{ status: 499 }
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (
|
|
231
|
+
error.name === 'TimeoutError' ||
|
|
232
|
+
error.message.includes('timeout')
|
|
233
|
+
) {
|
|
234
|
+
return NextResponse.json(
|
|
235
|
+
{
|
|
236
|
+
success: false,
|
|
237
|
+
error: {
|
|
238
|
+
code: 'TIMEOUT',
|
|
239
|
+
message: 'Request timed out',
|
|
240
|
+
statusCode: 504,
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
{ status: 504 }
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Generic error response - don't expose internal details in production
|
|
248
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
249
|
+
return NextResponse.json(
|
|
250
|
+
{
|
|
251
|
+
success: false,
|
|
252
|
+
error: {
|
|
253
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
254
|
+
message: isDev ? error.message : 'An unexpected error occurred',
|
|
255
|
+
statusCode: 500,
|
|
256
|
+
...(isDev && { stack: error.stack }),
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
{ status: 500 }
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Handle unknown error types
|
|
264
|
+
return NextResponse.json(
|
|
265
|
+
{
|
|
266
|
+
success: false,
|
|
267
|
+
error: {
|
|
268
|
+
code: 'UNKNOWN_ERROR',
|
|
269
|
+
message: 'An unknown error occurred',
|
|
270
|
+
statusCode: 500,
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
{ status: 500 }
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Get additional headers for specific error types
|
|
279
|
+
*/
|
|
280
|
+
function getErrorHeaders(error: ApiError): HeadersInit {
|
|
281
|
+
const headers: HeadersInit = {};
|
|
282
|
+
|
|
283
|
+
if (error instanceof RateLimitError && error.retryAfter) {
|
|
284
|
+
headers['Retry-After'] = String(error.retryAfter);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return headers;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Creates a standardized success response
|
|
292
|
+
*/
|
|
293
|
+
export function createSuccessResponse<T>(
|
|
294
|
+
data: T,
|
|
295
|
+
status = 200,
|
|
296
|
+
headers?: HeadersInit
|
|
297
|
+
): NextResponse {
|
|
298
|
+
return NextResponse.json(
|
|
299
|
+
{
|
|
300
|
+
success: true,
|
|
301
|
+
data,
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
status,
|
|
305
|
+
headers,
|
|
306
|
+
}
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Creates a standardized error response
|
|
312
|
+
*/
|
|
313
|
+
export function createErrorResponse(
|
|
314
|
+
code: string,
|
|
315
|
+
message: string,
|
|
316
|
+
status: number,
|
|
317
|
+
additionalData?: Record<string, unknown>
|
|
318
|
+
): NextResponse {
|
|
319
|
+
return NextResponse.json(
|
|
320
|
+
{
|
|
321
|
+
success: false,
|
|
322
|
+
error: {
|
|
323
|
+
code,
|
|
324
|
+
message,
|
|
325
|
+
statusCode: status,
|
|
326
|
+
...additionalData,
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
{ status }
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Wraps an async handler with error handling
|
|
335
|
+
*/
|
|
336
|
+
export function withErrorHandling<T extends unknown[]>(
|
|
337
|
+
handler: (...args: T) => Promise<NextResponse>
|
|
338
|
+
): (...args: T) => Promise<NextResponse> {
|
|
339
|
+
return async (...args: T): Promise<NextResponse> => {
|
|
340
|
+
try {
|
|
341
|
+
return await handler(...args);
|
|
342
|
+
} catch (error) {
|
|
343
|
+
return handleApiError(error);
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Type guard to check if an error is an ApiError
|
|
350
|
+
*/
|
|
351
|
+
export function isApiError(error: unknown): error is ApiError {
|
|
352
|
+
return error instanceof ApiError;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Type guard to check if an error is a specific ApiError type
|
|
357
|
+
*/
|
|
358
|
+
export function isValidationError(error: unknown): error is ValidationError {
|
|
359
|
+
return error instanceof ValidationError;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function isNotFoundError(error: unknown): error is NotFoundError {
|
|
363
|
+
return error instanceof NotFoundError;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function isMcpConnectionError(
|
|
367
|
+
error: unknown
|
|
368
|
+
): error is McpConnectionError {
|
|
369
|
+
return error instanceof McpConnectionError;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export function isMcpExecutionError(
|
|
373
|
+
error: unknown
|
|
374
|
+
): error is McpExecutionError {
|
|
375
|
+
return error instanceof McpExecutionError;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export function isSessionExpiredError(
|
|
379
|
+
error: unknown
|
|
380
|
+
): error is SessionExpiredError {
|
|
381
|
+
return error instanceof SessionExpiredError;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export function isRateLimitError(error: unknown): error is RateLimitError {
|
|
385
|
+
return error instanceof RateLimitError;
|
|
386
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Library Index
|
|
3
|
+
*
|
|
4
|
+
* Exports all API utilities for the playground API.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Error handling
|
|
8
|
+
export {
|
|
9
|
+
ApiError,
|
|
10
|
+
ValidationError,
|
|
11
|
+
NotFoundError,
|
|
12
|
+
McpConnectionError,
|
|
13
|
+
McpExecutionError,
|
|
14
|
+
SessionExpiredError,
|
|
15
|
+
RateLimitError,
|
|
16
|
+
InternalServerError,
|
|
17
|
+
handleApiError,
|
|
18
|
+
createSuccessResponse,
|
|
19
|
+
createErrorResponse,
|
|
20
|
+
withErrorHandling,
|
|
21
|
+
isApiError,
|
|
22
|
+
isValidationError,
|
|
23
|
+
isNotFoundError,
|
|
24
|
+
isMcpConnectionError,
|
|
25
|
+
isMcpExecutionError,
|
|
26
|
+
isSessionExpiredError,
|
|
27
|
+
isRateLimitError,
|
|
28
|
+
} from './errors';
|
|
29
|
+
|
|
30
|
+
// Validation
|
|
31
|
+
export {
|
|
32
|
+
validateTransportConfig,
|
|
33
|
+
validateToolCall,
|
|
34
|
+
validateResourceRead,
|
|
35
|
+
validatePromptGet,
|
|
36
|
+
validateSessionId,
|
|
37
|
+
validateSessionIdFromQuery,
|
|
38
|
+
validateConnectRequest,
|
|
39
|
+
sanitizeCode,
|
|
40
|
+
} from './validation';
|
|
41
|
+
export type {
|
|
42
|
+
TransportType,
|
|
43
|
+
StdioTransportConfig,
|
|
44
|
+
SseTransportConfig,
|
|
45
|
+
HttpTransportConfig,
|
|
46
|
+
TransportConfig,
|
|
47
|
+
} from './validation';
|
|
48
|
+
|
|
49
|
+
// Middleware
|
|
50
|
+
export {
|
|
51
|
+
getRequestId,
|
|
52
|
+
withRequestId,
|
|
53
|
+
checkRateLimit,
|
|
54
|
+
rateLimitResponse,
|
|
55
|
+
getClientId,
|
|
56
|
+
corsHeaders,
|
|
57
|
+
corsPreflightResponse,
|
|
58
|
+
parseJsonBody,
|
|
59
|
+
applyMiddleware,
|
|
60
|
+
BodyTooLargeError,
|
|
61
|
+
InvalidJsonError,
|
|
62
|
+
DEFAULT_RATE_LIMIT,
|
|
63
|
+
RELAXED_RATE_LIMIT,
|
|
64
|
+
STRICT_RATE_LIMIT,
|
|
65
|
+
NO_RATE_LIMIT,
|
|
66
|
+
DEFAULT_CORS,
|
|
67
|
+
} from './middleware';
|
|
68
|
+
export type {
|
|
69
|
+
RateLimitConfig,
|
|
70
|
+
CorsConfig,
|
|
71
|
+
BodyParseOptions,
|
|
72
|
+
ApiMiddlewareConfig,
|
|
73
|
+
ApiContext,
|
|
74
|
+
} from './middleware';
|
|
75
|
+
|
|
76
|
+
// Logger
|
|
77
|
+
export {
|
|
78
|
+
logger,
|
|
79
|
+
createLogContext,
|
|
80
|
+
timed,
|
|
81
|
+
} from './logger';
|
|
82
|
+
export type {
|
|
83
|
+
RequestLogEntry,
|
|
84
|
+
LogLevel,
|
|
85
|
+
} from './logger';
|
|
86
|
+
|
|
87
|
+
// Session Manager
|
|
88
|
+
export { SessionManager } from './session-manager';
|
|
89
|
+
export type {
|
|
90
|
+
CreateSessionResult,
|
|
91
|
+
ToolExecutionResult,
|
|
92
|
+
PromptResult,
|
|
93
|
+
} from './session-manager';
|
|
94
|
+
|
|
95
|
+
// Types
|
|
96
|
+
export type {
|
|
97
|
+
ApiResponse,
|
|
98
|
+
ApiErrorData,
|
|
99
|
+
McpCapabilities,
|
|
100
|
+
McpServerInfo,
|
|
101
|
+
McpTool,
|
|
102
|
+
McpToolPropertySchema,
|
|
103
|
+
McpResource,
|
|
104
|
+
McpResourceTemplate,
|
|
105
|
+
McpResourceContents,
|
|
106
|
+
McpPrompt,
|
|
107
|
+
McpPromptArgument,
|
|
108
|
+
McpPromptMessage,
|
|
109
|
+
McpPromptContent,
|
|
110
|
+
McpToolResultContent,
|
|
111
|
+
McpToolResult,
|
|
112
|
+
SessionInfo,
|
|
113
|
+
RateLimitHeaders,
|
|
114
|
+
ApiRequestLog,
|
|
115
|
+
// Response types
|
|
116
|
+
ConnectResponseData,
|
|
117
|
+
DisconnectResponseData,
|
|
118
|
+
ToolsListResponseData,
|
|
119
|
+
ToolCallResponseData,
|
|
120
|
+
ResourcesListResponseData,
|
|
121
|
+
ResourceReadResponseData,
|
|
122
|
+
PromptsListResponseData,
|
|
123
|
+
PromptGetResponseData,
|
|
124
|
+
SessionsListResponseData,
|
|
125
|
+
SessionsDeleteResponseData,
|
|
126
|
+
HealthResponseData,
|
|
127
|
+
// Request types
|
|
128
|
+
ConnectRequestBody,
|
|
129
|
+
ToolCallRequestBody,
|
|
130
|
+
ResourceReadRequestBody,
|
|
131
|
+
PromptGetRequestBody,
|
|
132
|
+
DisconnectRequestBody,
|
|
133
|
+
SessionsDeleteRequestBody,
|
|
134
|
+
} from './types';
|
|
135
|
+
|
|
136
|
+
// OpenAPI
|
|
137
|
+
export { openApiSpec, getOpenApiJson, getOpenApiYaml } from './openapi';
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Request Logger
|
|
3
|
+
*
|
|
4
|
+
* Structured logging for API requests with timing and context.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface RequestLogEntry {
|
|
8
|
+
timestamp: string;
|
|
9
|
+
requestId: string;
|
|
10
|
+
method: string;
|
|
11
|
+
path: string;
|
|
12
|
+
duration: number;
|
|
13
|
+
status: number;
|
|
14
|
+
clientId?: string;
|
|
15
|
+
sessionId?: string;
|
|
16
|
+
error?: string;
|
|
17
|
+
metadata?: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
21
|
+
|
|
22
|
+
interface LoggerConfig {
|
|
23
|
+
/** Minimum log level to output */
|
|
24
|
+
minLevel?: LogLevel;
|
|
25
|
+
/** Whether to pretty-print JSON */
|
|
26
|
+
pretty?: boolean;
|
|
27
|
+
/** Custom log handler */
|
|
28
|
+
handler?: (entry: RequestLogEntry, level: LogLevel) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const LOG_LEVELS: Record<LogLevel, number> = {
|
|
32
|
+
debug: 0,
|
|
33
|
+
info: 1,
|
|
34
|
+
warn: 2,
|
|
35
|
+
error: 3,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
class ApiLogger {
|
|
39
|
+
private config: LoggerConfig;
|
|
40
|
+
|
|
41
|
+
constructor(config: LoggerConfig = {}) {
|
|
42
|
+
this.config = {
|
|
43
|
+
minLevel: process.env.NODE_ENV === 'development' ? 'debug' : 'info',
|
|
44
|
+
pretty: process.env.NODE_ENV === 'development',
|
|
45
|
+
...config,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private shouldLog(level: LogLevel): boolean {
|
|
50
|
+
const minLevelValue = LOG_LEVELS[this.config.minLevel || 'info'];
|
|
51
|
+
const levelValue = LOG_LEVELS[level];
|
|
52
|
+
return levelValue >= minLevelValue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private formatEntry(entry: RequestLogEntry): string {
|
|
56
|
+
if (this.config.pretty) {
|
|
57
|
+
return JSON.stringify(entry, null, 2);
|
|
58
|
+
}
|
|
59
|
+
return JSON.stringify(entry);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private log(entry: RequestLogEntry, level: LogLevel): void {
|
|
63
|
+
if (!this.shouldLog(level)) return;
|
|
64
|
+
|
|
65
|
+
if (this.config.handler) {
|
|
66
|
+
this.config.handler(entry, level);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const formatted = this.formatEntry(entry);
|
|
71
|
+
|
|
72
|
+
switch (level) {
|
|
73
|
+
case 'debug':
|
|
74
|
+
console.debug(formatted);
|
|
75
|
+
break;
|
|
76
|
+
case 'info':
|
|
77
|
+
console.info(formatted);
|
|
78
|
+
break;
|
|
79
|
+
case 'warn':
|
|
80
|
+
console.warn(formatted);
|
|
81
|
+
break;
|
|
82
|
+
case 'error':
|
|
83
|
+
console.error(formatted);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Log a successful request
|
|
90
|
+
*/
|
|
91
|
+
success(entry: Omit<RequestLogEntry, 'timestamp'>): void {
|
|
92
|
+
this.log(
|
|
93
|
+
{
|
|
94
|
+
...entry,
|
|
95
|
+
timestamp: new Date().toISOString(),
|
|
96
|
+
},
|
|
97
|
+
entry.status >= 400 ? 'warn' : 'info'
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Log a failed request
|
|
103
|
+
*/
|
|
104
|
+
error(entry: Omit<RequestLogEntry, 'timestamp'>): void {
|
|
105
|
+
this.log(
|
|
106
|
+
{
|
|
107
|
+
...entry,
|
|
108
|
+
timestamp: new Date().toISOString(),
|
|
109
|
+
},
|
|
110
|
+
'error'
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Log a debug message
|
|
116
|
+
*/
|
|
117
|
+
debug(entry: Omit<RequestLogEntry, 'timestamp'>): void {
|
|
118
|
+
this.log(
|
|
119
|
+
{
|
|
120
|
+
...entry,
|
|
121
|
+
timestamp: new Date().toISOString(),
|
|
122
|
+
},
|
|
123
|
+
'debug'
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Singleton logger instance
|
|
129
|
+
export const logger = new ApiLogger();
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Creates a request context for logging
|
|
133
|
+
*/
|
|
134
|
+
export function createLogContext(
|
|
135
|
+
method: string,
|
|
136
|
+
path: string,
|
|
137
|
+
requestId: string,
|
|
138
|
+
clientId?: string
|
|
139
|
+
): {
|
|
140
|
+
startTime: number;
|
|
141
|
+
log: (status: number, sessionId?: string, error?: string, metadata?: Record<string, unknown>) => void;
|
|
142
|
+
} {
|
|
143
|
+
const startTime = Date.now();
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
startTime,
|
|
147
|
+
log: (status: number, sessionId?: string, error?: string, metadata?: Record<string, unknown>) => {
|
|
148
|
+
const duration = Date.now() - startTime;
|
|
149
|
+
const entry: Omit<RequestLogEntry, 'timestamp'> = {
|
|
150
|
+
requestId,
|
|
151
|
+
method,
|
|
152
|
+
path,
|
|
153
|
+
duration,
|
|
154
|
+
status,
|
|
155
|
+
clientId,
|
|
156
|
+
sessionId,
|
|
157
|
+
error,
|
|
158
|
+
metadata,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
if (error || status >= 500) {
|
|
162
|
+
logger.error(entry);
|
|
163
|
+
} else {
|
|
164
|
+
logger.success(entry);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Decorator for timing async functions
|
|
172
|
+
*/
|
|
173
|
+
export async function timed<T>(
|
|
174
|
+
name: string,
|
|
175
|
+
fn: () => Promise<T>,
|
|
176
|
+
onComplete?: (duration: number) => void
|
|
177
|
+
): Promise<T> {
|
|
178
|
+
const start = Date.now();
|
|
179
|
+
try {
|
|
180
|
+
return await fn();
|
|
181
|
+
} finally {
|
|
182
|
+
const duration = Date.now() - start;
|
|
183
|
+
if (onComplete) {
|
|
184
|
+
onComplete(duration);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|