claude-flow-novice 2.15.3 → 2.15.5
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/.claude/cfn-extras/skills/advanced-features/cfn-agent-swap/recommend-swap.sh +59 -59
- package/.claude/cfn-extras/skills/analytics/cfn-improvement-recommender/recommend-improvements.sh +91 -91
- package/.claude/cfn-extras/skills/analytics/cfn-pattern-extraction/extract-patterns.sh +79 -79
- package/.claude/cfn-extras/skills/analytics/cfn-retrospective-report/generate-report.sh +100 -100
- package/.claude/cfn-extras/skills/analytics/cfn-telemetry/start-telemetry.sh +110 -110
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/add-bullet.sh +145 -145
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/log-merge.sh +67 -67
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/monitor-injection-performance.sh +137 -137
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/optimize-injection-pipeline.sh +168 -168
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/query-reflections.sh +35 -35
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/store-reflection.sh +45 -45
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/track-ab-test.sh +41 -41
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/update-reflection.sh +41 -41
- package/.claude/cfn-extras/skills/deprecated/cfn-cli-setup/validate-cli-environment.sh +191 -191
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/create-campaign.sh +231 -231
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/get-campaign-performance.sh +190 -190
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/pause-campaign.sh +142 -142
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/set-budget.sh +181 -181
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/update-bid-strategy.sh +133 -133
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/get-conversation-history.sh +121 -121
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/qualify-lead.sh +156 -156
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/schedule-demo.sh +181 -181
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/send-message.sh +137 -137
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/transfer-to-human.sh +179 -179
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/create-campaign.sh +183 -183
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/get-delivery-status.sh +139 -139
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/opt-out.sh +150 -150
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/schedule-campaign.sh +187 -187
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/send-sms.sh +181 -181
- package/.claude/cfn-extras/skills/ui-portal/cfn-web-portal/test-web-portal-skill.sh +50 -50
- package/.claude/cfn-extras/skills/ui-portal/cfn-web-portal/validate-deployment.sh +84 -84
- package/.claude/cfn-extras/skills/utility/cfn-environment-sanitization/sanitize-environment.sh +243 -243
- package/.claude/commands/cfn-loop-cli.md +29 -6
- package/.claude/commands/switch-api.md +31 -10
- package/.claude/hooks/cfn-lint-sql-injection.sh +61 -0
- package/.claude/hooks/cfn-post-edit-cfn-retrospective.sh +33 -2
- package/.claude/hooks/cfn-pre-edit-security-warning.sh +40 -0
- package/.claude/skills/cfn-agent-spawning/spawn-agent.sh +22 -24
- package/.claude/skills/cfn-docker-agent-spawning/SKILL.md +28 -4
- package/.claude/skills/cfn-docker-agent-spawning/spawn-agent.sh +3 -1
- package/.claude/skills/cfn-docker-loop-orchestration/orchestrate.sh +224 -20
- package/.claude/skills/cfn-loop-orchestration/helpers/gate-check.sh +550 -46
- package/.claude/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +277 -0
- package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +238 -29
- package/.claude/skills/cfn-loop-orchestration/security_utils.sh +24 -0
- package/.claude/skills/cfn-loop-orchestration/test-iteration-context-injection.sh +366 -0
- package/.claude/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +319 -0
- package/.claude/skills/cfn-redis-coordination/agent-log.sh +4 -0
- package/.claude/skills/cfn-redis-coordination/agent-log.sh.bak +124 -0
- package/.claude/skills/cfn-redis-coordination/agent-recovery.sh +2 -2
- package/.claude/skills/cfn-redis-coordination/collect-confidence-scores.sh +30 -0
- package/.claude/skills/cfn-redis-coordination/get-context.sh +33 -0
- package/.claude/skills/cfn-redis-coordination/get-success-criteria.sh +54 -0
- package/.claude/skills/cfn-redis-coordination/invoke-waiting-mode.sh +6 -2
- package/.claude/skills/cfn-redis-coordination/redis-cli-wrapper.sh +24 -3
- package/.claude/skills/cfn-redis-coordination/redis-functions.sh +34 -0
- package/.claude/skills/cfn-redis-coordination/report-completion.sh +24 -31
- package/.claude/skills/cfn-redis-coordination/store-context.sh +4 -0
- package/.claude/skills/cfn-redis-coordination/store-success-criteria.sh +85 -0
- package/.claude/skills/cfn-redis-coordination/update-all-scripts.sh +67 -0
- package/.claude/skills/cfn-sqlite-memory/ttl-cleanup.sh +17 -25
- package/.claude/skills/cfn-transparency-middleware/test-e2e.sh +15 -0
- package/.claude/skills/cfn-transparency-middleware/tests/input-validation.sh +15 -0
- package/README.md +116 -475
- package/claude-assets/agents/cfn-dev-team/README.md +103 -0
- package/claude-assets/agents/cfn-dev-team/architecture/goal-planner.md +1 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +77 -15
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +355 -6
- package/claude-assets/agents/cfn-dev-team/coordinators/consensus-builder.md +82 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/handoff-coordinator.md +82 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/multi-sprint-coordinator.md +77 -15
- package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +99 -12
- package/claude-assets/agents/cfn-dev-team/dev-ops/github-commit-agent.md +1 -1
- package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +97 -0
- package/claude-assets/agents/cfn-dev-team/dev-ops/monitoring-specialist.md +20 -1
- package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +97 -0
- package/claude-assets/agents/cfn-dev-team/developers/backend-developer.md +110 -13
- package/claude-assets/agents/cfn-dev-team/developers/data/data-engineer.md +106 -15
- package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +115 -11
- package/claude-assets/agents/cfn-dev-team/developers/frontend/mobile-dev.md +94 -7
- package/claude-assets/agents/cfn-dev-team/developers/frontend/react-frontend-engineer.md +87 -9
- package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +85 -7
- package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +160 -28
- package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +101 -19
- package/claude-assets/agents/cfn-dev-team/developers/rust-developer.md +108 -14
- package/claude-assets/agents/cfn-dev-team/reviewers/{reviewer.md → code-reviewer.md} +95 -8
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/code-quality-validator.md +107 -7
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/perf-analyzer.md +98 -7
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/performance-benchmarker.md +95 -7
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +136 -9
- package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +108 -1
- package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +107 -13
- package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +737 -0
- package/claude-assets/agents/cfn-dev-team/testers/e2e/playwright-tester.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +828 -0
- package/claude-assets/agents/cfn-dev-team/testers/interaction-tester.md +106 -7
- package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +77 -0
- package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +684 -0
- package/claude-assets/agents/cfn-dev-team/testers/playwright-tester.md +110 -1
- package/claude-assets/agents/cfn-dev-team/testers/tester.md +94 -7
- package/claude-assets/agents/cfn-dev-team/utility/code-booster.md +1 -3
- package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +87 -13
- package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +103 -7
- package/claude-assets/agents/cfn-dev-team/utility/researcher.md +1 -3
- package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +94 -7
- package/claude-assets/agents/docker-coordinators/cfn-docker-v3-coordinator.md +46 -0
- package/claude-assets/agents/project-only-agents/npm-package-specialist.md +1 -1
- package/claude-assets/cfn-extras/skills/advanced-features/cfn-agent-swap/recommend-swap.sh +59 -59
- package/claude-assets/cfn-extras/skills/analytics/cfn-improvement-recommender/recommend-improvements.sh +91 -91
- package/claude-assets/cfn-extras/skills/analytics/cfn-pattern-extraction/extract-patterns.sh +79 -79
- package/claude-assets/cfn-extras/skills/analytics/cfn-retrospective-report/generate-report.sh +100 -100
- package/claude-assets/cfn-extras/skills/analytics/cfn-telemetry/start-telemetry.sh +110 -110
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/add-bullet.sh +145 -145
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/log-merge.sh +67 -67
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/monitor-injection-performance.sh +137 -137
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/optimize-injection-pipeline.sh +168 -168
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/query-reflections.sh +35 -35
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/store-reflection.sh +45 -45
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/track-ab-test.sh +41 -41
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/update-reflection.sh +41 -41
- package/claude-assets/cfn-extras/skills/deprecated/cfn-cli-setup/validate-cli-environment.sh +191 -191
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/create-campaign.sh +231 -231
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/get-campaign-performance.sh +190 -190
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/pause-campaign.sh +142 -142
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/set-budget.sh +181 -181
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/update-bid-strategy.sh +133 -133
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/get-conversation-history.sh +121 -121
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/qualify-lead.sh +156 -156
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/schedule-demo.sh +181 -181
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/send-message.sh +137 -137
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/transfer-to-human.sh +179 -179
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/create-campaign.sh +183 -183
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/get-delivery-status.sh +139 -139
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/opt-out.sh +150 -150
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/schedule-campaign.sh +187 -187
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/send-sms.sh +181 -181
- package/claude-assets/cfn-extras/skills/ui-portal/cfn-web-portal/test-web-portal-skill.sh +50 -50
- package/claude-assets/cfn-extras/skills/ui-portal/cfn-web-portal/validate-deployment.sh +84 -84
- package/claude-assets/cfn-extras/skills/utility/cfn-environment-sanitization/sanitize-environment.sh +243 -243
- package/claude-assets/commands/cfn-loop-cli.md +29 -6
- package/claude-assets/commands/switch-api.md +31 -10
- package/claude-assets/hooks/cfn-lint-sql-injection.sh +61 -0
- package/claude-assets/hooks/cfn-post-edit-cfn-retrospective.sh +33 -2
- package/claude-assets/hooks/cfn-pre-edit-security-warning.sh +40 -0
- package/claude-assets/hooks/detect-hardcoded-credentials.sh +212 -0
- package/claude-assets/skills/SKILL_TEMPLATE.md +774 -0
- package/claude-assets/skills/agent-lifecycle/execute-lifecycle-hook.sh +84 -113
- package/claude-assets/skills/agent-lifecycle/simple-audit.sh +33 -6
- package/claude-assets/skills/agent-template-generator/SKILL.md +440 -0
- package/claude-assets/skills/agent-template-generator/generate-agent.sh +405 -0
- package/claude-assets/skills/agent-validation-linter/SKILL.md +589 -0
- package/claude-assets/skills/agent-validation-linter/lint-agents.sh +271 -0
- package/claude-assets/skills/bootstrap/bash-fundamentals.md +786 -0
- package/claude-assets/skills/bootstrap/database-connection.md +464 -0
- package/claude-assets/skills/bootstrap/error-handling.md +580 -0
- package/claude-assets/skills/bootstrap/file-operations.md +699 -0
- package/claude-assets/skills/bootstrap/skill-loader.md +616 -0
- package/claude-assets/skills/bootstrap/sqlite-params.sh +287 -0
- package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh +22 -24
- package/claude-assets/skills/cfn-automatic-memory-persistence/test-memory-persistence.sh +17 -16
- package/claude-assets/skills/cfn-deployment/SKILL.md +293 -0
- package/claude-assets/skills/cfn-deployment/execute.sh +21 -0
- package/claude-assets/skills/cfn-docker-agent-spawning/SKILL.md +28 -4
- package/claude-assets/skills/cfn-docker-agent-spawning/spawn-agent.sh +3 -1
- package/claude-assets/skills/cfn-docker-loop-orchestration/orchestrate.sh +224 -20
- package/claude-assets/skills/cfn-environment-sanitization/sanitize-environment.sh +38 -0
- package/claude-assets/skills/cfn-error-batching-strategy/lib/core-functions.sh +47 -47
- package/claude-assets/skills/cfn-file-operations/SKILL.md +290 -0
- package/claude-assets/skills/cfn-file-operations/execute.sh +129 -0
- package/claude-assets/skills/cfn-file-operations/lib/atomic-write.sh +294 -0
- package/claude-assets/skills/cfn-file-operations/lib/lock.sh +361 -0
- package/claude-assets/skills/cfn-file-operations/test.sh +369 -0
- package/claude-assets/skills/cfn-log-operations/SKILL.md +308 -0
- package/claude-assets/skills/cfn-log-operations/execute.sh +420 -0
- package/claude-assets/skills/cfn-log-operations/lib/rotate.sh +406 -0
- package/claude-assets/skills/cfn-log-operations/lib/search.sh +448 -0
- package/claude-assets/skills/cfn-log-operations/test.sh +394 -0
- package/claude-assets/skills/cfn-loop-orchestration/helpers/gate-check.sh +550 -46
- package/claude-assets/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +277 -0
- package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh +238 -29
- package/claude-assets/skills/cfn-loop-orchestration/security_utils.sh +24 -0
- package/claude-assets/skills/cfn-loop-orchestration/test-iteration-context-injection.sh +366 -0
- package/claude-assets/skills/cfn-parameterized-queries/SKILL.md +339 -0
- package/claude-assets/skills/cfn-playbook/query-playbook.sh +19 -15
- package/claude-assets/skills/cfn-playbook/update-playbook.sh +25 -14
- package/claude-assets/skills/cfn-process-instrumentation/instrument-process.sh +44 -0
- package/claude-assets/skills/cfn-promotion/SKILL.md +305 -0
- package/claude-assets/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +319 -0
- package/claude-assets/skills/cfn-redis-coordination/agent-log.sh +4 -0
- package/claude-assets/skills/cfn-redis-coordination/agent-log.sh.bak +124 -0
- package/claude-assets/skills/cfn-redis-coordination/agent-recovery.sh +2 -2
- package/claude-assets/skills/cfn-redis-coordination/collect-confidence-scores.sh +30 -0
- package/claude-assets/skills/cfn-redis-coordination/get-context.sh +33 -0
- package/claude-assets/skills/cfn-redis-coordination/get-success-criteria.sh +54 -0
- package/claude-assets/skills/cfn-redis-coordination/invoke-waiting-mode.sh +6 -2
- package/claude-assets/skills/cfn-redis-coordination/redis-cli-wrapper.sh +24 -3
- package/claude-assets/skills/cfn-redis-coordination/redis-functions.sh +34 -0
- package/claude-assets/skills/cfn-redis-coordination/report-completion.sh +24 -31
- package/claude-assets/skills/cfn-redis-coordination/store-context.sh +4 -0
- package/claude-assets/skills/cfn-redis-coordination/store-success-criteria.sh +85 -0
- package/claude-assets/skills/cfn-redis-coordination/update-all-scripts.sh +67 -0
- package/claude-assets/skills/cfn-skill-loader/SKILL.md +466 -0
- package/claude-assets/skills/cfn-skill-loader/execute.sh +344 -0
- package/claude-assets/skills/cfn-sqlite-memory/ttl-cleanup.sh +17 -25
- package/claude-assets/skills/cfn-task-audit/get-audit-data.sh +42 -21
- package/claude-assets/skills/cfn-task-audit/store-task-audit.sh +17 -10
- package/claude-assets/skills/cfn-test-runner/detect-regressions.sh +17 -14
- package/claude-assets/skills/cfn-test-runner/detect-regressions.sh.backup-1763392821 +55 -0
- package/claude-assets/skills/cfn-test-runner/store-benchmarks.sh +17 -19
- package/claude-assets/skills/cfn-transparency-middleware/test-e2e.sh +15 -0
- package/claude-assets/skills/cfn-transparency-middleware/tests/input-validation.sh +15 -0
- package/claude-assets/skills/cfn-utilities/SKILL.md +237 -0
- package/claude-assets/skills/cfn-utilities/execute.sh +32 -0
- package/claude-assets/skills/cfn-utilities/lib/errors.sh +56 -0
- package/claude-assets/skills/cfn-utilities/lib/file-ops.sh +164 -0
- package/claude-assets/skills/cfn-utilities/lib/logging.sh +77 -0
- package/claude-assets/skills/cfn-utilities/lib/retry.sh +127 -0
- package/claude-assets/skills/cfn-utilities/test.sh +317 -0
- package/claude-assets/skills/integration/agent-handoff.sh +62 -64
- package/claude-assets/skills/json-validation/SKILL.md +431 -0
- package/claude-assets/skills/json-validation/test-validate-success-criteria.sh +421 -0
- package/claude-assets/skills/json-validation/validate-success-criteria.sh +197 -0
- package/claude-assets/skills/redis-coordination/validate-parameters.sh +34 -0
- package/claude-assets/skills/workflow-codification/DEPLOY_QUICK_REFERENCE.md +106 -0
- package/claude-assets/skills/workflow-codification/PROPAGATE_UPDATE_QUICK_REFERENCE.md +366 -0
- package/claude-assets/skills/workflow-codification/deploy-approved-skill.sh +481 -0
- package/claude-assets/skills/workflow-codification/deploy-approved-skill.sh.backup-1763392820 +512 -0
- package/claude-assets/skills/workflow-codification/lib/security-utils.sh +204 -0
- package/claude-assets/skills/workflow-codification/propagate-skill-update.sh +648 -0
- package/claude-assets/skills/workflow-codification/propagate-skill-update.sh.backup-1763392820 +664 -0
- package/claude-assets/skills/workflow-codification/test-integration.sh +15 -0
- package/claude-assets/skills/workflow-codification/test-metadata-update.sh +350 -0
- package/claude-assets/skills/workflow-codification/track-cost-savings.sh +55 -14
- package/claude-assets/skills/workflow-codification/track-cost-savings.sh.backup-1763392821 +445 -0
- package/claude-assets/skills/workflow-codification/track-edge-case.sh +27 -60
- package/claude-assets/skills/workflow-codification/workflow-codification.db +0 -0
- package/dist/ace/ace-curator.js +10 -2
- package/dist/ace/ace-curator.js.map +1 -1
- package/dist/ace/ace-generator.js +4 -0
- package/dist/ace/ace-generator.js.map +1 -1
- package/dist/ace/ace-reflector.js +1 -1
- package/dist/ace/ace-reflector.js.map +1 -1
- package/dist/ace/context-injection.js +24 -2
- package/dist/ace/context-injection.js.map +1 -1
- package/dist/agents/task-agent-integration.js +1 -1
- package/dist/agents/task-agent-integration.js.map +1 -1
- package/dist/api/health-endpoints.js +390 -0
- package/dist/api/health-endpoints.js.map +1 -0
- package/dist/cli/agent-executor.js +4 -1
- package/dist/cli/agent-executor.js.map +1 -1
- package/dist/cli/agent-prompt-builder.js +89 -1
- package/dist/cli/agent-prompt-builder.js.map +1 -1
- package/dist/cli/agent-spawn.js +130 -37
- package/dist/cli/agent-spawn.js.map +1 -1
- package/dist/cli/config-manager.js +109 -91
- package/dist/cli/config-manager.js.map +1 -1
- package/dist/cli/conversation-fork-cleanup.js +201 -0
- package/dist/cli/conversation-fork-cleanup.js.map +1 -0
- package/dist/cli/conversation-fork.js +16 -3
- package/dist/cli/conversation-fork.js.map +1 -1
- package/dist/cli/skill-cache-validator.js +412 -0
- package/dist/cli/skill-cache-validator.js.map +1 -0
- package/dist/cli/skill-cli.js +991 -0
- package/dist/cli/skill-cli.js.map +1 -0
- package/dist/cli/skill-execution-logger.js +284 -0
- package/dist/cli/skill-execution-logger.js.map +1 -0
- package/dist/cli/skill-loader.js +457 -0
- package/dist/cli/skill-loader.js.map +1 -0
- package/dist/coordination/event-bus.js +2 -2
- package/dist/coordination/event-bus.js.map +1 -1
- package/dist/coordination/fleet-manager.js +1 -1
- package/dist/coordination/fleet-manager.js.map +1 -1
- package/dist/coordination/index.js +23 -9
- package/dist/coordination/index.js.map +1 -1
- package/dist/coordination/types/fleet-manager.types.js.map +1 -1
- package/dist/db/migration-manager.js +483 -0
- package/dist/db/migration-manager.js.map +1 -0
- package/dist/db/skills-query.js +535 -0
- package/dist/db/skills-query.js.map +1 -0
- package/dist/integration/DatabaseHandoff.js +1 -1
- package/dist/integration/DatabaseHandoff.js.map +1 -1
- package/dist/jobs/edge-case-analyzer.js +367 -0
- package/dist/jobs/edge-case-analyzer.js.map +1 -0
- package/dist/jobs/promotion-sla-enforcer.js +288 -0
- package/dist/jobs/promotion-sla-enforcer.js.map +1 -0
- package/dist/lib/agent-output-parser.js.map +1 -1
- package/dist/lib/agent-output-validator.js.map +1 -1
- package/dist/lib/agent-workspace.js +281 -0
- package/dist/lib/agent-workspace.js.map +1 -0
- package/dist/lib/atomic-file-writer.js +377 -0
- package/dist/lib/atomic-file-writer.js.map +1 -0
- package/dist/lib/backup-manager.js +779 -0
- package/dist/lib/backup-manager.js.map +1 -0
- package/dist/lib/checkpoint-manager.js +837 -0
- package/dist/lib/checkpoint-manager.js.map +1 -0
- package/dist/lib/circuit-breaker.js +340 -0
- package/dist/lib/circuit-breaker.js.map +1 -0
- package/dist/lib/completion-signal-handler.js +243 -0
- package/dist/lib/completion-signal-handler.js.map +1 -0
- package/dist/lib/config-manager.js +312 -0
- package/dist/lib/config-manager.js.map +1 -0
- package/dist/lib/config-migrator.js +386 -0
- package/dist/lib/config-migrator.js.map +1 -0
- package/dist/lib/config-validator.js.map +1 -1
- package/dist/lib/correlation-cache.js +311 -0
- package/dist/lib/correlation-cache.js.map +1 -0
- package/dist/lib/correlation.js +263 -0
- package/dist/lib/correlation.js.map +1 -0
- package/dist/lib/database-service/connection-pool-manager.js +520 -0
- package/dist/lib/database-service/connection-pool-manager.js.map +1 -0
- package/dist/lib/database-service/correlation.js +329 -0
- package/dist/lib/database-service/correlation.js.map +1 -0
- package/dist/lib/database-service/errors.js +120 -0
- package/dist/lib/database-service/errors.js.map +1 -0
- package/dist/lib/database-service/index.js +168 -0
- package/dist/lib/database-service/index.js.map +1 -0
- package/dist/lib/database-service/postgres-adapter.js +526 -0
- package/dist/lib/database-service/postgres-adapter.js.map +1 -0
- package/dist/lib/database-service/redis-adapter.js +360 -0
- package/dist/lib/database-service/redis-adapter.js.map +1 -0
- package/dist/lib/database-service/sqlite-adapter.js +544 -0
- package/dist/lib/database-service/sqlite-adapter.js.map +1 -0
- package/dist/lib/database-service/transaction-manager.js +773 -0
- package/dist/lib/database-service/transaction-manager.js.map +1 -0
- package/dist/lib/database-service/types.js +23 -0
- package/dist/lib/database-service/types.js.map +1 -0
- package/dist/lib/deadlock-resolver.js +292 -0
- package/dist/lib/deadlock-resolver.js.map +1 -0
- package/dist/lib/distributed-lock.js +451 -0
- package/dist/lib/distributed-lock.js.map +1 -0
- package/dist/lib/edge-case-deduplicator.js +227 -0
- package/dist/lib/edge-case-deduplicator.js.map +1 -0
- package/dist/lib/encryption-manager.js +322 -0
- package/dist/lib/encryption-manager.js.map +1 -0
- package/dist/lib/error-aggregator.js +234 -0
- package/dist/lib/error-aggregator.js.map +1 -0
- package/dist/lib/errors.js +287 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/file-lock-manager.js +578 -0
- package/dist/lib/file-lock-manager.js.map +1 -0
- package/dist/lib/file-operations.js +367 -0
- package/dist/lib/file-operations.js.map +1 -0
- package/dist/lib/idempotent-write.js +237 -0
- package/dist/lib/idempotent-write.js.map +1 -0
- package/dist/lib/integration-schema-validator.js +522 -0
- package/dist/lib/integration-schema-validator.js.map +1 -0
- package/dist/lib/lock-health-monitor.js +298 -0
- package/dist/lib/lock-health-monitor.js.map +1 -0
- package/dist/lib/log-shipper.js +422 -0
- package/dist/lib/log-shipper.js.map +1 -0
- package/dist/lib/logging.js +146 -0
- package/dist/lib/logging.js.map +1 -0
- package/dist/lib/message-deduplicator.js +439 -0
- package/dist/lib/message-deduplicator.js.map +1 -0
- package/dist/lib/multi-system-query.js +604 -0
- package/dist/lib/multi-system-query.js.map +1 -0
- package/dist/lib/orphan-detector.js +332 -0
- package/dist/lib/orphan-detector.js.map +1 -0
- package/dist/lib/password-generator.js +166 -0
- package/dist/lib/password-generator.js.map +1 -0
- package/dist/lib/path-validator.js +429 -0
- package/dist/lib/path-validator.js.map +1 -0
- package/dist/lib/query-translator.js +905 -0
- package/dist/lib/query-translator.js.map +1 -0
- package/dist/lib/queue-recovery.js +469 -0
- package/dist/lib/queue-recovery.js.map +1 -0
- package/dist/lib/redis-queue-manager.js +512 -0
- package/dist/lib/redis-queue-manager.js.map +1 -0
- package/dist/lib/reflection-archiver.js +272 -0
- package/dist/lib/reflection-archiver.js.map +1 -0
- package/dist/lib/retry-manager.js +453 -0
- package/dist/lib/retry-manager.js.map +1 -0
- package/dist/lib/retry.js +262 -0
- package/dist/lib/retry.js.map +1 -0
- package/dist/lib/schema-transform.js +695 -0
- package/dist/lib/schema-transform.js.map +1 -0
- package/dist/lib/schema-validator.js +491 -0
- package/dist/lib/schema-validator.js.map +1 -0
- package/dist/lib/skill-cache.js +297 -0
- package/dist/lib/skill-cache.js.map +1 -0
- package/dist/lib/skill-content-manager.js +337 -0
- package/dist/lib/skill-content-manager.js.map +1 -0
- package/dist/lib/skill-frontmatter-parser.js +237 -0
- package/dist/lib/skill-frontmatter-parser.js.map +1 -0
- package/dist/lib/skill-git-integration.js +275 -0
- package/dist/lib/skill-git-integration.js.map +1 -0
- package/dist/lib/skill-markdown-validator.js +396 -0
- package/dist/lib/skill-markdown-validator.js.map +1 -0
- package/dist/lib/skill-output-parser.js +312 -0
- package/dist/lib/skill-output-parser.js.map +1 -0
- package/dist/lib/unified-query-api.js +467 -0
- package/dist/lib/unified-query-api.js.map +1 -0
- package/dist/middleware/auth-middleware.js +350 -0
- package/dist/middleware/auth-middleware.js.map +1 -0
- package/dist/middleware/schema-validation.js +347 -0
- package/dist/middleware/schema-validation.js.map +1 -0
- package/dist/providers/anthropic-provider.js +1 -1
- package/dist/providers/anthropic-provider.js.map +1 -1
- package/dist/providers/provider-factory.js +2 -2
- package/dist/providers/provider-factory.js.map +1 -1
- package/dist/services/edge-case-analyzer.js +321 -0
- package/dist/services/edge-case-analyzer.js.map +1 -0
- package/dist/services/edge-case-deduplicator.js +266 -0
- package/dist/services/edge-case-deduplicator.js.map +1 -0
- package/dist/services/edge-case-detector.js +337 -0
- package/dist/services/edge-case-detector.js.map +1 -0
- package/dist/services/edge-case-tracker.js +547 -0
- package/dist/services/edge-case-tracker.js.map +1 -0
- package/dist/services/health-check-system.js +586 -0
- package/dist/services/health-check-system.js.map +1 -0
- package/dist/services/metrics-logger.js +412 -0
- package/dist/services/metrics-logger.js.map +1 -0
- package/dist/services/patch-generator.js +378 -0
- package/dist/services/patch-generator.js.map +1 -0
- package/dist/services/patch-validator.js +337 -0
- package/dist/services/patch-validator.js.map +1 -0
- package/dist/services/performance-monitor.js +811 -0
- package/dist/services/performance-monitor.js.map +1 -0
- package/dist/services/promotion-pipeline.js +918 -0
- package/dist/services/promotion-pipeline.js.map +1 -0
- package/dist/services/promotion-validator.js +394 -0
- package/dist/services/promotion-validator.js.map +1 -0
- package/dist/services/reflection-logger.js +388 -0
- package/dist/services/reflection-logger.js.map +1 -0
- package/dist/services/skill-deployment.js +472 -0
- package/dist/services/skill-deployment.js.map +1 -0
- package/dist/services/skill-loader.js +427 -0
- package/dist/services/skill-loader.js.map +1 -0
- package/dist/services/skill-promotion.js +372 -0
- package/dist/services/skill-promotion.js.map +1 -0
- package/dist/services/skill-validator.js +454 -0
- package/dist/services/skill-validator.js.map +1 -0
- package/dist/services/skill-versioning.js +244 -0
- package/dist/services/skill-versioning.js.map +1 -0
- package/dist/services/workspace-supervisor.js +597 -0
- package/dist/services/workspace-supervisor.js.map +1 -0
- package/dist/types/edge-case.js +45 -0
- package/dist/types/edge-case.js.map +1 -0
- package/docs/BUG_19_MEMORY_LEAK_TASK_MODE.md +405 -0
- package/docs/MEMORY_CLEANUP_GUIDE.md +358 -0
- package/docs/MEMORY_LEAK_FIX_SUMMARY.md +322 -0
- package/docs/REDIS_CLEANUP_EXECUTIVE_SUMMARY.md +319 -0
- package/docs/REDIS_CLEANUP_VERIFICATION_REPORT.md +574 -0
- package/package.json +35 -4
- package/readme/README.md +53 -5
- package/scripts/backup-cleanup.sh +627 -0
- package/scripts/cleanup-workspaces.sh +412 -0
- package/scripts/cleanup-yaml-configs.sh +141 -0
- package/scripts/deploy-approved-skills.sh +263 -0
- package/scripts/health-check.sh +447 -0
- package/scripts/log-aggregator.sh +554 -0
- package/scripts/log-monitor.sh +629 -0
- package/scripts/manage-agent-workspaces.sh +434 -0
- package/scripts/migrate-schema.sh +533 -0
- package/scripts/promote-staged-skills.sh +423 -0
- package/scripts/verify-no-secrets.sh +88 -35
- package/scripts/verify-redis-cleanup.sh +173 -0
- package/tests/README.md +84 -0
- package/tests/test-memory-leak-task-mode.sh +435 -0
- package/.claude/cfn-extras/agents/deprecated-coordinators/adaptive-coordinator.md.backup +0 -161
- package/.claude/cfn-extras/agents/deprecated-coordinators/blocking-coordinator-example.md.backup +0 -728
- package/.claude/cfn-extras/agents/deprecated-coordinators/mesh-coordinator.md.backup +0 -131
- package/.claude/skills/agent-lifecycle/SKILL.md +0 -60
- package/.claude/skills/agent-lifecycle/execute-lifecycle-hook.sh +0 -573
- package/.claude/skills/agent-lifecycle/simple-audit.sh +0 -31
- package/.claude/skills/cfn-agent-spawning/spawn-agent.sh.backup +0 -273
- package/.claude/skills/cfn-loop-orchestration/orchestrate.sh.backup +0 -949
- package/README.md.backup_before_replace +0 -781
- package/claude-assets/cfn-extras/agents/deprecated-coordinators/adaptive-coordinator.md.backup +0 -161
- package/claude-assets/cfn-extras/agents/deprecated-coordinators/blocking-coordinator-example.md.backup +0 -728
- package/claude-assets/cfn-extras/agents/deprecated-coordinators/mesh-coordinator.md.backup +0 -131
- package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh.backup +0 -273
- package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh.backup +0 -949
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/query-translator.ts"],"sourcesContent":["/**\r\n * Query Translator - SECURITY HARDENED\r\n *\r\n * Translates between SQL queries and Redis commands for cross-backend compatibility.\r\n * Includes query optimization and backend recommendation logic.\r\n *\r\n * SECURITY ENHANCEMENTS (CVE-2024-SQL-INJECTION):\r\n * - Input validation with whitelisting for identifiers (table/column names)\r\n * - Parameterized queries for ALL database values\r\n * - Comprehensive SQL injection prevention\r\n * - Strict identifier validation against allowed patterns\r\n * - Error handling using StandardError\r\n *\r\n * Part of Phase 2, Task P2-3.1: Unified Query API\r\n *\r\n * Features:\r\n * - SQL to Redis translation with security validation\r\n * - Redis to SQL translation with parameterization\r\n * - Query optimization\r\n * - Backend recommendation based on query patterns\r\n * - Performance monitoring (<50ms translation time)\r\n *\r\n * @example\r\n * ```typescript\r\n * const translator = new QueryTranslator({\r\n * allowedTables: ['tasks', 'users', 'projects'],\r\n * allowedFields: { tasks: ['id', 'name', 'status', 'description'] }\r\n * });\r\n *\r\n * // Translate SQL to Redis (fully parameterized)\r\n * const redisCmd = translator.translateSQLToRedis(\r\n * 'SELECT * FROM tasks WHERE id = ?',\r\n * ['task-123']\r\n * );\r\n *\r\n * // Translate Redis to SQL (fully parameterized)\r\n * const sqlQuery = translator.translateRedisToSQL({\r\n * command: 'HGETALL',\r\n * key: 'task:123'\r\n * });\r\n * ```\r\n */\r\n\r\nimport { BackendType, QueryRequest } from './unified-query-api.js';\r\nimport { StandardError, ErrorCode } from './errors.js';\r\n\r\n/**\r\n * Redis command structure\r\n */\r\nexport interface RedisCommand {\r\n command: string;\r\n key?: string;\r\n fields?: Record<string, any>;\r\n args?: any[];\r\n}\r\n\r\n/**\r\n * Translation result\r\n */\r\nexport interface TranslationResult {\r\n success: boolean;\r\n redisCommand?: RedisCommand;\r\n sqlQuery?: string;\r\n sqlParams?: any[];\r\n executionTime: number;\r\n recommendedBackend?: BackendType;\r\n warnings?: string[];\r\n}\r\n\r\n/**\r\n * Query optimization result\r\n */\r\nexport interface OptimizationResult {\r\n indexed?: string[];\r\n executionPlan?: string;\r\n estimatedCost?: number;\r\n recommendations?: string[];\r\n}\r\n\r\n/**\r\n * Configuration for QueryTranslator\r\n */\r\nexport interface QueryTranslatorConfig {\r\n // Whitelist of allowed table names\r\n allowedTables?: string[];\r\n\r\n // Whitelist of allowed fields per table\r\n allowedFields?: Record<string, string[]>;\r\n\r\n // Maximum allowed query length\r\n maxQueryLength?: number;\r\n\r\n // Maximum allowed parameters\r\n maxParams?: number;\r\n\r\n // Enable strict mode (block all unvalidated identifiers)\r\n strictMode?: boolean;\r\n}\r\n\r\n/**\r\n * Identifier validation result\r\n */\r\ninterface IdentifierValidationResult {\r\n valid: boolean;\r\n sanitized?: string;\r\n error?: string;\r\n}\r\n\r\n/**\r\n * SQL query parser - SECURITY HARDENED\r\n */\r\nclass SQLParser {\r\n private allowedTables: Set<string>;\r\n private allowedFields: Map<string, Set<string>>;\r\n private strictMode: boolean;\r\n\r\n constructor(\r\n allowedTables?: string[],\r\n allowedFields?: Record<string, string[]>,\r\n strictMode: boolean = true\r\n ) {\r\n this.allowedTables = new Set(allowedTables || []);\r\n this.allowedFields = new Map();\r\n\r\n if (allowedFields) {\r\n for (const [table, fields] of Object.entries(allowedFields)) {\r\n this.allowedFields.set(table.toLowerCase(), new Set(fields.map(f => f.toLowerCase())));\r\n }\r\n }\r\n\r\n this.strictMode = strictMode;\r\n }\r\n\r\n /**\r\n * Validate SQL identifier (table/column name)\r\n * Pattern: alphanumeric, underscore, starts with letter or underscore\r\n */\r\n private validateIdentifier(identifier: string, type: 'table' | 'field'): IdentifierValidationResult {\r\n if (!identifier || typeof identifier !== 'string') {\r\n return {\r\n valid: false,\r\n error: `Invalid ${type} name: must be a non-empty string`\r\n };\r\n }\r\n\r\n // Remove surrounding whitespace\r\n const trimmed = identifier.trim();\r\n\r\n // Check pattern: must start with letter or underscore, contain only alphanumeric and underscore\r\n const identifierPattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\r\n if (!identifierPattern.test(trimmed)) {\r\n return {\r\n valid: false,\r\n error: `Invalid ${type} name: must match pattern /^[a-zA-Z_][a-zA-Z0-9_]*$/. Got: \"${identifier}\"`\r\n };\r\n }\r\n\r\n // Check length (reasonable limit to prevent DoS)\r\n if (trimmed.length > 128) {\r\n return {\r\n valid: false,\r\n error: `Invalid ${type} name: exceeds maximum length of 128 characters`\r\n };\r\n }\r\n\r\n return { valid: true, sanitized: trimmed };\r\n }\r\n\r\n /**\r\n * Validate table name against whitelist\r\n */\r\n private validateTableName(table: string): IdentifierValidationResult {\r\n const validation = this.validateIdentifier(table, 'table');\r\n if (!validation.valid) {\r\n return validation;\r\n }\r\n\r\n const sanitized = validation.sanitized!.toLowerCase();\r\n\r\n // In strict mode, check against whitelist\r\n if (this.strictMode && this.allowedTables.size > 0) {\r\n if (!this.allowedTables.has(sanitized)) {\r\n return {\r\n valid: false,\r\n error: `Table \"${table}\" is not in the whitelist of allowed tables`\r\n };\r\n }\r\n }\r\n\r\n return { valid: true, sanitized: validation.sanitized };\r\n }\r\n\r\n /**\r\n * Validate field name against whitelist for specific table\r\n */\r\n private validateFieldName(field: string, table?: string): IdentifierValidationResult {\r\n const validation = this.validateIdentifier(field, 'field');\r\n if (!validation.valid) {\r\n return validation;\r\n }\r\n\r\n const sanitized = validation.sanitized!.toLowerCase();\r\n\r\n // In strict mode, check against whitelist if available\r\n if (this.strictMode && table) {\r\n const tableLower = table.toLowerCase();\r\n const allowedFieldsForTable = this.allowedFields.get(tableLower);\r\n\r\n if (allowedFieldsForTable && !allowedFieldsForTable.has(sanitized)) {\r\n return {\r\n valid: false,\r\n error: `Field \"${field}\" is not in the whitelist for table \"${table}\"`\r\n };\r\n }\r\n }\r\n\r\n return { valid: true, sanitized: validation.sanitized };\r\n }\r\n\r\n /**\r\n * Parse SQL SELECT statement\r\n */\r\n parseSelect(sql: string): {\r\n table?: string;\r\n fields?: string[];\r\n where?: { field: string; operator: string; value?: any }[];\r\n joins?: Array<{ table: string; on: string }>;\r\n error?: string;\r\n } {\r\n const result: any = {};\r\n\r\n try {\r\n // Extract table name\r\n const tableMatch = sql.match(/FROM\\s+(\\w+)/i);\r\n if (tableMatch) {\r\n const tableValidation = this.validateTableName(tableMatch[1]);\r\n if (!tableValidation.valid) {\r\n return { error: tableValidation.error };\r\n }\r\n result.table = tableValidation.sanitized;\r\n }\r\n\r\n // Extract fields\r\n const fieldsMatch = sql.match(/SELECT\\s+(.*?)\\s+FROM/i);\r\n if (fieldsMatch) {\r\n const fields = fieldsMatch[1].trim();\r\n if (fields === '*') {\r\n result.fields = ['*'];\r\n } else {\r\n const fieldList = fields.split(',').map(f => f.trim());\r\n const validatedFields = [];\r\n\r\n for (const field of fieldList) {\r\n const fieldValidation = this.validateFieldName(field, result.table);\r\n if (!fieldValidation.valid) {\r\n return { error: fieldValidation.error };\r\n }\r\n validatedFields.push(fieldValidation.sanitized);\r\n }\r\n result.fields = validatedFields;\r\n }\r\n }\r\n\r\n // Extract WHERE clause\r\n const whereMatch = sql.match(/WHERE\\s+(.*?)(?:ORDER BY|GROUP BY|LIMIT|$)/i);\r\n if (whereMatch) {\r\n const whereClause = whereMatch[1].trim();\r\n const whereResult = this.parseWhereClause(whereClause, result.table);\r\n if (whereResult.error) {\r\n return { error: whereResult.error };\r\n }\r\n result.where = whereResult.conditions;\r\n }\r\n\r\n // Extract JOINs\r\n const joinMatches = sql.matchAll(/(?:INNER |LEFT |RIGHT |)?JOIN\\s+(\\w+)\\s+ON\\s+(.*?)(?:WHERE|ORDER BY|GROUP BY|LIMIT|JOIN|$)/gi);\r\n result.joins = [];\r\n for (const match of joinMatches) {\r\n const joinTableValidation = this.validateTableName(match[1]);\r\n if (!joinTableValidation.valid) {\r\n return { error: joinTableValidation.error };\r\n }\r\n result.joins.push({\r\n table: joinTableValidation.sanitized,\r\n on: match[2].trim(),\r\n });\r\n }\r\n\r\n return result;\r\n } catch (error) {\r\n return {\r\n error: `Failed to parse SELECT statement: ${error instanceof Error ? error.message : 'Unknown error'}`\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Parse WHERE clause with validation\r\n */\r\n private parseWhereClause(whereClause: string, table?: string): {\r\n conditions?: Array<{ field: string; operator: string; value?: any }>;\r\n error?: string;\r\n } {\r\n const conditions: Array<{ field: string; operator: string; value?: any }> = [];\r\n\r\n try {\r\n // Simple parser for basic conditions\r\n // Format: field = ? OR field LIKE ? etc.\r\n const parts = whereClause.split(/\\s+AND\\s+/i);\r\n\r\n for (const part of parts) {\r\n const match = part.match(/(\\w+)\\s*(=|!=|>|>=|<|<=|LIKE|IN|NOT IN)\\s*(.+)/i);\r\n if (match) {\r\n const fieldValidation = this.validateFieldName(match[1], table);\r\n if (!fieldValidation.valid) {\r\n return { error: fieldValidation.error };\r\n }\r\n\r\n conditions.push({\r\n field: fieldValidation.sanitized!,\r\n operator: match[2].toLowerCase(),\r\n value: match[3] === '?' ? undefined : match[3],\r\n });\r\n }\r\n }\r\n\r\n return { conditions };\r\n } catch (error) {\r\n return {\r\n error: `Failed to parse WHERE clause: ${error instanceof Error ? error.message : 'Unknown error'}`\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Parse SQL INSERT statement with validation\r\n */\r\n parseInsert(sql: string): {\r\n table?: string;\r\n fields?: string[];\r\n error?: string;\r\n } {\r\n const result: any = {};\r\n\r\n try {\r\n // Extract table name\r\n const tableMatch = sql.match(/INSERT INTO\\s+(\\w+)/i);\r\n if (tableMatch) {\r\n const tableValidation = this.validateTableName(tableMatch[1]);\r\n if (!tableValidation.valid) {\r\n return { error: tableValidation.error };\r\n }\r\n result.table = tableValidation.sanitized;\r\n }\r\n\r\n // Extract fields\r\n const fieldsMatch = sql.match(/\\(([^)]+)\\)\\s+VALUES/i);\r\n if (fieldsMatch) {\r\n const fieldList = fieldsMatch[1].split(',').map(f => f.trim());\r\n const validatedFields = [];\r\n\r\n for (const field of fieldList) {\r\n const fieldValidation = this.validateFieldName(field, result.table);\r\n if (!fieldValidation.valid) {\r\n return { error: fieldValidation.error };\r\n }\r\n validatedFields.push(fieldValidation.sanitized);\r\n }\r\n result.fields = validatedFields;\r\n }\r\n\r\n return result;\r\n } catch (error) {\r\n return {\r\n error: `Failed to parse INSERT statement: ${error instanceof Error ? error.message : 'Unknown error'}`\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Parse SQL UPDATE statement with validation\r\n */\r\n parseUpdate(sql: string): {\r\n table?: string;\r\n fields?: string[];\r\n where?: { field: string; operator: string; value?: any }[];\r\n error?: string;\r\n } {\r\n const result: any = {};\r\n\r\n try {\r\n // Extract table name\r\n const tableMatch = sql.match(/UPDATE\\s+(\\w+)/i);\r\n if (tableMatch) {\r\n const tableValidation = this.validateTableName(tableMatch[1]);\r\n if (!tableValidation.valid) {\r\n return { error: tableValidation.error };\r\n }\r\n result.table = tableValidation.sanitized;\r\n }\r\n\r\n // Extract SET clause\r\n const setMatch = sql.match(/SET\\s+(.*?)\\s+WHERE/i);\r\n if (setMatch) {\r\n const setParts = setMatch[1].split(',');\r\n const validatedFields = [];\r\n\r\n for (const part of setParts) {\r\n const field = part.split('=')[0].trim();\r\n const fieldValidation = this.validateFieldName(field, result.table);\r\n if (!fieldValidation.valid) {\r\n return { error: fieldValidation.error };\r\n }\r\n validatedFields.push(fieldValidation.sanitized);\r\n }\r\n result.fields = validatedFields;\r\n }\r\n\r\n // Extract WHERE clause\r\n const whereMatch = sql.match(/WHERE\\s+(.*?)$/i);\r\n if (whereMatch) {\r\n const whereResult = this.parseWhereClause(whereMatch[1].trim(), result.table);\r\n if (whereResult.error) {\r\n return { error: whereResult.error };\r\n }\r\n result.where = whereResult.conditions;\r\n }\r\n\r\n return result;\r\n } catch (error) {\r\n return {\r\n error: `Failed to parse UPDATE statement: ${error instanceof Error ? error.message : 'Unknown error'}`\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Parse SQL DELETE statement with validation\r\n */\r\n parseDelete(sql: string): {\r\n table?: string;\r\n where?: { field: string; operator: string; value?: any }[];\r\n error?: string;\r\n } {\r\n const result: any = {};\r\n\r\n try {\r\n // Extract table name\r\n const tableMatch = sql.match(/DELETE FROM\\s+(\\w+)/i);\r\n if (tableMatch) {\r\n const tableValidation = this.validateTableName(tableMatch[1]);\r\n if (!tableValidation.valid) {\r\n return { error: tableValidation.error };\r\n }\r\n result.table = tableValidation.sanitized;\r\n }\r\n\r\n // Extract WHERE clause\r\n const whereMatch = sql.match(/WHERE\\s+(.*?)$/i);\r\n if (whereMatch) {\r\n const whereResult = this.parseWhereClause(whereMatch[1].trim(), result.table);\r\n if (whereResult.error) {\r\n return { error: whereResult.error };\r\n }\r\n result.where = whereResult.conditions;\r\n }\r\n\r\n return result;\r\n } catch (error) {\r\n return {\r\n error: `Failed to parse DELETE statement: ${error instanceof Error ? error.message : 'Unknown error'}`\r\n };\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Query Translator - SECURITY HARDENED\r\n *\r\n * Provides bidirectional translation between SQL and Redis commands\r\n * with comprehensive input validation and SQL injection prevention\r\n */\r\nexport class QueryTranslator {\r\n private parser: SQLParser;\r\n private config: Required<QueryTranslatorConfig>;\r\n\r\n constructor(config?: QueryTranslatorConfig) {\r\n this.config = {\r\n allowedTables: config?.allowedTables || [],\r\n allowedFields: config?.allowedFields || {},\r\n maxQueryLength: config?.maxQueryLength || 10000,\r\n maxParams: config?.maxParams || 100,\r\n strictMode: config?.strictMode !== false,\r\n };\r\n\r\n this.parser = new SQLParser(\r\n this.config.allowedTables,\r\n this.config.allowedFields,\r\n this.config.strictMode\r\n );\r\n }\r\n\r\n /**\r\n * Validate input parameters\r\n */\r\n private validateInput(sql: string, params: any[] = []): { valid: boolean; error?: string } {\r\n if (!sql || typeof sql !== 'string') {\r\n return { valid: false, error: 'SQL query must be a non-empty string' };\r\n }\r\n\r\n if (sql.length > this.config.maxQueryLength) {\r\n return {\r\n valid: false,\r\n error: `SQL query exceeds maximum length of ${this.config.maxQueryLength} characters`\r\n };\r\n }\r\n\r\n if (!Array.isArray(params)) {\r\n return { valid: false, error: 'Parameters must be an array' };\r\n }\r\n\r\n if (params.length > this.config.maxParams) {\r\n return {\r\n valid: false,\r\n error: `Too many parameters. Maximum: ${this.config.maxParams}`\r\n };\r\n }\r\n\r\n // Detect SQL injection patterns in the query\r\n // These patterns should ONLY appear in specific contexts (after ?)\r\n const injectionPatterns = [\r\n /\\bOR\\b[\\s]*(\\d+\\s*=\\s*\\d+|'[^']*'\\s*=\\s*'[^']*'|true|1)/i, // OR 1=1, OR 'a'='a'\r\n /\\bUNION\\b[\\s]+(SELECT|ALL)/i, // UNION SELECT\r\n /--\\s*$/i, // SQL comments at end\r\n /\\/\\*[\\s\\S]*?\\*\\//i, // Multi-line comments\r\n /;\\s*(SELECT|INSERT|UPDATE|DELETE|DROP|TRUNCATE|ALTER)/i, // Stacked queries\r\n /\\bDROP\\b|\\bTRUNCATE\\b|\\bALTER\\b|\\bCREATE\\b/i, // DDL commands\r\n /\\bEVAL\\b|\\bEXEC\\b|\\bSCRIPT\\b/i, // Dangerous functions\r\n /\\x00/, // Null bytes\r\n ];\r\n\r\n // Split query into parts based on ? placeholders\r\n const parts = sql.split('?');\r\n\r\n // Check structure: should be [prefix, suffix] or [prefix1, middle1, middle2, suffix]\r\n // Injection attempts will have SQL keywords AFTER the ?\r\n for (let i = 0; i < parts.length; i++) {\r\n const part = parts[i];\r\n\r\n // First part should contain SELECT/INSERT/UPDATE/DELETE/FROM\r\n if (i === 0) {\r\n // Validate it's a valid SQL statement start\r\n if (!/(SELECT|INSERT|UPDATE|DELETE|FROM)\\b/i.test(part)) {\r\n // Only valid if it's completely empty (error caught elsewhere)\r\n if (part.trim()) {\r\n return {\r\n valid: false,\r\n error: 'Invalid SQL query structure. Must start with SELECT, INSERT, UPDATE, or DELETE'\r\n };\r\n }\r\n }\r\n } else if (i === parts.length - 1) {\r\n // Last part (after last ?) should not have SQL keywords that indicate injection\r\n if (/\\b(OR|UNION|SELECT|INSERT|UPDATE|DELETE|DROP|TRUNCATE)\\b/i.test(part)) {\r\n return {\r\n valid: false,\r\n error: 'SQL query contains suspicious injection patterns'\r\n };\r\n }\r\n } else {\r\n // Middle parts (between ? and ?) should be minimal\r\n // Should only contain WHERE, AND, OR (as operators), commas, etc.\r\n // But NOT SELECT, UNION, etc.\r\n if (/\\b(UNION|SELECT|INSERT|UPDATE|DELETE|DROP)\\b/i.test(part)) {\r\n return {\r\n valid: false,\r\n error: 'SQL query contains suspicious injection patterns'\r\n };\r\n }\r\n }\r\n }\r\n\r\n // Check for injection patterns in the entire query\r\n for (const pattern of injectionPatterns) {\r\n // Skip OR if it's part of valid syntax (after WHERE)\r\n if (pattern.source.includes('OR') && /WHERE[\\s\\S]*\\?[\\s\\S]*OR/i.test(sql)) {\r\n // This could be valid OR in WHERE clause\r\n const afterQuestion = sql.substring(sql.indexOf('?') + 1);\r\n if (/\\bOR\\b[\\s]*[\\'\\\"]?\\d+['\\\"]*\\s*=\\s*[\\'\\\"]?\\d+['\\\"]*|OR\\s*'[^']*'\\s*=\\s*'[^']*'|OR\\s*TRUE/i.test(afterQuestion)) {\r\n return {\r\n valid: false,\r\n error: 'SQL query contains SQL injection pattern: OR-based bypass'\r\n };\r\n }\r\n } else if (pattern.test(sql) && !sql.includes('?')) {\r\n // Pattern found but no parameterization\r\n return {\r\n valid: false,\r\n error: 'SQL query contains suspicious patterns. Use parameterized queries.'\r\n };\r\n }\r\n }\r\n\r\n return { valid: true };\r\n }\r\n\r\n /**\r\n * Validate Redis command structure\r\n */\r\n private validateRedisCommand(command: RedisCommand): { valid: boolean; error?: string } {\r\n if (!command || typeof command !== 'object') {\r\n return { valid: false, error: 'Redis command must be an object' };\r\n }\r\n\r\n if (!command.command || typeof command.command !== 'string') {\r\n return { valid: false, error: 'Redis command name must be a non-empty string' };\r\n }\r\n\r\n const allowedCommands = ['GET', 'SET', 'HGET', 'HGETALL', 'HMSET', 'HSET', 'DEL', 'MGET', 'MSET'];\r\n if (!allowedCommands.includes(command.command.toUpperCase())) {\r\n return {\r\n valid: false,\r\n error: `Redis command \"${command.command}\" is not allowed. Allowed: ${allowedCommands.join(', ')}`\r\n };\r\n }\r\n\r\n if (command.key && typeof command.key !== 'string') {\r\n return { valid: false, error: 'Redis key must be a string' };\r\n }\r\n\r\n if (command.fields && typeof command.fields !== 'object') {\r\n return { valid: false, error: 'Redis fields must be an object' };\r\n }\r\n\r\n if (command.args && !Array.isArray(command.args)) {\r\n return { valid: false, error: 'Redis args must be an array' };\r\n }\r\n\r\n return { valid: true };\r\n }\r\n\r\n /**\r\n * Translate SQL query to Redis commands\r\n */\r\n translateSQLToRedis(sql: string, params: any[] = []): TranslationResult {\r\n const startTime = Date.now();\r\n const warnings: string[] = [];\r\n\r\n try {\r\n // Validate inputs\r\n const inputValidation = this.validateInput(sql, params);\r\n if (!inputValidation.valid) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n inputValidation.error || 'Invalid input',\r\n { sql, paramCount: params.length }\r\n );\r\n }\r\n\r\n // Determine query type\r\n const queryType = this.getQueryType(sql);\r\n\r\n let redisCommand: RedisCommand | undefined;\r\n let recommendedBackend = BackendType.REDIS;\r\n\r\n switch (queryType) {\r\n case 'SELECT': {\r\n const selectParsed = this.parser.parseSelect(sql);\r\n\r\n if (selectParsed.error) {\r\n throw new StandardError(\r\n ErrorCode.PARSE_ERROR,\r\n `Failed to parse SELECT statement: ${selectParsed.error}`,\r\n { sql }\r\n );\r\n }\r\n\r\n // Check if query is complex (has joins)\r\n if (selectParsed.joins && selectParsed.joins.length > 0) {\r\n warnings.push('Complex queries with JOINs are better suited for PostgreSQL');\r\n recommendedBackend = BackendType.POSTGRES;\r\n }\r\n\r\n // Translate to Redis HGETALL or GET\r\n if (selectParsed.where && selectParsed.where.length > 0) {\r\n const idCondition = selectParsed.where.find(w => w.field === 'id');\r\n if (idCondition) {\r\n const keyValue = params[0];\r\n\r\n // Validate key value\r\n if (typeof keyValue !== 'string' && typeof keyValue !== 'number') {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Redis key value must be a string or number',\r\n { paramValue: typeof keyValue }\r\n );\r\n }\r\n\r\n const redisKey = `${selectParsed.table}:${keyValue}`;\r\n\r\n redisCommand = {\r\n command: selectParsed.fields?.[0] === '*' ? 'HGETALL' : 'HGET',\r\n key: redisKey,\r\n };\r\n }\r\n }\r\n break;\r\n }\r\n\r\n case 'INSERT': {\r\n const insertParsed = this.parser.parseInsert(sql);\r\n\r\n if (insertParsed.error) {\r\n throw new StandardError(\r\n ErrorCode.PARSE_ERROR,\r\n `Failed to parse INSERT statement: ${insertParsed.error}`,\r\n { sql }\r\n );\r\n }\r\n\r\n if (insertParsed.table && insertParsed.fields) {\r\n const idValue = params[0];\r\n\r\n // Validate key value\r\n if (typeof idValue !== 'string' && typeof idValue !== 'number') {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Redis key value must be a string or number',\r\n { paramValue: typeof idValue }\r\n );\r\n }\r\n\r\n const redisKey = `${insertParsed.table}:${idValue}`;\r\n\r\n // Build field-value pairs with validation\r\n const fields: Record<string, any> = {};\r\n for (let i = 0; i < insertParsed.fields.length && i < params.length; i++) {\r\n // Ensure params are not objects/arrays (prevent injection)\r\n const paramValue = params[i];\r\n if (typeof paramValue === 'object' && paramValue !== null) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Parameter values must be primitives (string, number, boolean, null)',\r\n { paramIndex: i, paramType: typeof paramValue }\r\n );\r\n }\r\n fields[insertParsed.fields[i]] = paramValue;\r\n }\r\n\r\n redisCommand = {\r\n command: 'HMSET',\r\n key: redisKey,\r\n fields,\r\n };\r\n }\r\n break;\r\n }\r\n\r\n case 'UPDATE': {\r\n const updateParsed = this.parser.parseUpdate(sql);\r\n\r\n if (updateParsed.error) {\r\n throw new StandardError(\r\n ErrorCode.PARSE_ERROR,\r\n `Failed to parse UPDATE statement: ${updateParsed.error}`,\r\n { sql }\r\n );\r\n }\r\n\r\n if (updateParsed.table && updateParsed.where) {\r\n const idCondition = updateParsed.where.find(w => w.field === 'id');\r\n if (idCondition) {\r\n const keyValue = params[params.length - 1];\r\n\r\n // Validate key value\r\n if (typeof keyValue !== 'string' && typeof keyValue !== 'number') {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Redis key value must be a string or number',\r\n { paramValue: typeof keyValue }\r\n );\r\n }\r\n\r\n const redisKey = `${updateParsed.table}:${keyValue}`;\r\n\r\n redisCommand = {\r\n command: 'HSET',\r\n key: redisKey,\r\n args: params.slice(0, -1),\r\n };\r\n }\r\n }\r\n break;\r\n }\r\n\r\n case 'DELETE': {\r\n const deleteParsed = this.parser.parseDelete(sql);\r\n\r\n if (deleteParsed.error) {\r\n throw new StandardError(\r\n ErrorCode.PARSE_ERROR,\r\n `Failed to parse DELETE statement: ${deleteParsed.error}`,\r\n { sql }\r\n );\r\n }\r\n\r\n if (deleteParsed.table && deleteParsed.where) {\r\n const idCondition = deleteParsed.where.find(w => w.field === 'id');\r\n if (idCondition) {\r\n const keyValue = params[0];\r\n\r\n // Validate key value\r\n if (typeof keyValue !== 'string' && typeof keyValue !== 'number') {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Redis key value must be a string or number',\r\n { paramValue: typeof keyValue }\r\n );\r\n }\r\n\r\n const redisKey = `${deleteParsed.table}:${keyValue}`;\r\n\r\n redisCommand = {\r\n command: 'DEL',\r\n key: redisKey,\r\n };\r\n }\r\n }\r\n break;\r\n }\r\n\r\n default:\r\n warnings.push(`Unsupported SQL query type: ${queryType}`);\r\n recommendedBackend = BackendType.POSTGRES;\r\n }\r\n\r\n const executionTime = Date.now() - startTime;\r\n\r\n if (executionTime > 50) {\r\n warnings.push(`Translation took ${executionTime}ms (target: <50ms)`);\r\n }\r\n\r\n return {\r\n success: !!redisCommand,\r\n redisCommand,\r\n executionTime,\r\n recommendedBackend,\r\n warnings: warnings.length > 0 ? warnings : undefined,\r\n };\r\n } catch (error) {\r\n const executionTime = Date.now() - startTime;\r\n\r\n const message = error instanceof StandardError\r\n ? error.message\r\n : error instanceof Error\r\n ? error.message\r\n : 'Unknown error';\r\n\r\n return {\r\n success: false,\r\n executionTime,\r\n warnings: [`Translation failed: ${message}`],\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Translate Redis command to SQL query (with parameterization)\r\n */\r\n translateRedisToSQL(command: RedisCommand): TranslationResult {\r\n const startTime = Date.now();\r\n const warnings: string[] = [];\r\n\r\n try {\r\n // Validate Redis command\r\n const commandValidation = this.validateRedisCommand(command);\r\n if (!commandValidation.valid) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n commandValidation.error || 'Invalid Redis command',\r\n { command: command.command }\r\n );\r\n }\r\n\r\n let sqlQuery: string | undefined;\r\n let sqlParams: any[] = [];\r\n\r\n // Parse Redis key to extract table and ID\r\n const keyParts = command.key?.split(':') || [];\r\n const table = keyParts[0] || 'unknown';\r\n const id = keyParts[1];\r\n\r\n // Validate table name\r\n const tableValidation = this.parser['validateTableName'] ||\r\n ((t: string) => ({ valid: true, sanitized: t }));\r\n\r\n // Since validateTableName is private, we do basic validation\r\n const tablePattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\r\n if (!tablePattern.test(table)) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Invalid table name in Redis key',\r\n { key: command.key }\r\n );\r\n }\r\n\r\n switch (command.command.toUpperCase()) {\r\n case 'GET':\r\n case 'HGET':\r\n case 'HGETALL': {\r\n // SELECT * FROM table WHERE id = ?\r\n sqlQuery = `SELECT * FROM ${table} WHERE id = ?`;\r\n sqlParams = [id];\r\n break;\r\n }\r\n\r\n case 'SET':\r\n case 'HMSET': {\r\n // INSERT INTO table (field1, field2, ...) VALUES (?, ?, ...)\r\n if (command.fields) {\r\n const fields = Object.keys(command.fields);\r\n const placeholders = fields.map(() => '?').join(', ');\r\n sqlQuery = `INSERT INTO ${table} (${fields.join(', ')}) VALUES (${placeholders})`;\r\n sqlParams = Object.values(command.fields);\r\n\r\n // Validate all params are primitives\r\n for (let i = 0; i < sqlParams.length; i++) {\r\n if (typeof sqlParams[i] === 'object' && sqlParams[i] !== null) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Parameter values must be primitives',\r\n { paramIndex: i }\r\n );\r\n }\r\n }\r\n }\r\n break;\r\n }\r\n\r\n case 'HSET': {\r\n // UPDATE table SET field = ? WHERE id = ?\r\n if (command.args && command.args.length > 0) {\r\n const field = command.args[0];\r\n\r\n // Validate field name\r\n if (typeof field !== 'string' || !/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(field)) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Invalid field name in HSET command',\r\n { field }\r\n );\r\n }\r\n\r\n sqlQuery = `UPDATE ${table} SET ${field} = ? WHERE id = ?`;\r\n sqlParams = [command.args[1], id];\r\n\r\n // Validate params\r\n for (let i = 0; i < sqlParams.length; i++) {\r\n if (typeof sqlParams[i] === 'object' && sqlParams[i] !== null) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Parameter values must be primitives',\r\n { paramIndex: i }\r\n );\r\n }\r\n }\r\n }\r\n break;\r\n }\r\n\r\n case 'DEL': {\r\n // DELETE FROM table WHERE id = ?\r\n sqlQuery = `DELETE FROM ${table} WHERE id = ?`;\r\n sqlParams = [id];\r\n break;\r\n }\r\n\r\n default:\r\n warnings.push(`Unsupported Redis command: ${command.command}`);\r\n }\r\n\r\n const executionTime = Date.now() - startTime;\r\n\r\n if (executionTime > 50) {\r\n warnings.push(`Translation took ${executionTime}ms (target: <50ms)`);\r\n }\r\n\r\n return {\r\n success: !!sqlQuery,\r\n sqlQuery,\r\n sqlParams,\r\n executionTime,\r\n warnings: warnings.length > 0 ? warnings : undefined,\r\n };\r\n } catch (error) {\r\n const executionTime = Date.now() - startTime;\r\n\r\n const message = error instanceof StandardError\r\n ? error.message\r\n : error instanceof Error\r\n ? error.message\r\n : 'Unknown error';\r\n\r\n return {\r\n success: false,\r\n executionTime,\r\n warnings: [`Translation failed: ${message}`],\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Optimize query and provide recommendations\r\n */\r\n optimizeQuery(request: QueryRequest): OptimizationResult & { indexes?: string[] } {\r\n const result: OptimizationResult & { indexes?: string[] } = {\r\n indexed: [],\r\n recommendations: [],\r\n };\r\n\r\n // Recommend indexes for filtered fields\r\n if (request.filters) {\r\n const indexFields = request.filters.map(f => String(f.field));\r\n result.indexed = indexFields;\r\n result.indexes = indexFields;\r\n result.recommendations?.push(`Consider adding indexes on: ${indexFields.join(', ')}`);\r\n }\r\n\r\n // Estimate query cost\r\n let cost = 1;\r\n if (request.joins) {\r\n cost += request.joins.length * 10; // JOINs are expensive\r\n }\r\n if (request.filters) {\r\n cost += request.filters.length * 2;\r\n }\r\n result.estimatedCost = cost;\r\n\r\n // Provide optimization recommendations\r\n if (request.joins && request.joins.length > 2) {\r\n result.recommendations?.push('Consider denormalizing data or using materialized views for complex joins');\r\n }\r\n\r\n if (request.filters && request.filters.length > 5) {\r\n result.recommendations?.push('Consider composite indexes for multiple filter conditions');\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Recommend backend based on query characteristics\r\n */\r\n recommendBackend(request: QueryRequest): BackendType {\r\n // Simple key-value access → Redis\r\n if (request.key && !request.joins) {\r\n return BackendType.REDIS;\r\n }\r\n\r\n // Complex queries with JOINs → PostgreSQL\r\n if (request.joins && request.joins.length > 0) {\r\n return BackendType.POSTGRES;\r\n }\r\n\r\n // Session/cache data → Redis\r\n if (request.dataType === 'cache' || request.dataType === 'session') {\r\n return BackendType.REDIS;\r\n }\r\n\r\n // Embedded/local data → SQLite\r\n if (request.dataType === 'embedded') {\r\n return BackendType.SQLITE;\r\n }\r\n\r\n // Default to PostgreSQL for structured data\r\n return BackendType.POSTGRES;\r\n }\r\n\r\n /**\r\n * Get query type from SQL string\r\n */\r\n private getQueryType(sql: string): string {\r\n const trimmed = sql.trim().toUpperCase();\r\n\r\n if (trimmed.startsWith('SELECT')) return 'SELECT';\r\n if (trimmed.startsWith('INSERT')) return 'INSERT';\r\n if (trimmed.startsWith('UPDATE')) return 'UPDATE';\r\n if (trimmed.startsWith('DELETE')) return 'DELETE';\r\n\r\n return 'UNKNOWN';\r\n }\r\n}\r\n"],"names":["BackendType","StandardError","ErrorCode","SQLParser","allowedTables","allowedFields","strictMode","Set","Map","table","fields","Object","entries","set","toLowerCase","map","f","validateIdentifier","identifier","type","valid","error","trimmed","trim","identifierPattern","test","length","sanitized","validateTableName","validation","size","has","validateFieldName","field","tableLower","allowedFieldsForTable","get","parseSelect","sql","result","tableMatch","match","tableValidation","fieldsMatch","fieldList","split","validatedFields","fieldValidation","push","whereMatch","whereClause","whereResult","parseWhereClause","where","conditions","joinMatches","matchAll","joins","joinTableValidation","on","Error","message","parts","part","operator","value","undefined","parseInsert","parseUpdate","setMatch","setParts","parseDelete","QueryTranslator","parser","config","maxQueryLength","maxParams","validateInput","params","Array","isArray","injectionPatterns","i","pattern","source","includes","afterQuestion","substring","indexOf","validateRedisCommand","command","allowedCommands","toUpperCase","join","key","args","translateSQLToRedis","startTime","Date","now","warnings","inputValidation","VALIDATION_FAILED","paramCount","queryType","getQueryType","redisCommand","recommendedBackend","REDIS","selectParsed","PARSE_ERROR","POSTGRES","idCondition","find","w","keyValue","paramValue","redisKey","insertParsed","idValue","paramIndex","paramType","updateParsed","slice","deleteParsed","executionTime","success","translateRedisToSQL","commandValidation","sqlQuery","sqlParams","keyParts","id","t","tablePattern","keys","placeholders","values","optimizeQuery","request","indexed","recommendations","filters","indexFields","String","indexes","cost","estimatedCost","recommendBackend","dataType","SQLITE","startsWith"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyCC,GAED,SAASA,WAAW,QAAsB,yBAAyB;AACnE,SAASC,aAAa,EAAEC,SAAS,QAAQ,cAAc;AAgEvD;;CAEC,GACD,IAAA,AAAMC,YAAN,MAAMA;IACIC,cAA2B;IAC3BC,cAAwC;IACxCC,WAAoB;IAE5B,YACEF,aAAwB,EACxBC,aAAwC,EACxCC,aAAsB,IAAI,CAC1B;QACA,IAAI,CAACF,aAAa,GAAG,IAAIG,IAAIH,iBAAiB,EAAE;QAChD,IAAI,CAACC,aAAa,GAAG,IAAIG;QAEzB,IAAIH,eAAe;YACjB,KAAK,MAAM,CAACI,OAAOC,OAAO,IAAIC,OAAOC,OAAO,CAACP,eAAgB;gBAC3D,IAAI,CAACA,aAAa,CAACQ,GAAG,CAACJ,MAAMK,WAAW,IAAI,IAAIP,IAAIG,OAAOK,GAAG,CAACC,CAAAA,IAAKA,EAAEF,WAAW;YACnF;QACF;QAEA,IAAI,CAACR,UAAU,GAAGA;IACpB;IAEA;;;GAGC,GACD,AAAQW,mBAAmBC,UAAkB,EAAEC,IAAuB,EAA8B;QAClG,IAAI,CAACD,cAAc,OAAOA,eAAe,UAAU;YACjD,OAAO;gBACLE,OAAO;gBACPC,OAAO,CAAC,QAAQ,EAAEF,KAAK,iCAAiC,CAAC;YAC3D;QACF;QAEA,gCAAgC;QAChC,MAAMG,UAAUJ,WAAWK,IAAI;QAE/B,gGAAgG;QAChG,MAAMC,oBAAoB;QAC1B,IAAI,CAACA,kBAAkBC,IAAI,CAACH,UAAU;YACpC,OAAO;gBACLF,OAAO;gBACPC,OAAO,CAAC,QAAQ,EAAEF,KAAK,4DAA4D,EAAED,WAAW,CAAC,CAAC;YACpG;QACF;QAEA,iDAAiD;QACjD,IAAII,QAAQI,MAAM,GAAG,KAAK;YACxB,OAAO;gBACLN,OAAO;gBACPC,OAAO,CAAC,QAAQ,EAAEF,KAAK,+CAA+C,CAAC;YACzE;QACF;QAEA,OAAO;YAAEC,OAAO;YAAMO,WAAWL;QAAQ;IAC3C;IAEA;;GAEC,GACD,AAAQM,kBAAkBnB,KAAa,EAA8B;QACnE,MAAMoB,aAAa,IAAI,CAACZ,kBAAkB,CAACR,OAAO;QAClD,IAAI,CAACoB,WAAWT,KAAK,EAAE;YACrB,OAAOS;QACT;QAEA,MAAMF,YAAYE,WAAWF,SAAS,CAAEb,WAAW;QAEnD,0CAA0C;QAC1C,IAAI,IAAI,CAACR,UAAU,IAAI,IAAI,CAACF,aAAa,CAAC0B,IAAI,GAAG,GAAG;YAClD,IAAI,CAAC,IAAI,CAAC1B,aAAa,CAAC2B,GAAG,CAACJ,YAAY;gBACtC,OAAO;oBACLP,OAAO;oBACPC,OAAO,CAAC,OAAO,EAAEZ,MAAM,2CAA2C,CAAC;gBACrE;YACF;QACF;QAEA,OAAO;YAAEW,OAAO;YAAMO,WAAWE,WAAWF,SAAS;QAAC;IACxD;IAEA;;GAEC,GACD,AAAQK,kBAAkBC,KAAa,EAAExB,KAAc,EAA8B;QACnF,MAAMoB,aAAa,IAAI,CAACZ,kBAAkB,CAACgB,OAAO;QAClD,IAAI,CAACJ,WAAWT,KAAK,EAAE;YACrB,OAAOS;QACT;QAEA,MAAMF,YAAYE,WAAWF,SAAS,CAAEb,WAAW;QAEnD,uDAAuD;QACvD,IAAI,IAAI,CAACR,UAAU,IAAIG,OAAO;YAC5B,MAAMyB,aAAazB,MAAMK,WAAW;YACpC,MAAMqB,wBAAwB,IAAI,CAAC9B,aAAa,CAAC+B,GAAG,CAACF;YAErD,IAAIC,yBAAyB,CAACA,sBAAsBJ,GAAG,CAACJ,YAAY;gBAClE,OAAO;oBACLP,OAAO;oBACPC,OAAO,CAAC,OAAO,EAAEY,MAAM,qCAAqC,EAAExB,MAAM,CAAC,CAAC;gBACxE;YACF;QACF;QAEA,OAAO;YAAEW,OAAO;YAAMO,WAAWE,WAAWF,SAAS;QAAC;IACxD;IAEA;;GAEC,GACDU,YAAYC,GAAW,EAMrB;QACA,MAAMC,SAAc,CAAC;QAErB,IAAI;YACF,qBAAqB;YACrB,MAAMC,aAAaF,IAAIG,KAAK,CAAC;YAC7B,IAAID,YAAY;gBACd,MAAME,kBAAkB,IAAI,CAACd,iBAAiB,CAACY,UAAU,CAAC,EAAE;gBAC5D,IAAI,CAACE,gBAAgBtB,KAAK,EAAE;oBAC1B,OAAO;wBAAEC,OAAOqB,gBAAgBrB,KAAK;oBAAC;gBACxC;gBACAkB,OAAO9B,KAAK,GAAGiC,gBAAgBf,SAAS;YAC1C;YAEA,iBAAiB;YACjB,MAAMgB,cAAcL,IAAIG,KAAK,CAAC;YAC9B,IAAIE,aAAa;gBACf,MAAMjC,SAASiC,WAAW,CAAC,EAAE,CAACpB,IAAI;gBAClC,IAAIb,WAAW,KAAK;oBAClB6B,OAAO7B,MAAM,GAAG;wBAAC;qBAAI;gBACvB,OAAO;oBACL,MAAMkC,YAAYlC,OAAOmC,KAAK,CAAC,KAAK9B,GAAG,CAACC,CAAAA,IAAKA,EAAEO,IAAI;oBACnD,MAAMuB,kBAAkB,EAAE;oBAE1B,KAAK,MAAMb,SAASW,UAAW;wBAC7B,MAAMG,kBAAkB,IAAI,CAACf,iBAAiB,CAACC,OAAOM,OAAO9B,KAAK;wBAClE,IAAI,CAACsC,gBAAgB3B,KAAK,EAAE;4BAC1B,OAAO;gCAAEC,OAAO0B,gBAAgB1B,KAAK;4BAAC;wBACxC;wBACAyB,gBAAgBE,IAAI,CAACD,gBAAgBpB,SAAS;oBAChD;oBACAY,OAAO7B,MAAM,GAAGoC;gBAClB;YACF;YAEA,uBAAuB;YACvB,MAAMG,aAAaX,IAAIG,KAAK,CAAC;YAC7B,IAAIQ,YAAY;gBACd,MAAMC,cAAcD,UAAU,CAAC,EAAE,CAAC1B,IAAI;gBACtC,MAAM4B,cAAc,IAAI,CAACC,gBAAgB,CAACF,aAAaX,OAAO9B,KAAK;gBACnE,IAAI0C,YAAY9B,KAAK,EAAE;oBACrB,OAAO;wBAAEA,OAAO8B,YAAY9B,KAAK;oBAAC;gBACpC;gBACAkB,OAAOc,KAAK,GAAGF,YAAYG,UAAU;YACvC;YAEA,gBAAgB;YAChB,MAAMC,cAAcjB,IAAIkB,QAAQ,CAAC;YACjCjB,OAAOkB,KAAK,GAAG,EAAE;YACjB,KAAK,MAAMhB,SAASc,YAAa;gBAC/B,MAAMG,sBAAsB,IAAI,CAAC9B,iBAAiB,CAACa,KAAK,CAAC,EAAE;gBAC3D,IAAI,CAACiB,oBAAoBtC,KAAK,EAAE;oBAC9B,OAAO;wBAAEC,OAAOqC,oBAAoBrC,KAAK;oBAAC;gBAC5C;gBACAkB,OAAOkB,KAAK,CAACT,IAAI,CAAC;oBAChBvC,OAAOiD,oBAAoB/B,SAAS;oBACpCgC,IAAIlB,KAAK,CAAC,EAAE,CAAClB,IAAI;gBACnB;YACF;YAEA,OAAOgB;QACT,EAAE,OAAOlB,OAAO;YACd,OAAO;gBACLA,OAAO,CAAC,kCAAkC,EAAEA,iBAAiBuC,QAAQvC,MAAMwC,OAAO,GAAG,iBAAiB;YACxG;QACF;IACF;IAEA;;GAEC,GACD,AAAQT,iBAAiBF,WAAmB,EAAEzC,KAAc,EAG1D;QACA,MAAM6C,aAAsE,EAAE;QAE9E,IAAI;YACF,qCAAqC;YACrC,yCAAyC;YACzC,MAAMQ,QAAQZ,YAAYL,KAAK,CAAC;YAEhC,KAAK,MAAMkB,QAAQD,MAAO;gBACxB,MAAMrB,QAAQsB,KAAKtB,KAAK,CAAC;gBACzB,IAAIA,OAAO;oBACT,MAAMM,kBAAkB,IAAI,CAACf,iBAAiB,CAACS,KAAK,CAAC,EAAE,EAAEhC;oBACzD,IAAI,CAACsC,gBAAgB3B,KAAK,EAAE;wBAC1B,OAAO;4BAAEC,OAAO0B,gBAAgB1B,KAAK;wBAAC;oBACxC;oBAEAiC,WAAWN,IAAI,CAAC;wBACdf,OAAOc,gBAAgBpB,SAAS;wBAChCqC,UAAUvB,KAAK,CAAC,EAAE,CAAC3B,WAAW;wBAC9BmD,OAAOxB,KAAK,CAAC,EAAE,KAAK,MAAMyB,YAAYzB,KAAK,CAAC,EAAE;oBAChD;gBACF;YACF;YAEA,OAAO;gBAAEa;YAAW;QACtB,EAAE,OAAOjC,OAAO;YACd,OAAO;gBACLA,OAAO,CAAC,8BAA8B,EAAEA,iBAAiBuC,QAAQvC,MAAMwC,OAAO,GAAG,iBAAiB;YACpG;QACF;IACF;IAEA;;GAEC,GACDM,YAAY7B,GAAW,EAIrB;QACA,MAAMC,SAAc,CAAC;QAErB,IAAI;YACF,qBAAqB;YACrB,MAAMC,aAAaF,IAAIG,KAAK,CAAC;YAC7B,IAAID,YAAY;gBACd,MAAME,kBAAkB,IAAI,CAACd,iBAAiB,CAACY,UAAU,CAAC,EAAE;gBAC5D,IAAI,CAACE,gBAAgBtB,KAAK,EAAE;oBAC1B,OAAO;wBAAEC,OAAOqB,gBAAgBrB,KAAK;oBAAC;gBACxC;gBACAkB,OAAO9B,KAAK,GAAGiC,gBAAgBf,SAAS;YAC1C;YAEA,iBAAiB;YACjB,MAAMgB,cAAcL,IAAIG,KAAK,CAAC;YAC9B,IAAIE,aAAa;gBACf,MAAMC,YAAYD,WAAW,CAAC,EAAE,CAACE,KAAK,CAAC,KAAK9B,GAAG,CAACC,CAAAA,IAAKA,EAAEO,IAAI;gBAC3D,MAAMuB,kBAAkB,EAAE;gBAE1B,KAAK,MAAMb,SAASW,UAAW;oBAC7B,MAAMG,kBAAkB,IAAI,CAACf,iBAAiB,CAACC,OAAOM,OAAO9B,KAAK;oBAClE,IAAI,CAACsC,gBAAgB3B,KAAK,EAAE;wBAC1B,OAAO;4BAAEC,OAAO0B,gBAAgB1B,KAAK;wBAAC;oBACxC;oBACAyB,gBAAgBE,IAAI,CAACD,gBAAgBpB,SAAS;gBAChD;gBACAY,OAAO7B,MAAM,GAAGoC;YAClB;YAEA,OAAOP;QACT,EAAE,OAAOlB,OAAO;YACd,OAAO;gBACLA,OAAO,CAAC,kCAAkC,EAAEA,iBAAiBuC,QAAQvC,MAAMwC,OAAO,GAAG,iBAAiB;YACxG;QACF;IACF;IAEA;;GAEC,GACDO,YAAY9B,GAAW,EAKrB;QACA,MAAMC,SAAc,CAAC;QAErB,IAAI;YACF,qBAAqB;YACrB,MAAMC,aAAaF,IAAIG,KAAK,CAAC;YAC7B,IAAID,YAAY;gBACd,MAAME,kBAAkB,IAAI,CAACd,iBAAiB,CAACY,UAAU,CAAC,EAAE;gBAC5D,IAAI,CAACE,gBAAgBtB,KAAK,EAAE;oBAC1B,OAAO;wBAAEC,OAAOqB,gBAAgBrB,KAAK;oBAAC;gBACxC;gBACAkB,OAAO9B,KAAK,GAAGiC,gBAAgBf,SAAS;YAC1C;YAEA,qBAAqB;YACrB,MAAM0C,WAAW/B,IAAIG,KAAK,CAAC;YAC3B,IAAI4B,UAAU;gBACZ,MAAMC,WAAWD,QAAQ,CAAC,EAAE,CAACxB,KAAK,CAAC;gBACnC,MAAMC,kBAAkB,EAAE;gBAE1B,KAAK,MAAMiB,QAAQO,SAAU;oBAC3B,MAAMrC,QAAQ8B,KAAKlB,KAAK,CAAC,IAAI,CAAC,EAAE,CAACtB,IAAI;oBACrC,MAAMwB,kBAAkB,IAAI,CAACf,iBAAiB,CAACC,OAAOM,OAAO9B,KAAK;oBAClE,IAAI,CAACsC,gBAAgB3B,KAAK,EAAE;wBAC1B,OAAO;4BAAEC,OAAO0B,gBAAgB1B,KAAK;wBAAC;oBACxC;oBACAyB,gBAAgBE,IAAI,CAACD,gBAAgBpB,SAAS;gBAChD;gBACAY,OAAO7B,MAAM,GAAGoC;YAClB;YAEA,uBAAuB;YACvB,MAAMG,aAAaX,IAAIG,KAAK,CAAC;YAC7B,IAAIQ,YAAY;gBACd,MAAME,cAAc,IAAI,CAACC,gBAAgB,CAACH,UAAU,CAAC,EAAE,CAAC1B,IAAI,IAAIgB,OAAO9B,KAAK;gBAC5E,IAAI0C,YAAY9B,KAAK,EAAE;oBACrB,OAAO;wBAAEA,OAAO8B,YAAY9B,KAAK;oBAAC;gBACpC;gBACAkB,OAAOc,KAAK,GAAGF,YAAYG,UAAU;YACvC;YAEA,OAAOf;QACT,EAAE,OAAOlB,OAAO;YACd,OAAO;gBACLA,OAAO,CAAC,kCAAkC,EAAEA,iBAAiBuC,QAAQvC,MAAMwC,OAAO,GAAG,iBAAiB;YACxG;QACF;IACF;IAEA;;GAEC,GACDU,YAAYjC,GAAW,EAIrB;QACA,MAAMC,SAAc,CAAC;QAErB,IAAI;YACF,qBAAqB;YACrB,MAAMC,aAAaF,IAAIG,KAAK,CAAC;YAC7B,IAAID,YAAY;gBACd,MAAME,kBAAkB,IAAI,CAACd,iBAAiB,CAACY,UAAU,CAAC,EAAE;gBAC5D,IAAI,CAACE,gBAAgBtB,KAAK,EAAE;oBAC1B,OAAO;wBAAEC,OAAOqB,gBAAgBrB,KAAK;oBAAC;gBACxC;gBACAkB,OAAO9B,KAAK,GAAGiC,gBAAgBf,SAAS;YAC1C;YAEA,uBAAuB;YACvB,MAAMsB,aAAaX,IAAIG,KAAK,CAAC;YAC7B,IAAIQ,YAAY;gBACd,MAAME,cAAc,IAAI,CAACC,gBAAgB,CAACH,UAAU,CAAC,EAAE,CAAC1B,IAAI,IAAIgB,OAAO9B,KAAK;gBAC5E,IAAI0C,YAAY9B,KAAK,EAAE;oBACrB,OAAO;wBAAEA,OAAO8B,YAAY9B,KAAK;oBAAC;gBACpC;gBACAkB,OAAOc,KAAK,GAAGF,YAAYG,UAAU;YACvC;YAEA,OAAOf;QACT,EAAE,OAAOlB,OAAO;YACd,OAAO;gBACLA,OAAO,CAAC,kCAAkC,EAAEA,iBAAiBuC,QAAQvC,MAAMwC,OAAO,GAAG,iBAAiB;YACxG;QACF;IACF;AACF;AAEA;;;;;CAKC,GACD,OAAO,MAAMW;IACHC,OAAkB;IAClBC,OAAwC;IAEhD,YAAYA,MAA8B,CAAE;QAC1C,IAAI,CAACA,MAAM,GAAG;YACZtE,eAAesE,QAAQtE,iBAAiB,EAAE;YAC1CC,eAAeqE,QAAQrE,iBAAiB,CAAC;YACzCsE,gBAAgBD,QAAQC,kBAAkB;YAC1CC,WAAWF,QAAQE,aAAa;YAChCtE,YAAYoE,QAAQpE,eAAe;QACrC;QAEA,IAAI,CAACmE,MAAM,GAAG,IAAItE,UAChB,IAAI,CAACuE,MAAM,CAACtE,aAAa,EACzB,IAAI,CAACsE,MAAM,CAACrE,aAAa,EACzB,IAAI,CAACqE,MAAM,CAACpE,UAAU;IAE1B;IAEA;;GAEC,GACD,AAAQuE,cAAcvC,GAAW,EAAEwC,SAAgB,EAAE,EAAsC;QACzF,IAAI,CAACxC,OAAO,OAAOA,QAAQ,UAAU;YACnC,OAAO;gBAAElB,OAAO;gBAAOC,OAAO;YAAuC;QACvE;QAEA,IAAIiB,IAAIZ,MAAM,GAAG,IAAI,CAACgD,MAAM,CAACC,cAAc,EAAE;YAC3C,OAAO;gBACLvD,OAAO;gBACPC,OAAO,CAAC,oCAAoC,EAAE,IAAI,CAACqD,MAAM,CAACC,cAAc,CAAC,WAAW,CAAC;YACvF;QACF;QAEA,IAAI,CAACI,MAAMC,OAAO,CAACF,SAAS;YAC1B,OAAO;gBAAE1D,OAAO;gBAAOC,OAAO;YAA8B;QAC9D;QAEA,IAAIyD,OAAOpD,MAAM,GAAG,IAAI,CAACgD,MAAM,CAACE,SAAS,EAAE;YACzC,OAAO;gBACLxD,OAAO;gBACPC,OAAO,CAAC,8BAA8B,EAAE,IAAI,CAACqD,MAAM,CAACE,SAAS,EAAE;YACjE;QACF;QAEA,6CAA6C;QAC7C,mEAAmE;QACnE,MAAMK,oBAAoB;YACxB;YACA;YACA;YACA;YACA;YACA;YACA;YACA;SACD;QAED,iDAAiD;QACjD,MAAMnB,QAAQxB,IAAIO,KAAK,CAAC;QAExB,qFAAqF;QACrF,wDAAwD;QACxD,IAAK,IAAIqC,IAAI,GAAGA,IAAIpB,MAAMpC,MAAM,EAAEwD,IAAK;YACrC,MAAMnB,OAAOD,KAAK,CAACoB,EAAE;YAErB,6DAA6D;YAC7D,IAAIA,MAAM,GAAG;gBACX,4CAA4C;gBAC5C,IAAI,CAAC,wCAAwCzD,IAAI,CAACsC,OAAO;oBACvD,+DAA+D;oBAC/D,IAAIA,KAAKxC,IAAI,IAAI;wBACf,OAAO;4BACLH,OAAO;4BACPC,OAAO;wBACT;oBACF;gBACF;YACF,OAAO,IAAI6D,MAAMpB,MAAMpC,MAAM,GAAG,GAAG;gBACjC,gFAAgF;gBAChF,IAAI,4DAA4DD,IAAI,CAACsC,OAAO;oBAC1E,OAAO;wBACL3C,OAAO;wBACPC,OAAO;oBACT;gBACF;YACF,OAAO;gBACL,mDAAmD;gBACnD,kEAAkE;gBAClE,8BAA8B;gBAC9B,IAAI,gDAAgDI,IAAI,CAACsC,OAAO;oBAC9D,OAAO;wBACL3C,OAAO;wBACPC,OAAO;oBACT;gBACF;YACF;QACF;QAEA,mDAAmD;QACnD,KAAK,MAAM8D,WAAWF,kBAAmB;YACvC,qDAAqD;YACrD,IAAIE,QAAQC,MAAM,CAACC,QAAQ,CAAC,SAAS,2BAA2B5D,IAAI,CAACa,MAAM;gBACzE,yCAAyC;gBACzC,MAAMgD,gBAAgBhD,IAAIiD,SAAS,CAACjD,IAAIkD,OAAO,CAAC,OAAO;gBACvD,IAAI,2FAA2F/D,IAAI,CAAC6D,gBAAgB;oBAClH,OAAO;wBACLlE,OAAO;wBACPC,OAAO;oBACT;gBACF;YACF,OAAO,IAAI8D,QAAQ1D,IAAI,CAACa,QAAQ,CAACA,IAAI+C,QAAQ,CAAC,MAAM;gBAClD,wCAAwC;gBACxC,OAAO;oBACLjE,OAAO;oBACPC,OAAO;gBACT;YACF;QACF;QAEA,OAAO;YAAED,OAAO;QAAK;IACvB;IAEA;;GAEC,GACD,AAAQqE,qBAAqBC,OAAqB,EAAsC;QACtF,IAAI,CAACA,WAAW,OAAOA,YAAY,UAAU;YAC3C,OAAO;gBAAEtE,OAAO;gBAAOC,OAAO;YAAkC;QAClE;QAEA,IAAI,CAACqE,QAAQA,OAAO,IAAI,OAAOA,QAAQA,OAAO,KAAK,UAAU;YAC3D,OAAO;gBAAEtE,OAAO;gBAAOC,OAAO;YAAgD;QAChF;QAEA,MAAMsE,kBAAkB;YAAC;YAAO;YAAO;YAAQ;YAAW;YAAS;YAAQ;YAAO;YAAQ;SAAO;QACjG,IAAI,CAACA,gBAAgBN,QAAQ,CAACK,QAAQA,OAAO,CAACE,WAAW,KAAK;YAC5D,OAAO;gBACLxE,OAAO;gBACPC,OAAO,CAAC,eAAe,EAAEqE,QAAQA,OAAO,CAAC,2BAA2B,EAAEC,gBAAgBE,IAAI,CAAC,OAAO;YACpG;QACF;QAEA,IAAIH,QAAQI,GAAG,IAAI,OAAOJ,QAAQI,GAAG,KAAK,UAAU;YAClD,OAAO;gBAAE1E,OAAO;gBAAOC,OAAO;YAA6B;QAC7D;QAEA,IAAIqE,QAAQhF,MAAM,IAAI,OAAOgF,QAAQhF,MAAM,KAAK,UAAU;YACxD,OAAO;gBAAEU,OAAO;gBAAOC,OAAO;YAAiC;QACjE;QAEA,IAAIqE,QAAQK,IAAI,IAAI,CAAChB,MAAMC,OAAO,CAACU,QAAQK,IAAI,GAAG;YAChD,OAAO;gBAAE3E,OAAO;gBAAOC,OAAO;YAA8B;QAC9D;QAEA,OAAO;YAAED,OAAO;QAAK;IACvB;IAEA;;GAEC,GACD4E,oBAAoB1D,GAAW,EAAEwC,SAAgB,EAAE,EAAqB;QACtE,MAAMmB,YAAYC,KAAKC,GAAG;QAC1B,MAAMC,WAAqB,EAAE;QAE7B,IAAI;YACF,kBAAkB;YAClB,MAAMC,kBAAkB,IAAI,CAACxB,aAAa,CAACvC,KAAKwC;YAChD,IAAI,CAACuB,gBAAgBjF,KAAK,EAAE;gBAC1B,MAAM,IAAInB,cACRC,UAAUoG,iBAAiB,EAC3BD,gBAAgBhF,KAAK,IAAI,iBACzB;oBAAEiB;oBAAKiE,YAAYzB,OAAOpD,MAAM;gBAAC;YAErC;YAEA,uBAAuB;YACvB,MAAM8E,YAAY,IAAI,CAACC,YAAY,CAACnE;YAEpC,IAAIoE;YACJ,IAAIC,qBAAqB3G,YAAY4G,KAAK;YAE1C,OAAQJ;gBACN,KAAK;oBAAU;wBACb,MAAMK,eAAe,IAAI,CAACpC,MAAM,CAACpC,WAAW,CAACC;wBAE7C,IAAIuE,aAAaxF,KAAK,EAAE;4BACtB,MAAM,IAAIpB,cACRC,UAAU4G,WAAW,EACrB,CAAC,kCAAkC,EAAED,aAAaxF,KAAK,EAAE,EACzD;gCAAEiB;4BAAI;wBAEV;wBAEA,wCAAwC;wBACxC,IAAIuE,aAAapD,KAAK,IAAIoD,aAAapD,KAAK,CAAC/B,MAAM,GAAG,GAAG;4BACvD0E,SAASpD,IAAI,CAAC;4BACd2D,qBAAqB3G,YAAY+G,QAAQ;wBAC3C;wBAEA,oCAAoC;wBACpC,IAAIF,aAAaxD,KAAK,IAAIwD,aAAaxD,KAAK,CAAC3B,MAAM,GAAG,GAAG;4BACvD,MAAMsF,cAAcH,aAAaxD,KAAK,CAAC4D,IAAI,CAACC,CAAAA,IAAKA,EAAEjF,KAAK,KAAK;4BAC7D,IAAI+E,aAAa;gCACf,MAAMG,WAAWrC,MAAM,CAAC,EAAE;gCAE1B,qBAAqB;gCACrB,IAAI,OAAOqC,aAAa,YAAY,OAAOA,aAAa,UAAU;oCAChE,MAAM,IAAIlH,cACRC,UAAUoG,iBAAiB,EAC3B,8CACA;wCAAEc,YAAY,OAAOD;oCAAS;gCAElC;gCAEA,MAAME,WAAW,GAAGR,aAAapG,KAAK,CAAC,CAAC,EAAE0G,UAAU;gCAEpDT,eAAe;oCACbhB,SAASmB,aAAanG,MAAM,EAAE,CAAC,EAAE,KAAK,MAAM,YAAY;oCACxDoF,KAAKuB;gCACP;4BACF;wBACF;wBACA;oBACF;gBAEA,KAAK;oBAAU;wBACb,MAAMC,eAAe,IAAI,CAAC7C,MAAM,CAACN,WAAW,CAAC7B;wBAE7C,IAAIgF,aAAajG,KAAK,EAAE;4BACtB,MAAM,IAAIpB,cACRC,UAAU4G,WAAW,EACrB,CAAC,kCAAkC,EAAEQ,aAAajG,KAAK,EAAE,EACzD;gCAAEiB;4BAAI;wBAEV;wBAEA,IAAIgF,aAAa7G,KAAK,IAAI6G,aAAa5G,MAAM,EAAE;4BAC7C,MAAM6G,UAAUzC,MAAM,CAAC,EAAE;4BAEzB,qBAAqB;4BACrB,IAAI,OAAOyC,YAAY,YAAY,OAAOA,YAAY,UAAU;gCAC9D,MAAM,IAAItH,cACRC,UAAUoG,iBAAiB,EAC3B,8CACA;oCAAEc,YAAY,OAAOG;gCAAQ;4BAEjC;4BAEA,MAAMF,WAAW,GAAGC,aAAa7G,KAAK,CAAC,CAAC,EAAE8G,SAAS;4BAEnD,0CAA0C;4BAC1C,MAAM7G,SAA8B,CAAC;4BACrC,IAAK,IAAIwE,IAAI,GAAGA,IAAIoC,aAAa5G,MAAM,CAACgB,MAAM,IAAIwD,IAAIJ,OAAOpD,MAAM,EAAEwD,IAAK;gCACxE,2DAA2D;gCAC3D,MAAMkC,aAAatC,MAAM,CAACI,EAAE;gCAC5B,IAAI,OAAOkC,eAAe,YAAYA,eAAe,MAAM;oCACzD,MAAM,IAAInH,cACRC,UAAUoG,iBAAiB,EAC3B,uEACA;wCAAEkB,YAAYtC;wCAAGuC,WAAW,OAAOL;oCAAW;gCAElD;gCACA1G,MAAM,CAAC4G,aAAa5G,MAAM,CAACwE,EAAE,CAAC,GAAGkC;4BACnC;4BAEAV,eAAe;gCACbhB,SAAS;gCACTI,KAAKuB;gCACL3G;4BACF;wBACF;wBACA;oBACF;gBAEA,KAAK;oBAAU;wBACb,MAAMgH,eAAe,IAAI,CAACjD,MAAM,CAACL,WAAW,CAAC9B;wBAE7C,IAAIoF,aAAarG,KAAK,EAAE;4BACtB,MAAM,IAAIpB,cACRC,UAAU4G,WAAW,EACrB,CAAC,kCAAkC,EAAEY,aAAarG,KAAK,EAAE,EACzD;gCAAEiB;4BAAI;wBAEV;wBAEA,IAAIoF,aAAajH,KAAK,IAAIiH,aAAarE,KAAK,EAAE;4BAC5C,MAAM2D,cAAcU,aAAarE,KAAK,CAAC4D,IAAI,CAACC,CAAAA,IAAKA,EAAEjF,KAAK,KAAK;4BAC7D,IAAI+E,aAAa;gCACf,MAAMG,WAAWrC,MAAM,CAACA,OAAOpD,MAAM,GAAG,EAAE;gCAE1C,qBAAqB;gCACrB,IAAI,OAAOyF,aAAa,YAAY,OAAOA,aAAa,UAAU;oCAChE,MAAM,IAAIlH,cACRC,UAAUoG,iBAAiB,EAC3B,8CACA;wCAAEc,YAAY,OAAOD;oCAAS;gCAElC;gCAEA,MAAME,WAAW,GAAGK,aAAajH,KAAK,CAAC,CAAC,EAAE0G,UAAU;gCAEpDT,eAAe;oCACbhB,SAAS;oCACTI,KAAKuB;oCACLtB,MAAMjB,OAAO6C,KAAK,CAAC,GAAG,CAAC;gCACzB;4BACF;wBACF;wBACA;oBACF;gBAEA,KAAK;oBAAU;wBACb,MAAMC,eAAe,IAAI,CAACnD,MAAM,CAACF,WAAW,CAACjC;wBAE7C,IAAIsF,aAAavG,KAAK,EAAE;4BACtB,MAAM,IAAIpB,cACRC,UAAU4G,WAAW,EACrB,CAAC,kCAAkC,EAAEc,aAAavG,KAAK,EAAE,EACzD;gCAAEiB;4BAAI;wBAEV;wBAEA,IAAIsF,aAAanH,KAAK,IAAImH,aAAavE,KAAK,EAAE;4BAC5C,MAAM2D,cAAcY,aAAavE,KAAK,CAAC4D,IAAI,CAACC,CAAAA,IAAKA,EAAEjF,KAAK,KAAK;4BAC7D,IAAI+E,aAAa;gCACf,MAAMG,WAAWrC,MAAM,CAAC,EAAE;gCAE1B,qBAAqB;gCACrB,IAAI,OAAOqC,aAAa,YAAY,OAAOA,aAAa,UAAU;oCAChE,MAAM,IAAIlH,cACRC,UAAUoG,iBAAiB,EAC3B,8CACA;wCAAEc,YAAY,OAAOD;oCAAS;gCAElC;gCAEA,MAAME,WAAW,GAAGO,aAAanH,KAAK,CAAC,CAAC,EAAE0G,UAAU;gCAEpDT,eAAe;oCACbhB,SAAS;oCACTI,KAAKuB;gCACP;4BACF;wBACF;wBACA;oBACF;gBAEA;oBACEjB,SAASpD,IAAI,CAAC,CAAC,4BAA4B,EAAEwD,WAAW;oBACxDG,qBAAqB3G,YAAY+G,QAAQ;YAC7C;YAEA,MAAMc,gBAAgB3B,KAAKC,GAAG,KAAKF;YAEnC,IAAI4B,gBAAgB,IAAI;gBACtBzB,SAASpD,IAAI,CAAC,CAAC,iBAAiB,EAAE6E,cAAc,kBAAkB,CAAC;YACrE;YAEA,OAAO;gBACLC,SAAS,CAAC,CAACpB;gBACXA;gBACAmB;gBACAlB;gBACAP,UAAUA,SAAS1E,MAAM,GAAG,IAAI0E,WAAWlC;YAC7C;QACF,EAAE,OAAO7C,OAAO;YACd,MAAMwG,gBAAgB3B,KAAKC,GAAG,KAAKF;YAEnC,MAAMpC,UAAUxC,iBAAiBpB,gBAC7BoB,MAAMwC,OAAO,GACbxC,iBAAiBuC,QACfvC,MAAMwC,OAAO,GACb;YAEN,OAAO;gBACLiE,SAAS;gBACTD;gBACAzB,UAAU;oBAAC,CAAC,oBAAoB,EAAEvC,SAAS;iBAAC;YAC9C;QACF;IACF;IAEA;;GAEC,GACDkE,oBAAoBrC,OAAqB,EAAqB;QAC5D,MAAMO,YAAYC,KAAKC,GAAG;QAC1B,MAAMC,WAAqB,EAAE;QAE7B,IAAI;YACF,yBAAyB;YACzB,MAAM4B,oBAAoB,IAAI,CAACvC,oBAAoB,CAACC;YACpD,IAAI,CAACsC,kBAAkB5G,KAAK,EAAE;gBAC5B,MAAM,IAAInB,cACRC,UAAUoG,iBAAiB,EAC3B0B,kBAAkB3G,KAAK,IAAI,yBAC3B;oBAAEqE,SAASA,QAAQA,OAAO;gBAAC;YAE/B;YAEA,IAAIuC;YACJ,IAAIC,YAAmB,EAAE;YAEzB,0CAA0C;YAC1C,MAAMC,WAAWzC,QAAQI,GAAG,EAAEjD,MAAM,QAAQ,EAAE;YAC9C,MAAMpC,QAAQ0H,QAAQ,CAAC,EAAE,IAAI;YAC7B,MAAMC,KAAKD,QAAQ,CAAC,EAAE;YAEtB,sBAAsB;YACtB,MAAMzF,kBAAkB,IAAI,CAAC+B,MAAM,CAAC,oBAAoB,IACrD,CAAA,CAAC4D,IAAe,CAAA;oBAAEjH,OAAO;oBAAMO,WAAW0G;gBAAE,CAAA,CAAC;YAEhD,6DAA6D;YAC7D,MAAMC,eAAe;YACrB,IAAI,CAACA,aAAa7G,IAAI,CAAChB,QAAQ;gBAC7B,MAAM,IAAIR,cACRC,UAAUoG,iBAAiB,EAC3B,mCACA;oBAAER,KAAKJ,QAAQI,GAAG;gBAAC;YAEvB;YAEA,OAAQJ,QAAQA,OAAO,CAACE,WAAW;gBACjC,KAAK;gBACL,KAAK;gBACL,KAAK;oBAAW;wBACd,mCAAmC;wBACnCqC,WAAW,CAAC,cAAc,EAAExH,MAAM,aAAa,CAAC;wBAChDyH,YAAY;4BAACE;yBAAG;wBAChB;oBACF;gBAEA,KAAK;gBACL,KAAK;oBAAS;wBACZ,6DAA6D;wBAC7D,IAAI1C,QAAQhF,MAAM,EAAE;4BAClB,MAAMA,SAASC,OAAO4H,IAAI,CAAC7C,QAAQhF,MAAM;4BACzC,MAAM8H,eAAe9H,OAAOK,GAAG,CAAC,IAAM,KAAK8E,IAAI,CAAC;4BAChDoC,WAAW,CAAC,YAAY,EAAExH,MAAM,EAAE,EAAEC,OAAOmF,IAAI,CAAC,MAAM,UAAU,EAAE2C,aAAa,CAAC,CAAC;4BACjFN,YAAYvH,OAAO8H,MAAM,CAAC/C,QAAQhF,MAAM;4BAExC,qCAAqC;4BACrC,IAAK,IAAIwE,IAAI,GAAGA,IAAIgD,UAAUxG,MAAM,EAAEwD,IAAK;gCACzC,IAAI,OAAOgD,SAAS,CAAChD,EAAE,KAAK,YAAYgD,SAAS,CAAChD,EAAE,KAAK,MAAM;oCAC7D,MAAM,IAAIjF,cACRC,UAAUoG,iBAAiB,EAC3B,uCACA;wCAAEkB,YAAYtC;oCAAE;gCAEpB;4BACF;wBACF;wBACA;oBACF;gBAEA,KAAK;oBAAQ;wBACX,0CAA0C;wBAC1C,IAAIQ,QAAQK,IAAI,IAAIL,QAAQK,IAAI,CAACrE,MAAM,GAAG,GAAG;4BAC3C,MAAMO,QAAQyD,QAAQK,IAAI,CAAC,EAAE;4BAE7B,sBAAsB;4BACtB,IAAI,OAAO9D,UAAU,YAAY,CAAC,2BAA2BR,IAAI,CAACQ,QAAQ;gCACxE,MAAM,IAAIhC,cACRC,UAAUoG,iBAAiB,EAC3B,sCACA;oCAAErE;gCAAM;4BAEZ;4BAEAgG,WAAW,CAAC,OAAO,EAAExH,MAAM,KAAK,EAAEwB,MAAM,iBAAiB,CAAC;4BAC1DiG,YAAY;gCAACxC,QAAQK,IAAI,CAAC,EAAE;gCAAEqC;6BAAG;4BAEjC,kBAAkB;4BAClB,IAAK,IAAIlD,IAAI,GAAGA,IAAIgD,UAAUxG,MAAM,EAAEwD,IAAK;gCACzC,IAAI,OAAOgD,SAAS,CAAChD,EAAE,KAAK,YAAYgD,SAAS,CAAChD,EAAE,KAAK,MAAM;oCAC7D,MAAM,IAAIjF,cACRC,UAAUoG,iBAAiB,EAC3B,uCACA;wCAAEkB,YAAYtC;oCAAE;gCAEpB;4BACF;wBACF;wBACA;oBACF;gBAEA,KAAK;oBAAO;wBACV,iCAAiC;wBACjC+C,WAAW,CAAC,YAAY,EAAExH,MAAM,aAAa,CAAC;wBAC9CyH,YAAY;4BAACE;yBAAG;wBAChB;oBACF;gBAEA;oBACEhC,SAASpD,IAAI,CAAC,CAAC,2BAA2B,EAAE0C,QAAQA,OAAO,EAAE;YACjE;YAEA,MAAMmC,gBAAgB3B,KAAKC,GAAG,KAAKF;YAEnC,IAAI4B,gBAAgB,IAAI;gBACtBzB,SAASpD,IAAI,CAAC,CAAC,iBAAiB,EAAE6E,cAAc,kBAAkB,CAAC;YACrE;YAEA,OAAO;gBACLC,SAAS,CAAC,CAACG;gBACXA;gBACAC;gBACAL;gBACAzB,UAAUA,SAAS1E,MAAM,GAAG,IAAI0E,WAAWlC;YAC7C;QACF,EAAE,OAAO7C,OAAO;YACd,MAAMwG,gBAAgB3B,KAAKC,GAAG,KAAKF;YAEnC,MAAMpC,UAAUxC,iBAAiBpB,gBAC7BoB,MAAMwC,OAAO,GACbxC,iBAAiBuC,QACfvC,MAAMwC,OAAO,GACb;YAEN,OAAO;gBACLiE,SAAS;gBACTD;gBACAzB,UAAU;oBAAC,CAAC,oBAAoB,EAAEvC,SAAS;iBAAC;YAC9C;QACF;IACF;IAEA;;GAEC,GACD6E,cAAcC,OAAqB,EAA+C;QAChF,MAAMpG,SAAsD;YAC1DqG,SAAS,EAAE;YACXC,iBAAiB,EAAE;QACrB;QAEA,wCAAwC;QACxC,IAAIF,QAAQG,OAAO,EAAE;YACnB,MAAMC,cAAcJ,QAAQG,OAAO,CAAC/H,GAAG,CAACC,CAAAA,IAAKgI,OAAOhI,EAAEiB,KAAK;YAC3DM,OAAOqG,OAAO,GAAGG;YACjBxG,OAAO0G,OAAO,GAAGF;YACjBxG,OAAOsG,eAAe,EAAE7F,KAAK,CAAC,4BAA4B,EAAE+F,YAAYlD,IAAI,CAAC,OAAO;QACtF;QAEA,sBAAsB;QACtB,IAAIqD,OAAO;QACX,IAAIP,QAAQlF,KAAK,EAAE;YACjByF,QAAQP,QAAQlF,KAAK,CAAC/B,MAAM,GAAG,IAAI,sBAAsB;QAC3D;QACA,IAAIiH,QAAQG,OAAO,EAAE;YACnBI,QAAQP,QAAQG,OAAO,CAACpH,MAAM,GAAG;QACnC;QACAa,OAAO4G,aAAa,GAAGD;QAEvB,uCAAuC;QACvC,IAAIP,QAAQlF,KAAK,IAAIkF,QAAQlF,KAAK,CAAC/B,MAAM,GAAG,GAAG;YAC7Ca,OAAOsG,eAAe,EAAE7F,KAAK;QAC/B;QAEA,IAAI2F,QAAQG,OAAO,IAAIH,QAAQG,OAAO,CAACpH,MAAM,GAAG,GAAG;YACjDa,OAAOsG,eAAe,EAAE7F,KAAK;QAC/B;QAEA,OAAOT;IACT;IAEA;;GAEC,GACD6G,iBAAiBT,OAAqB,EAAe;QACnD,kCAAkC;QAClC,IAAIA,QAAQ7C,GAAG,IAAI,CAAC6C,QAAQlF,KAAK,EAAE;YACjC,OAAOzD,YAAY4G,KAAK;QAC1B;QAEA,0CAA0C;QAC1C,IAAI+B,QAAQlF,KAAK,IAAIkF,QAAQlF,KAAK,CAAC/B,MAAM,GAAG,GAAG;YAC7C,OAAO1B,YAAY+G,QAAQ;QAC7B;QAEA,6BAA6B;QAC7B,IAAI4B,QAAQU,QAAQ,KAAK,WAAWV,QAAQU,QAAQ,KAAK,WAAW;YAClE,OAAOrJ,YAAY4G,KAAK;QAC1B;QAEA,+BAA+B;QAC/B,IAAI+B,QAAQU,QAAQ,KAAK,YAAY;YACnC,OAAOrJ,YAAYsJ,MAAM;QAC3B;QAEA,4CAA4C;QAC5C,OAAOtJ,YAAY+G,QAAQ;IAC7B;IAEA;;GAEC,GACD,AAAQN,aAAanE,GAAW,EAAU;QACxC,MAAMhB,UAAUgB,IAAIf,IAAI,GAAGqE,WAAW;QAEtC,IAAItE,QAAQiI,UAAU,CAAC,WAAW,OAAO;QACzC,IAAIjI,QAAQiI,UAAU,CAAC,WAAW,OAAO;QACzC,IAAIjI,QAAQiI,UAAU,CAAC,WAAW,OAAO;QACzC,IAAIjI,QAAQiI,UAAU,CAAC,WAAW,OAAO;QAEzC,OAAO;IACT;AACF"}
|
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue Recovery System
|
|
3
|
+
*
|
|
4
|
+
* Provides failure detection, dead letter queue management, and automatic recovery
|
|
5
|
+
* for Redis queue operations. Handles coordinator crashes and message reprocessing.
|
|
6
|
+
* Part of Task 3.4: Redis Queue Consistency & Recovery (Integration Standardization Sprint 3)
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Dead letter queue for failed messages
|
|
10
|
+
* - Automatic retry with exponential backoff
|
|
11
|
+
* - Failure detection (tasks not processed within timeout)
|
|
12
|
+
* - Coordinator crash recovery procedure
|
|
13
|
+
* - Message reprocessing safeguards
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* const recovery = new QueueRecovery(queueManager);
|
|
17
|
+
*
|
|
18
|
+
* // Start monitoring for stuck messages
|
|
19
|
+
* recovery.startMonitoring();
|
|
20
|
+
*
|
|
21
|
+
* // Recover from coordinator crash
|
|
22
|
+
* await recovery.recoverFromCrash();
|
|
23
|
+
*
|
|
24
|
+
* // Process dead letter queue
|
|
25
|
+
* const reprocessed = await recovery.reprocessDeadLetters();
|
|
26
|
+
*/ import { createLogger } from './logging.js';
|
|
27
|
+
import { createError, ErrorCode, isRetryableError } from './errors.js';
|
|
28
|
+
import { sleep } from './retry.js';
|
|
29
|
+
const logger = createLogger('queue-recovery');
|
|
30
|
+
/**
|
|
31
|
+
* Default recovery options
|
|
32
|
+
*/ const DEFAULT_RECOVERY_OPTIONS = {
|
|
33
|
+
maxRetries: 3,
|
|
34
|
+
baseRetryDelayMs: 1000,
|
|
35
|
+
maxRetryDelayMs: 60000,
|
|
36
|
+
processingTimeoutMs: 300000,
|
|
37
|
+
monitoringIntervalMs: 60000,
|
|
38
|
+
deadLetterQueue: 'dlq',
|
|
39
|
+
autoReprocess: false
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Queue Recovery System
|
|
43
|
+
*
|
|
44
|
+
* Handles failure detection, retry, and recovery for queue operations.
|
|
45
|
+
*/ export class QueueRecovery {
|
|
46
|
+
queueManager;
|
|
47
|
+
redis;
|
|
48
|
+
options;
|
|
49
|
+
monitoringTimer = null;
|
|
50
|
+
stats = {
|
|
51
|
+
totalRecovered: 0,
|
|
52
|
+
totalDeadLettered: 0,
|
|
53
|
+
totalReprocessed: 0,
|
|
54
|
+
totalStuckDetected: 0
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Create a new QueueRecovery instance
|
|
58
|
+
*
|
|
59
|
+
* @param queueManager - Queue manager instance
|
|
60
|
+
* @param redis - Redis client instance
|
|
61
|
+
* @param options - Recovery options
|
|
62
|
+
*/ constructor(queueManager, redis, options = {}){
|
|
63
|
+
this.queueManager = queueManager;
|
|
64
|
+
this.redis = redis;
|
|
65
|
+
this.options = {
|
|
66
|
+
...DEFAULT_RECOVERY_OPTIONS,
|
|
67
|
+
...options
|
|
68
|
+
};
|
|
69
|
+
logger.info('QueueRecovery initialized', {
|
|
70
|
+
maxRetries: this.options.maxRetries,
|
|
71
|
+
processingTimeoutMs: this.options.processingTimeoutMs,
|
|
72
|
+
deadLetterQueue: this.options.deadLetterQueue
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Retry message processing with exponential backoff
|
|
77
|
+
*
|
|
78
|
+
* @param message - Message to retry
|
|
79
|
+
* @param processFn - Function to process message
|
|
80
|
+
* @returns Processing result
|
|
81
|
+
*/ async retryWithBackoff(message, processFn) {
|
|
82
|
+
const maxAttempts = this.options.maxRetries;
|
|
83
|
+
let attempt = message.deliveryAttempts;
|
|
84
|
+
while(attempt <= maxAttempts){
|
|
85
|
+
try {
|
|
86
|
+
logger.debug('Processing message with retry', {
|
|
87
|
+
messageId: message.id,
|
|
88
|
+
attempt,
|
|
89
|
+
maxAttempts
|
|
90
|
+
});
|
|
91
|
+
const result = await processFn(message.payload);
|
|
92
|
+
if (attempt > 1) {
|
|
93
|
+
this.stats.totalRecovered++;
|
|
94
|
+
logger.info('Message processed successfully after retry', {
|
|
95
|
+
messageId: message.id,
|
|
96
|
+
attempt
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
102
|
+
logger.warn('Message processing failed', {
|
|
103
|
+
messageId: message.id,
|
|
104
|
+
attempt,
|
|
105
|
+
error: err.message
|
|
106
|
+
});
|
|
107
|
+
// Check if we should retry
|
|
108
|
+
if (!isRetryableError(err) || attempt >= maxAttempts) {
|
|
109
|
+
logger.error('Message processing failed permanently', err, {
|
|
110
|
+
messageId: message.id,
|
|
111
|
+
attempt,
|
|
112
|
+
retryable: isRetryableError(err)
|
|
113
|
+
});
|
|
114
|
+
// Send to dead letter queue
|
|
115
|
+
await this.sendToDeadLetter(message, err.message);
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
118
|
+
// Calculate backoff delay
|
|
119
|
+
const delay = this.calculateBackoffDelay(attempt);
|
|
120
|
+
logger.debug('Retrying message after delay', {
|
|
121
|
+
messageId: message.id,
|
|
122
|
+
attempt,
|
|
123
|
+
delayMs: delay
|
|
124
|
+
});
|
|
125
|
+
// Wait before retry
|
|
126
|
+
await sleep(delay);
|
|
127
|
+
attempt++;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Should never reach here, but TypeScript needs it
|
|
131
|
+
throw createError(ErrorCode.RETRY_EXHAUSTED, 'Message processing retry exhausted', {
|
|
132
|
+
messageId: message.id,
|
|
133
|
+
attempts: attempt
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Send message to dead letter queue
|
|
138
|
+
*
|
|
139
|
+
* @param message - Message to dead letter
|
|
140
|
+
* @param reason - Failure reason
|
|
141
|
+
*/ async sendToDeadLetter(message, reason) {
|
|
142
|
+
try {
|
|
143
|
+
const metadata = {
|
|
144
|
+
originalQueue: message.queue,
|
|
145
|
+
failureReason: reason,
|
|
146
|
+
retryAttempts: message.deliveryAttempts,
|
|
147
|
+
deadLetteredAt: new Date(),
|
|
148
|
+
originalMetadata: message.metadata
|
|
149
|
+
};
|
|
150
|
+
// Enqueue to dead letter queue
|
|
151
|
+
await this.queueManager.enqueue(this.options.deadLetterQueue, message.payload, {
|
|
152
|
+
deduplicate: false,
|
|
153
|
+
metadata
|
|
154
|
+
});
|
|
155
|
+
this.stats.totalDeadLettered++;
|
|
156
|
+
logger.info('Message sent to dead letter queue', {
|
|
157
|
+
messageId: message.id,
|
|
158
|
+
originalQueue: message.queue,
|
|
159
|
+
reason,
|
|
160
|
+
retryAttempts: message.deliveryAttempts
|
|
161
|
+
});
|
|
162
|
+
} catch (error) {
|
|
163
|
+
logger.error('Failed to send message to DLQ', error instanceof Error ? error : new Error(String(error)), {
|
|
164
|
+
messageId: message.id
|
|
165
|
+
});
|
|
166
|
+
throw createError(ErrorCode.DB_QUERY_FAILED, 'Failed to send message to dead letter queue', {
|
|
167
|
+
messageId: message.id
|
|
168
|
+
}, error instanceof Error ? error : undefined);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Reprocess messages from dead letter queue
|
|
173
|
+
*
|
|
174
|
+
* @param processFn - Function to process dead letter messages
|
|
175
|
+
* @param maxMessages - Maximum number of messages to reprocess (default: 100)
|
|
176
|
+
* @returns Number of messages reprocessed
|
|
177
|
+
*/ async reprocessDeadLetters(processFn, maxMessages = 100) {
|
|
178
|
+
let reprocessedCount = 0;
|
|
179
|
+
try {
|
|
180
|
+
logger.info('Starting dead letter reprocessing', {
|
|
181
|
+
maxMessages
|
|
182
|
+
});
|
|
183
|
+
for(let i = 0; i < maxMessages; i++){
|
|
184
|
+
// Dequeue from dead letter queue
|
|
185
|
+
const message = await this.queueManager.dequeue(this.options.deadLetterQueue, {
|
|
186
|
+
timeout: 0
|
|
187
|
+
});
|
|
188
|
+
if (!message) {
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
const metadata = message.metadata;
|
|
193
|
+
// Process message
|
|
194
|
+
await processFn(message.payload, metadata);
|
|
195
|
+
// Acknowledge successful processing
|
|
196
|
+
await this.queueManager.acknowledge(message.id);
|
|
197
|
+
reprocessedCount++;
|
|
198
|
+
this.stats.totalReprocessed++;
|
|
199
|
+
logger.debug('Dead letter message reprocessed', {
|
|
200
|
+
messageId: message.id,
|
|
201
|
+
originalQueue: metadata.originalQueue
|
|
202
|
+
});
|
|
203
|
+
} catch (error) {
|
|
204
|
+
// Reject message (will stay in DLQ or be retried based on options)
|
|
205
|
+
await this.queueManager.reject(message.id, {
|
|
206
|
+
retry: false,
|
|
207
|
+
error: error instanceof Error ? error.message : String(error)
|
|
208
|
+
});
|
|
209
|
+
logger.error('Failed to reprocess dead letter', error instanceof Error ? error : new Error(String(error)), {
|
|
210
|
+
messageId: message.id
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
logger.info('Dead letter reprocessing complete', {
|
|
215
|
+
reprocessedCount
|
|
216
|
+
});
|
|
217
|
+
return reprocessedCount;
|
|
218
|
+
} catch (error) {
|
|
219
|
+
logger.error('Dead letter reprocessing failed', error instanceof Error ? error : new Error(String(error)));
|
|
220
|
+
throw createError(ErrorCode.DB_QUERY_FAILED, 'Failed to reprocess dead letters', {
|
|
221
|
+
reprocessedCount
|
|
222
|
+
}, error instanceof Error ? error : undefined);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Detect and recover stuck messages (messages in processing longer than timeout)
|
|
227
|
+
*
|
|
228
|
+
* @param queue - Queue name to check
|
|
229
|
+
* @returns Number of stuck messages recovered
|
|
230
|
+
*/ async recoverStuckMessages(queue) {
|
|
231
|
+
try {
|
|
232
|
+
const processingKey = `queue:${queue}:processing`;
|
|
233
|
+
const queueKey = `queue:${queue}`;
|
|
234
|
+
// Get all messages in processing
|
|
235
|
+
const processingMessages = await this.redis.lRange(processingKey, 0, -1);
|
|
236
|
+
let recoveredCount = 0;
|
|
237
|
+
const now = Date.now();
|
|
238
|
+
for (const messageData of processingMessages){
|
|
239
|
+
const message = JSON.parse(messageData);
|
|
240
|
+
// Check if message is stuck (processing longer than timeout)
|
|
241
|
+
if (message.dequeuedAt) {
|
|
242
|
+
const processingTime = now - new Date(message.dequeuedAt).getTime();
|
|
243
|
+
if (processingTime > this.options.processingTimeoutMs) {
|
|
244
|
+
logger.warn('Stuck message detected', {
|
|
245
|
+
messageId: message.id,
|
|
246
|
+
queue,
|
|
247
|
+
processingTimeMs: processingTime,
|
|
248
|
+
timeoutMs: this.options.processingTimeoutMs
|
|
249
|
+
});
|
|
250
|
+
this.stats.totalStuckDetected++;
|
|
251
|
+
// Check if message has exceeded max retries
|
|
252
|
+
if (message.deliveryAttempts >= this.options.maxRetries) {
|
|
253
|
+
// Send to dead letter queue
|
|
254
|
+
await this.sendToDeadLetter(message, `Message stuck in processing for ${processingTime}ms (exceeded max retries)`);
|
|
255
|
+
// Remove from processing
|
|
256
|
+
await this.redis.lRem(processingKey, 1, messageData);
|
|
257
|
+
logger.info('Stuck message sent to DLQ', {
|
|
258
|
+
messageId: message.id,
|
|
259
|
+
deliveryAttempts: message.deliveryAttempts
|
|
260
|
+
});
|
|
261
|
+
} else {
|
|
262
|
+
// Re-enqueue for retry
|
|
263
|
+
message.deliveryAttempts++;
|
|
264
|
+
message.metadata = {
|
|
265
|
+
...message.metadata,
|
|
266
|
+
recoveredAt: new Date().toISOString(),
|
|
267
|
+
recoveryReason: 'Stuck in processing'
|
|
268
|
+
};
|
|
269
|
+
// Remove from processing and add back to queue
|
|
270
|
+
await this.redis.lRem(processingKey, 1, messageData);
|
|
271
|
+
await this.redis.rPush(queueKey, JSON.stringify(message));
|
|
272
|
+
recoveredCount++;
|
|
273
|
+
this.stats.totalRecovered++;
|
|
274
|
+
logger.info('Stuck message re-enqueued', {
|
|
275
|
+
messageId: message.id,
|
|
276
|
+
deliveryAttempts: message.deliveryAttempts
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (recoveredCount > 0) {
|
|
283
|
+
this.stats.lastRecoveryAt = new Date();
|
|
284
|
+
}
|
|
285
|
+
logger.debug('Stuck message recovery complete', {
|
|
286
|
+
queue,
|
|
287
|
+
recoveredCount,
|
|
288
|
+
totalStuckDetected: this.stats.totalStuckDetected
|
|
289
|
+
});
|
|
290
|
+
return recoveredCount;
|
|
291
|
+
} catch (error) {
|
|
292
|
+
logger.error('Failed to recover stuck messages', error instanceof Error ? error : new Error(String(error)), {
|
|
293
|
+
queue
|
|
294
|
+
});
|
|
295
|
+
throw createError(ErrorCode.DB_QUERY_FAILED, 'Failed to recover stuck messages', {
|
|
296
|
+
queue
|
|
297
|
+
}, error instanceof Error ? error : undefined);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Recover from coordinator crash
|
|
302
|
+
*
|
|
303
|
+
* Scans all queues for stuck messages and recovers them.
|
|
304
|
+
*
|
|
305
|
+
* @returns Object with recovery results per queue
|
|
306
|
+
*/ async recoverFromCrash() {
|
|
307
|
+
try {
|
|
308
|
+
logger.info('Starting coordinator crash recovery');
|
|
309
|
+
// Get all queues
|
|
310
|
+
const queues = await this.queueManager.getQueues();
|
|
311
|
+
const results = {};
|
|
312
|
+
// Recover stuck messages from each queue
|
|
313
|
+
for (const queue of queues){
|
|
314
|
+
// Skip dead letter queue
|
|
315
|
+
if (queue === this.options.deadLetterQueue) {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
const recovered = await this.recoverStuckMessages(queue);
|
|
319
|
+
results[queue] = recovered;
|
|
320
|
+
}
|
|
321
|
+
const totalRecovered = Object.values(results).reduce((sum, count)=>sum + count, 0);
|
|
322
|
+
logger.info('Coordinator crash recovery complete', {
|
|
323
|
+
queuesProcessed: queues.length,
|
|
324
|
+
totalRecovered,
|
|
325
|
+
results
|
|
326
|
+
});
|
|
327
|
+
return results;
|
|
328
|
+
} catch (error) {
|
|
329
|
+
logger.error('Coordinator crash recovery failed', error instanceof Error ? error : new Error(String(error)));
|
|
330
|
+
throw createError(ErrorCode.DB_QUERY_FAILED, 'Failed to recover from coordinator crash', {}, error instanceof Error ? error : undefined);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Start automatic monitoring for stuck messages
|
|
335
|
+
*/ startMonitoring() {
|
|
336
|
+
if (this.monitoringTimer) {
|
|
337
|
+
logger.warn('Monitoring already started');
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
this.monitoringTimer = setInterval(async ()=>{
|
|
341
|
+
try {
|
|
342
|
+
await this.recoverFromCrash();
|
|
343
|
+
// Auto-reprocess dead letters if enabled
|
|
344
|
+
if (this.options.autoReprocess) {
|
|
345
|
+
await this.reprocessDeadLetters(async (payload, metadata)=>{
|
|
346
|
+
logger.debug('Auto-reprocessing dead letter', {
|
|
347
|
+
originalQueue: metadata.originalQueue
|
|
348
|
+
});
|
|
349
|
+
// Re-enqueue to original queue
|
|
350
|
+
await this.queueManager.enqueue(metadata.originalQueue, payload, {
|
|
351
|
+
deduplicate: false,
|
|
352
|
+
metadata: {
|
|
353
|
+
...metadata,
|
|
354
|
+
reprocessedAt: new Date().toISOString()
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
} catch (error) {
|
|
360
|
+
logger.error('Monitoring cycle failed', error instanceof Error ? error : new Error(String(error)));
|
|
361
|
+
}
|
|
362
|
+
}, this.options.monitoringIntervalMs);
|
|
363
|
+
logger.info('Monitoring started', {
|
|
364
|
+
intervalMs: this.options.monitoringIntervalMs,
|
|
365
|
+
autoReprocess: this.options.autoReprocess
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Stop automatic monitoring
|
|
370
|
+
*/ stopMonitoring() {
|
|
371
|
+
if (this.monitoringTimer) {
|
|
372
|
+
clearInterval(this.monitoringTimer);
|
|
373
|
+
this.monitoringTimer = null;
|
|
374
|
+
logger.info('Monitoring stopped');
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Get recovery statistics
|
|
379
|
+
*
|
|
380
|
+
* @returns Current statistics
|
|
381
|
+
*/ getStats() {
|
|
382
|
+
return {
|
|
383
|
+
...this.stats
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Reset statistics
|
|
388
|
+
*/ resetStats() {
|
|
389
|
+
this.stats = {
|
|
390
|
+
totalRecovered: 0,
|
|
391
|
+
totalDeadLettered: 0,
|
|
392
|
+
totalReprocessed: 0,
|
|
393
|
+
totalStuckDetected: 0
|
|
394
|
+
};
|
|
395
|
+
logger.debug('Statistics reset');
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Shutdown recovery system (stop monitoring)
|
|
399
|
+
*/ shutdown() {
|
|
400
|
+
this.stopMonitoring();
|
|
401
|
+
logger.info('QueueRecovery shutdown');
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Calculate exponential backoff delay
|
|
405
|
+
*
|
|
406
|
+
* @param attempt - Current attempt number (1-based)
|
|
407
|
+
* @returns Delay in milliseconds
|
|
408
|
+
*/ calculateBackoffDelay(attempt) {
|
|
409
|
+
// Exponential backoff: baseDelay * 2^(attempt - 1)
|
|
410
|
+
const delay = this.options.baseRetryDelayMs * Math.pow(2, attempt - 1);
|
|
411
|
+
// Cap at max delay
|
|
412
|
+
const cappedDelay = Math.min(delay, this.options.maxRetryDelayMs);
|
|
413
|
+
// Add jitter (+/- 10%) to prevent thundering herd
|
|
414
|
+
const jitterFactor = 0.1;
|
|
415
|
+
const jitterRange = cappedDelay * jitterFactor;
|
|
416
|
+
const jitter = (Math.random() * 2 - 1) * jitterRange;
|
|
417
|
+
return Math.max(0, Math.floor(cappedDelay + jitter));
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Message reprocessing safeguards
|
|
422
|
+
*/ export class ReprocessingSafeguards {
|
|
423
|
+
processedMessages = new Set();
|
|
424
|
+
maxProcessedTracking;
|
|
425
|
+
/**
|
|
426
|
+
* Create a new ReprocessingSafeguards instance
|
|
427
|
+
*
|
|
428
|
+
* @param maxProcessedTracking - Maximum number of processed message IDs to track (default: 10000)
|
|
429
|
+
*/ constructor(maxProcessedTracking = 10000){
|
|
430
|
+
this.maxProcessedTracking = maxProcessedTracking;
|
|
431
|
+
logger.debug('ReprocessingSafeguards initialized', {
|
|
432
|
+
maxProcessedTracking
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Check if message has already been processed
|
|
437
|
+
*
|
|
438
|
+
* @param messageId - Message ID to check
|
|
439
|
+
* @returns True if already processed
|
|
440
|
+
*/ hasBeenProcessed(messageId) {
|
|
441
|
+
return this.processedMessages.has(messageId);
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Mark message as processed
|
|
445
|
+
*
|
|
446
|
+
* @param messageId - Message ID to mark
|
|
447
|
+
*/ markProcessed(messageId) {
|
|
448
|
+
// Implement simple LRU-like behavior
|
|
449
|
+
if (this.processedMessages.size >= this.maxProcessedTracking) {
|
|
450
|
+
// Remove oldest entry (first in Set)
|
|
451
|
+
const firstId = this.processedMessages.values().next().value;
|
|
452
|
+
this.processedMessages.delete(firstId);
|
|
453
|
+
}
|
|
454
|
+
this.processedMessages.add(messageId);
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Clear all processed message tracking
|
|
458
|
+
*/ clear() {
|
|
459
|
+
this.processedMessages.clear();
|
|
460
|
+
logger.debug('Processed messages cleared');
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Get number of tracked processed messages
|
|
464
|
+
*/ getTrackedCount() {
|
|
465
|
+
return this.processedMessages.size;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
//# sourceMappingURL=queue-recovery.js.map
|