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,578 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Redis-based queue implementation for distributed processing
|
|
3
|
+
* @copyright Copyright (c) 2024-2026 nirholas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
QueueInterface,
|
|
9
|
+
Job,
|
|
10
|
+
JobStatus,
|
|
11
|
+
JobOptions,
|
|
12
|
+
QueueConfig,
|
|
13
|
+
JobHandler,
|
|
14
|
+
QueueStats
|
|
15
|
+
} from './types';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Redis client interface (compatible with ioredis and node-redis)
|
|
19
|
+
*/
|
|
20
|
+
export interface RedisClientInterface {
|
|
21
|
+
get(key: string): Promise<string | null>;
|
|
22
|
+
set(key: string, value: string, ...args: any[]): Promise<any>;
|
|
23
|
+
del(key: string | string[]): Promise<number>;
|
|
24
|
+
keys(pattern: string): Promise<string[]>;
|
|
25
|
+
lpush(key: string, ...values: string[]): Promise<number>;
|
|
26
|
+
rpop(key: string): Promise<string | null>;
|
|
27
|
+
brpop(key: string, timeout: number): Promise<[string, string] | null>;
|
|
28
|
+
llen(key: string): Promise<number>;
|
|
29
|
+
lrange(key: string, start: number, stop: number): Promise<string[]>;
|
|
30
|
+
lrem(key: string, count: number, value: string): Promise<number>;
|
|
31
|
+
zadd(key: string, ...args: (string | number)[]): Promise<number>;
|
|
32
|
+
zrange(key: string, start: number, stop: number): Promise<string[]>;
|
|
33
|
+
zrangebyscore(key: string, min: string | number, max: string | number): Promise<string[]>;
|
|
34
|
+
zrem(key: string, ...members: string[]): Promise<number>;
|
|
35
|
+
zcard(key: string): Promise<number>;
|
|
36
|
+
hset(key: string, field: string, value: string): Promise<number>;
|
|
37
|
+
hget(key: string, field: string): Promise<string | null>;
|
|
38
|
+
hdel(key: string, ...fields: string[]): Promise<number>;
|
|
39
|
+
hlen(key: string): Promise<number>;
|
|
40
|
+
hgetall(key: string): Promise<Record<string, string>>;
|
|
41
|
+
quit(): Promise<any>;
|
|
42
|
+
publish?(channel: string, message: string): Promise<number>;
|
|
43
|
+
subscribe?(channel: string, callback: (message: string) => void): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Redis queue configuration
|
|
48
|
+
*/
|
|
49
|
+
export interface RedisQueueConfig extends QueueConfig {
|
|
50
|
+
/** Redis client instance */
|
|
51
|
+
client: RedisClientInterface;
|
|
52
|
+
/** Queue name (used as Redis key prefix) */
|
|
53
|
+
queueName?: string;
|
|
54
|
+
/** Use blocking pop for job consumption */
|
|
55
|
+
useBlocking?: boolean;
|
|
56
|
+
/** Blocking timeout in seconds */
|
|
57
|
+
blockingTimeout?: number;
|
|
58
|
+
/** Enable distributed locking */
|
|
59
|
+
enableLocking?: boolean;
|
|
60
|
+
/** Lock TTL in milliseconds */
|
|
61
|
+
lockTTL?: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Redis-based queue implementation
|
|
66
|
+
* Supports distributed processing across multiple workers
|
|
67
|
+
*/
|
|
68
|
+
export class RedisQueue<T = unknown> implements QueueInterface<T> {
|
|
69
|
+
private client: RedisClientInterface;
|
|
70
|
+
private config: RedisQueueConfig;
|
|
71
|
+
private handlers: Map<string, JobHandler<T>> = new Map();
|
|
72
|
+
private onCompleteCallbacks: Array<(job: Job<T>) => void> = [];
|
|
73
|
+
private onFailCallbacks: Array<(job: Job<T>, error: Error) => void> = [];
|
|
74
|
+
private isProcessing = false;
|
|
75
|
+
private shouldStop = false;
|
|
76
|
+
private jobCounter = 0;
|
|
77
|
+
|
|
78
|
+
// Redis key prefixes
|
|
79
|
+
private keys: {
|
|
80
|
+
pending: string;
|
|
81
|
+
delayed: string;
|
|
82
|
+
processing: string;
|
|
83
|
+
completed: string;
|
|
84
|
+
failed: string;
|
|
85
|
+
jobs: string;
|
|
86
|
+
locks: string;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
constructor(config: RedisQueueConfig) {
|
|
90
|
+
this.client = config.client;
|
|
91
|
+
this.config = {
|
|
92
|
+
queueName: 'mcp-queue',
|
|
93
|
+
maxConcurrency: 5,
|
|
94
|
+
defaultPriority: 0,
|
|
95
|
+
maxRetries: 3,
|
|
96
|
+
retryDelay: 1000,
|
|
97
|
+
jobTimeout: 60000,
|
|
98
|
+
useBlocking: true,
|
|
99
|
+
blockingTimeout: 5,
|
|
100
|
+
enableLocking: true,
|
|
101
|
+
lockTTL: 30000,
|
|
102
|
+
...config
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const prefix = this.config.queueName!;
|
|
106
|
+
this.keys = {
|
|
107
|
+
pending: `${prefix}:pending`,
|
|
108
|
+
delayed: `${prefix}:delayed`,
|
|
109
|
+
processing: `${prefix}:processing`,
|
|
110
|
+
completed: `${prefix}:completed`,
|
|
111
|
+
failed: `${prefix}:failed`,
|
|
112
|
+
jobs: `${prefix}:jobs`,
|
|
113
|
+
locks: `${prefix}:locks`
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Generate unique job ID
|
|
119
|
+
*/
|
|
120
|
+
private generateJobId(): string {
|
|
121
|
+
this.jobCounter++;
|
|
122
|
+
const workerId = process.pid || Math.random().toString(36).substring(2, 6);
|
|
123
|
+
return `job_${Date.now()}_${workerId}_${this.jobCounter}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Serialize job for storage
|
|
128
|
+
*/
|
|
129
|
+
private serializeJob(job: Job<T>): string {
|
|
130
|
+
return JSON.stringify({
|
|
131
|
+
...job,
|
|
132
|
+
createdAt: job.createdAt?.toISOString(),
|
|
133
|
+
updatedAt: job.updatedAt?.toISOString(),
|
|
134
|
+
startedAt: job.startedAt?.toISOString(),
|
|
135
|
+
completedAt: job.completedAt?.toISOString(),
|
|
136
|
+
failedAt: job.failedAt?.toISOString()
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Deserialize job from storage
|
|
142
|
+
*/
|
|
143
|
+
private deserializeJob(data: string): Job<T> {
|
|
144
|
+
const parsed = JSON.parse(data);
|
|
145
|
+
return {
|
|
146
|
+
...parsed,
|
|
147
|
+
createdAt: parsed.createdAt ? new Date(parsed.createdAt) : undefined,
|
|
148
|
+
updatedAt: parsed.updatedAt ? new Date(parsed.updatedAt) : undefined,
|
|
149
|
+
startedAt: parsed.startedAt ? new Date(parsed.startedAt) : undefined,
|
|
150
|
+
completedAt: parsed.completedAt ? new Date(parsed.completedAt) : undefined,
|
|
151
|
+
failedAt: parsed.failedAt ? new Date(parsed.failedAt) : undefined
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Enqueue a new job
|
|
157
|
+
*/
|
|
158
|
+
async enqueue(data: T, options: JobOptions = {}): Promise<Job<T>> {
|
|
159
|
+
const job: Job<T> = {
|
|
160
|
+
id: this.generateJobId(),
|
|
161
|
+
data,
|
|
162
|
+
status: 'pending',
|
|
163
|
+
priority: options.priority ?? this.config.defaultPriority ?? 0,
|
|
164
|
+
attempts: 0,
|
|
165
|
+
maxRetries: options.maxRetries ?? this.config.maxRetries ?? 3,
|
|
166
|
+
createdAt: new Date(),
|
|
167
|
+
updatedAt: new Date(),
|
|
168
|
+
delay: options.delay,
|
|
169
|
+
timeout: options.timeout ?? this.config.jobTimeout,
|
|
170
|
+
tags: options.tags,
|
|
171
|
+
metadata: options.metadata
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Store job data
|
|
175
|
+
await this.client.hset(this.keys.jobs, job.id, this.serializeJob(job));
|
|
176
|
+
|
|
177
|
+
if (job.delay && job.delay > 0) {
|
|
178
|
+
// Add to delayed set with score = timestamp when it should run
|
|
179
|
+
const runAt = Date.now() + job.delay;
|
|
180
|
+
await this.client.zadd(this.keys.delayed, runAt, job.id);
|
|
181
|
+
} else {
|
|
182
|
+
// Add to pending queue with priority
|
|
183
|
+
await this.addToPendingQueue(job);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return job;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Add job to pending queue with priority
|
|
191
|
+
*/
|
|
192
|
+
private async addToPendingQueue(job: Job<T>): Promise<void> {
|
|
193
|
+
// Use sorted set for priority ordering (higher priority = higher score)
|
|
194
|
+
const score = (job.priority || 0) * 1000000000 + (1000000000 - Date.now() % 1000000000);
|
|
195
|
+
await this.client.zadd(this.keys.pending, score, job.id);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Move delayed jobs to pending queue
|
|
200
|
+
*/
|
|
201
|
+
private async processDelayedJobs(): Promise<void> {
|
|
202
|
+
const now = Date.now();
|
|
203
|
+
const jobIds = await this.client.zrangebyscore(this.keys.delayed, '-inf', now);
|
|
204
|
+
|
|
205
|
+
for (const jobId of jobIds) {
|
|
206
|
+
const jobData = await this.client.hget(this.keys.jobs, jobId);
|
|
207
|
+
if (jobData) {
|
|
208
|
+
const job = this.deserializeJob(jobData);
|
|
209
|
+
await this.client.zrem(this.keys.delayed, jobId);
|
|
210
|
+
await this.addToPendingQueue(job);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Dequeue the next job
|
|
217
|
+
*/
|
|
218
|
+
async dequeue(): Promise<Job<T> | null> {
|
|
219
|
+
// Process any delayed jobs first
|
|
220
|
+
await this.processDelayedJobs();
|
|
221
|
+
|
|
222
|
+
// Get highest priority job
|
|
223
|
+
const jobIds = await this.client.zrange(this.keys.pending, -1, -1);
|
|
224
|
+
if (jobIds.length === 0) return null;
|
|
225
|
+
|
|
226
|
+
const jobId = jobIds[0];
|
|
227
|
+
|
|
228
|
+
// Remove from pending
|
|
229
|
+
const removed = await this.client.zrem(this.keys.pending, jobId);
|
|
230
|
+
if (removed === 0) return null; // Another worker got it
|
|
231
|
+
|
|
232
|
+
// Get job data
|
|
233
|
+
const jobData = await this.client.hget(this.keys.jobs, jobId);
|
|
234
|
+
if (!jobData) return null;
|
|
235
|
+
|
|
236
|
+
const job = this.deserializeJob(jobData);
|
|
237
|
+
job.status = 'processing';
|
|
238
|
+
job.startedAt = new Date();
|
|
239
|
+
job.updatedAt = new Date();
|
|
240
|
+
job.attempts++;
|
|
241
|
+
|
|
242
|
+
// Add to processing set
|
|
243
|
+
await this.client.hset(this.keys.processing, jobId, this.serializeJob(job));
|
|
244
|
+
await this.client.hset(this.keys.jobs, jobId, this.serializeJob(job));
|
|
245
|
+
|
|
246
|
+
return job;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Get job status
|
|
251
|
+
*/
|
|
252
|
+
async getStatus(jobId: string): Promise<JobStatus | null> {
|
|
253
|
+
const jobData = await this.client.hget(this.keys.jobs, jobId);
|
|
254
|
+
if (!jobData) return null;
|
|
255
|
+
const job = this.deserializeJob(jobData);
|
|
256
|
+
return job.status;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Get job by ID
|
|
261
|
+
*/
|
|
262
|
+
async getJob(jobId: string): Promise<Job<T> | null> {
|
|
263
|
+
const jobData = await this.client.hget(this.keys.jobs, jobId);
|
|
264
|
+
if (!jobData) return null;
|
|
265
|
+
return this.deserializeJob(jobData);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Register job completion callback
|
|
270
|
+
*/
|
|
271
|
+
onComplete(callback: (job: Job<T>) => void): void {
|
|
272
|
+
this.onCompleteCallbacks.push(callback);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Register job failure callback
|
|
277
|
+
*/
|
|
278
|
+
onFail(callback: (job: Job<T>, error: Error) => void): void {
|
|
279
|
+
this.onFailCallbacks.push(callback);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Register a job handler
|
|
284
|
+
*/
|
|
285
|
+
registerHandler(name: string, handler: JobHandler<T>): void {
|
|
286
|
+
this.handlers.set(name, handler);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Process jobs with the default handler
|
|
291
|
+
*/
|
|
292
|
+
async process(handler: JobHandler<T>): Promise<void> {
|
|
293
|
+
this.handlers.set('default', handler);
|
|
294
|
+
this.shouldStop = false;
|
|
295
|
+
this.processQueue();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Internal queue processing loop
|
|
300
|
+
*/
|
|
301
|
+
private async processQueue(): Promise<void> {
|
|
302
|
+
if (this.isProcessing) return;
|
|
303
|
+
this.isProcessing = true;
|
|
304
|
+
|
|
305
|
+
const maxConcurrency = this.config.maxConcurrency || 5;
|
|
306
|
+
const activeJobs: Promise<void>[] = [];
|
|
307
|
+
|
|
308
|
+
while (!this.shouldStop) {
|
|
309
|
+
// Wait for a slot if at max concurrency
|
|
310
|
+
while (activeJobs.length >= maxConcurrency) {
|
|
311
|
+
await Promise.race(activeJobs);
|
|
312
|
+
// Remove completed promises
|
|
313
|
+
for (let i = activeJobs.length - 1; i >= 0; i--) {
|
|
314
|
+
const job = activeJobs[i];
|
|
315
|
+
if (await Promise.race([job.then(() => true), Promise.resolve(false)])) {
|
|
316
|
+
activeJobs.splice(i, 1);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const job = await this.dequeue();
|
|
322
|
+
if (!job) {
|
|
323
|
+
// No jobs, wait a bit
|
|
324
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const jobPromise = this.processJob(job);
|
|
329
|
+
activeJobs.push(jobPromise);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Wait for all active jobs to complete
|
|
333
|
+
await Promise.all(activeJobs);
|
|
334
|
+
this.isProcessing = false;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Process a single job
|
|
339
|
+
*/
|
|
340
|
+
private async processJob(job: Job<T>): Promise<void> {
|
|
341
|
+
const handler = this.handlers.get('default');
|
|
342
|
+
if (!handler) {
|
|
343
|
+
console.warn('No handler registered for job processing');
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Set up timeout
|
|
348
|
+
let timeoutId: NodeJS.Timeout | undefined;
|
|
349
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
350
|
+
if (job.timeout) {
|
|
351
|
+
timeoutId = setTimeout(() => {
|
|
352
|
+
reject(new Error(`Job ${job.id} timed out after ${job.timeout}ms`));
|
|
353
|
+
}, job.timeout);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
try {
|
|
358
|
+
const result = await Promise.race([
|
|
359
|
+
handler(job),
|
|
360
|
+
timeoutPromise
|
|
361
|
+
]);
|
|
362
|
+
|
|
363
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
364
|
+
|
|
365
|
+
// Job completed successfully
|
|
366
|
+
job.status = 'completed';
|
|
367
|
+
job.result = result;
|
|
368
|
+
job.completedAt = new Date();
|
|
369
|
+
job.updatedAt = new Date();
|
|
370
|
+
|
|
371
|
+
// Move to completed
|
|
372
|
+
await this.client.hdel(this.keys.processing, job.id);
|
|
373
|
+
await this.client.zadd(this.keys.completed, Date.now(), job.id);
|
|
374
|
+
await this.client.hset(this.keys.jobs, job.id, this.serializeJob(job));
|
|
375
|
+
|
|
376
|
+
// Notify callbacks
|
|
377
|
+
this.onCompleteCallbacks.forEach(cb => {
|
|
378
|
+
try {
|
|
379
|
+
cb(job);
|
|
380
|
+
} catch (err) {
|
|
381
|
+
console.error('Error in onComplete callback:', err);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
} catch (error) {
|
|
386
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
387
|
+
|
|
388
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
389
|
+
job.error = err.message;
|
|
390
|
+
job.updatedAt = new Date();
|
|
391
|
+
|
|
392
|
+
await this.client.hdel(this.keys.processing, job.id);
|
|
393
|
+
|
|
394
|
+
// Check if we should retry
|
|
395
|
+
if (job.attempts < (job.maxRetries || 0)) {
|
|
396
|
+
job.status = 'pending';
|
|
397
|
+
const retryDelay = this.config.retryDelay || 1000;
|
|
398
|
+
const backoffDelay = retryDelay * Math.pow(2, job.attempts - 1);
|
|
399
|
+
|
|
400
|
+
// Add to delayed queue for retry
|
|
401
|
+
const runAt = Date.now() + backoffDelay;
|
|
402
|
+
await this.client.zadd(this.keys.delayed, runAt, job.id);
|
|
403
|
+
await this.client.hset(this.keys.jobs, job.id, this.serializeJob(job));
|
|
404
|
+
|
|
405
|
+
} else {
|
|
406
|
+
// Mark as failed
|
|
407
|
+
job.status = 'failed';
|
|
408
|
+
job.failedAt = new Date();
|
|
409
|
+
await this.client.zadd(this.keys.failed, Date.now(), job.id);
|
|
410
|
+
await this.client.hset(this.keys.jobs, job.id, this.serializeJob(job));
|
|
411
|
+
|
|
412
|
+
// Notify callbacks
|
|
413
|
+
this.onFailCallbacks.forEach(cb => {
|
|
414
|
+
try {
|
|
415
|
+
cb(job, err);
|
|
416
|
+
} catch (callbackErr) {
|
|
417
|
+
console.error('Error in onFail callback:', callbackErr);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Cancel a job
|
|
426
|
+
*/
|
|
427
|
+
async cancel(jobId: string): Promise<boolean> {
|
|
428
|
+
const job = await this.getJob(jobId);
|
|
429
|
+
if (!job) return false;
|
|
430
|
+
|
|
431
|
+
if (job.status === 'pending') {
|
|
432
|
+
job.status = 'cancelled';
|
|
433
|
+
job.updatedAt = new Date();
|
|
434
|
+
await this.client.zrem(this.keys.pending, jobId);
|
|
435
|
+
await this.client.hset(this.keys.jobs, jobId, this.serializeJob(job));
|
|
436
|
+
return true;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Retry a failed job
|
|
444
|
+
*/
|
|
445
|
+
async retry(jobId: string): Promise<Job<T> | null> {
|
|
446
|
+
const job = await this.getJob(jobId);
|
|
447
|
+
if (!job || job.status !== 'failed') return null;
|
|
448
|
+
|
|
449
|
+
job.status = 'pending';
|
|
450
|
+
job.attempts = 0;
|
|
451
|
+
job.error = undefined;
|
|
452
|
+
job.failedAt = undefined;
|
|
453
|
+
job.updatedAt = new Date();
|
|
454
|
+
|
|
455
|
+
await this.client.zrem(this.keys.failed, jobId);
|
|
456
|
+
await this.addToPendingQueue(job);
|
|
457
|
+
await this.client.hset(this.keys.jobs, jobId, this.serializeJob(job));
|
|
458
|
+
|
|
459
|
+
return job;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Get queue statistics
|
|
464
|
+
*/
|
|
465
|
+
async getStats(): Promise<QueueStats> {
|
|
466
|
+
const [pending, processing, completed, failed] = await Promise.all([
|
|
467
|
+
this.client.zcard(this.keys.pending),
|
|
468
|
+
this.client.hlen(this.keys.processing),
|
|
469
|
+
this.client.zcard(this.keys.completed),
|
|
470
|
+
this.client.zcard(this.keys.failed)
|
|
471
|
+
]);
|
|
472
|
+
|
|
473
|
+
return {
|
|
474
|
+
pending,
|
|
475
|
+
processing,
|
|
476
|
+
completed,
|
|
477
|
+
failed,
|
|
478
|
+
total: pending + processing + completed + failed
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Get jobs by status
|
|
484
|
+
*/
|
|
485
|
+
async getJobsByStatus(status: JobStatus, limit: number = 100): Promise<Job<T>[]> {
|
|
486
|
+
let jobIds: string[] = [];
|
|
487
|
+
|
|
488
|
+
switch (status) {
|
|
489
|
+
case 'pending':
|
|
490
|
+
jobIds = await this.client.zrange(this.keys.pending, -limit, -1);
|
|
491
|
+
break;
|
|
492
|
+
case 'processing':
|
|
493
|
+
const processingData = await this.client.hgetall(this.keys.processing);
|
|
494
|
+
jobIds = Object.keys(processingData).slice(0, limit);
|
|
495
|
+
break;
|
|
496
|
+
case 'completed':
|
|
497
|
+
jobIds = await this.client.zrange(this.keys.completed, -limit, -1);
|
|
498
|
+
break;
|
|
499
|
+
case 'failed':
|
|
500
|
+
jobIds = await this.client.zrange(this.keys.failed, -limit, -1);
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const jobs: Job<T>[] = [];
|
|
505
|
+
for (const jobId of jobIds) {
|
|
506
|
+
const job = await this.getJob(jobId);
|
|
507
|
+
if (job) jobs.push(job);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return jobs;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Clear completed jobs
|
|
515
|
+
*/
|
|
516
|
+
async clearCompleted(): Promise<number> {
|
|
517
|
+
const jobIds = await this.client.zrange(this.keys.completed, 0, -1);
|
|
518
|
+
if (jobIds.length === 0) return 0;
|
|
519
|
+
|
|
520
|
+
await this.client.del(this.keys.completed);
|
|
521
|
+
for (const jobId of jobIds) {
|
|
522
|
+
await this.client.hdel(this.keys.jobs, jobId);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return jobIds.length;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Clear failed jobs
|
|
530
|
+
*/
|
|
531
|
+
async clearFailed(): Promise<number> {
|
|
532
|
+
const jobIds = await this.client.zrange(this.keys.failed, 0, -1);
|
|
533
|
+
if (jobIds.length === 0) return 0;
|
|
534
|
+
|
|
535
|
+
await this.client.del(this.keys.failed);
|
|
536
|
+
for (const jobId of jobIds) {
|
|
537
|
+
await this.client.hdel(this.keys.jobs, jobId);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return jobIds.length;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Pause the queue
|
|
545
|
+
*/
|
|
546
|
+
async pause(): Promise<void> {
|
|
547
|
+
this.shouldStop = true;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Resume the queue
|
|
552
|
+
*/
|
|
553
|
+
async resume(): Promise<void> {
|
|
554
|
+
this.shouldStop = false;
|
|
555
|
+
if (!this.isProcessing && this.handlers.has('default')) {
|
|
556
|
+
this.processQueue();
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Close the queue
|
|
562
|
+
*/
|
|
563
|
+
async close(): Promise<void> {
|
|
564
|
+
this.shouldStop = true;
|
|
565
|
+
// Wait for processing to complete
|
|
566
|
+
while (this.isProcessing) {
|
|
567
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
568
|
+
}
|
|
569
|
+
await this.client.quit();
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Create a Redis queue instance
|
|
575
|
+
*/
|
|
576
|
+
export function createRedisQueue<T = unknown>(config: RedisQueueConfig): RedisQueue<T> {
|
|
577
|
+
return new RedisQueue<T>(config);
|
|
578
|
+
}
|