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/database-service/index.ts"],"sourcesContent":["/**\r\n * Database Service - Main Entry Point\r\n *\r\n * Unified database abstraction layer for Redis, SQLite, and PostgreSQL.\r\n * Part of Task 0.4: Database Query Abstraction Layer (MVP)\r\n *\r\n * @example\r\n * ```typescript\r\n * const dbService = new DatabaseService({\r\n * redis: { type: 'redis', host: 'localhost', port: 6379 },\r\n * sqlite: { type: 'sqlite', database: './data.db' },\r\n * postgres: { type: 'postgres', connectionString: 'postgresql://...' }\r\n * });\r\n *\r\n * await dbService.connect();\r\n *\r\n * // Get data by correlation key across all databases\r\n * const data = await dbService.getByCorrelationKey({\r\n * type: 'task',\r\n * id: 'abc123',\r\n * entity: 'agent'\r\n * });\r\n * ```\r\n */\r\n\r\nimport {\r\n IDatabaseService,\r\n IDatabaseAdapter,\r\n DatabaseConfig,\r\n CrossDatabaseResult,\r\n CorrelationKey,\r\n} from './types.js';\r\nimport { RedisAdapter } from './redis-adapter.js';\r\nimport { SQLiteAdapter } from './sqlite-adapter.js';\r\nimport { PostgresAdapter } from './postgres-adapter.js';\r\nimport { TransactionManager } from './transaction-manager.js';\r\nimport {\r\n buildCorrelationKey,\r\n parseCorrelationKey,\r\n buildTaskKey,\r\n buildAgentKey,\r\n buildSkillKey,\r\n buildExecutionKey,\r\n} from './correlation.js';\r\nimport { DatabaseErrorCode, createDatabaseError } from './errors.js';\r\n\r\nexport interface DatabaseServiceConfig {\r\n redis?: DatabaseConfig;\r\n sqlite?: DatabaseConfig;\r\n postgres?: DatabaseConfig;\r\n}\r\n\r\nexport class DatabaseService implements IDatabaseService {\r\n private adapters: Map<string, IDatabaseAdapter> = new Map();\r\n private transactionManager: TransactionManager;\r\n private config: DatabaseServiceConfig;\r\n\r\n constructor(config: DatabaseServiceConfig) {\r\n this.config = config;\r\n this.transactionManager = new TransactionManager();\r\n\r\n // Initialize adapters\r\n if (config.redis) {\r\n this.adapters.set('redis', new RedisAdapter(config.redis));\r\n }\r\n\r\n if (config.sqlite) {\r\n this.adapters.set('sqlite', new SQLiteAdapter(config.sqlite));\r\n }\r\n\r\n if (config.postgres) {\r\n this.adapters.set('postgres', new PostgresAdapter(config.postgres));\r\n }\r\n }\r\n\r\n /**\r\n * Connect to all configured databases\r\n */\r\n async connect(): Promise<void> {\r\n const promises = Array.from(this.adapters.values()).map(adapter => adapter.connect());\r\n await Promise.all(promises);\r\n }\r\n\r\n /**\r\n * Disconnect from all databases\r\n */\r\n async disconnect(): Promise<void> {\r\n const promises = Array.from(this.adapters.values()).map(adapter => adapter.disconnect());\r\n await Promise.all(promises);\r\n }\r\n\r\n /**\r\n * Get adapter for specific database\r\n */\r\n getAdapter(type: 'redis' | 'sqlite' | 'postgres'): IDatabaseAdapter {\r\n const adapter = this.adapters.get(type);\r\n\r\n if (!adapter) {\r\n throw createDatabaseError(\r\n DatabaseErrorCode.CONNECTION_FAILED,\r\n `Database adapter not configured: ${type}`,\r\n undefined,\r\n { type }\r\n );\r\n }\r\n\r\n return adapter;\r\n }\r\n\r\n /**\r\n * Get record by correlation key across all databases\r\n */\r\n async getByCorrelationKey<T = any>(key: CorrelationKey): Promise<CrossDatabaseResult<T>> {\r\n const correlationKeyString = buildCorrelationKey(key);\r\n\r\n const result: CrossDatabaseResult<T> = {\r\n correlationKey: correlationKeyString,\r\n timestamp: new Date(),\r\n };\r\n\r\n // Query each database in parallel\r\n const promises: Promise<void>[] = [];\r\n\r\n if (this.adapters.has('redis')) {\r\n promises.push(\r\n this.adapters.get('redis')!.get<T>(correlationKeyString)\r\n .then(data => { if (data !== null && data !== undefined) result.redis = data; })\r\n .catch(err => console.warn('Redis lookup failed:', err))\r\n );\r\n }\r\n\r\n if (this.adapters.has('sqlite')) {\r\n promises.push(\r\n this.adapters.get('sqlite')!.get<T>(correlationKeyString)\r\n .then(data => { if (data !== null && data !== undefined) result.sqlite = data; })\r\n .catch(err => console.warn('SQLite lookup failed:', err))\r\n );\r\n }\r\n\r\n if (this.adapters.has('postgres')) {\r\n promises.push(\r\n this.adapters.get('postgres')!.get<T>(correlationKeyString)\r\n .then(data => { if (data !== null && data !== undefined) result.postgres = data; })\r\n .catch(err => console.warn('PostgreSQL lookup failed:', err))\r\n );\r\n }\r\n\r\n await Promise.all(promises);\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Execute cross-database transaction\r\n */\r\n async executeTransaction<T = any>(\r\n operations: Array<{\r\n database: 'redis' | 'sqlite' | 'postgres';\r\n operation: (adapter: IDatabaseAdapter) => Promise<T>;\r\n }>\r\n ): Promise<T[]> {\r\n const adapters = operations.map(op => this.getAdapter(op.database));\r\n const ops = operations.map(op => op.operation);\r\n\r\n return this.transactionManager.executeTransaction(adapters, ops);\r\n }\r\n\r\n /**\r\n * Build correlation key\r\n */\r\n buildCorrelationKey(key: CorrelationKey): string {\r\n return buildCorrelationKey(key);\r\n }\r\n\r\n /**\r\n * Parse correlation key\r\n */\r\n parseCorrelationKey(key: string): CorrelationKey | null {\r\n return parseCorrelationKey(key);\r\n }\r\n\r\n /**\r\n * Get transaction manager\r\n */\r\n getTransactionManager(): TransactionManager {\r\n return this.transactionManager;\r\n }\r\n\r\n /**\r\n * Check if all configured databases are connected\r\n */\r\n isConnected(): boolean {\r\n return Array.from(this.adapters.values()).every(adapter => adapter.isConnected());\r\n }\r\n\r\n /**\r\n * Get database statistics including connection pool metrics\r\n */\r\n getStats() {\r\n const poolStats: any = {};\r\n\r\n // Get pool stats from each adapter\r\n if (this.adapters.has('redis')) {\r\n const adapter = this.adapters.get('redis') as any;\r\n poolStats.redis = adapter.getPoolStats?.();\r\n }\r\n\r\n if (this.adapters.has('sqlite')) {\r\n const adapter = this.adapters.get('sqlite') as any;\r\n poolStats.sqlite = adapter.getPoolStats?.();\r\n }\r\n\r\n if (this.adapters.has('postgres')) {\r\n const adapter = this.adapters.get('postgres') as any;\r\n poolStats.postgres = adapter.getPoolStats?.();\r\n }\r\n\r\n return {\r\n adapters: {\r\n redis: this.adapters.has('redis') && this.adapters.get('redis')!.isConnected(),\r\n sqlite: this.adapters.has('sqlite') && this.adapters.get('sqlite')!.isConnected(),\r\n postgres: this.adapters.has('postgres') && this.adapters.get('postgres')!.isConnected(),\r\n },\r\n transactions: {\r\n active: this.transactionManager.getActiveCount(),\r\n },\r\n connectionPools: poolStats,\r\n };\r\n }\r\n}\r\n\r\n// Re-export types and utilities\r\nexport * from './types.js';\r\nexport * from './errors.js';\r\nexport * from './correlation.js';\r\nexport { RedisAdapter } from './redis-adapter.js';\r\nexport { SQLiteAdapter } from './sqlite-adapter.js';\r\nexport { PostgresAdapter } from './postgres-adapter.js';\r\nexport { TransactionManager } from './transaction-manager.js';\r\nexport { ConnectionPoolManager } from './connection-pool-manager.js';\r\n\r\n// Re-export correlation utilities\r\nexport {\r\n buildTaskKey,\r\n buildAgentKey,\r\n buildSkillKey,\r\n buildExecutionKey,\r\n};\r\n"],"names":["RedisAdapter","SQLiteAdapter","PostgresAdapter","TransactionManager","buildCorrelationKey","parseCorrelationKey","buildTaskKey","buildAgentKey","buildSkillKey","buildExecutionKey","DatabaseErrorCode","createDatabaseError","DatabaseService","adapters","Map","transactionManager","config","redis","set","sqlite","postgres","connect","promises","Array","from","values","map","adapter","Promise","all","disconnect","getAdapter","type","get","CONNECTION_FAILED","undefined","getByCorrelationKey","key","correlationKeyString","result","correlationKey","timestamp","Date","has","push","then","data","catch","err","console","warn","executeTransaction","operations","op","database","ops","operation","getTransactionManager","isConnected","every","getStats","poolStats","getPoolStats","transactions","active","getActiveCount","connectionPools","ConnectionPoolManager"],"mappings":"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"}
|
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL Database Adapter
|
|
3
|
+
*
|
|
4
|
+
* Implements IDatabaseAdapter for PostgreSQL with connection pooling and parameterized queries.
|
|
5
|
+
* Part of Task 0.4: Database Query Abstraction Layer (MVP)
|
|
6
|
+
*
|
|
7
|
+
* SECURITY: Requires password authentication and uses parameterized queries for SQL injection prevention
|
|
8
|
+
*/ import { Pool } from 'pg';
|
|
9
|
+
import { DatabaseErrorCode, createDatabaseError, createSuccessResult, createFailedResult, mapPostgresError } from './errors.js';
|
|
10
|
+
import { withDatabaseRetry } from '../retry-manager.js';
|
|
11
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
12
|
+
export class PostgresAdapter {
|
|
13
|
+
pool = null;
|
|
14
|
+
config;
|
|
15
|
+
connected = false;
|
|
16
|
+
transactions = new Map();
|
|
17
|
+
errorAggregator;
|
|
18
|
+
correlationId;
|
|
19
|
+
constructor(config, errorAggregator){
|
|
20
|
+
this.config = config;
|
|
21
|
+
this.errorAggregator = errorAggregator;
|
|
22
|
+
this.correlationId = uuidv4();
|
|
23
|
+
}
|
|
24
|
+
getType() {
|
|
25
|
+
return 'postgres';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Track error with error aggregator
|
|
29
|
+
* @private
|
|
30
|
+
*/ trackError(error, operation, context) {
|
|
31
|
+
if (this.errorAggregator) {
|
|
32
|
+
const dbError = error.code ? error : createDatabaseError(DatabaseErrorCode.QUERY_FAILED, `PostgreSQL ${operation} failed`, error instanceof Error ? error : new Error(String(error)), context);
|
|
33
|
+
this.errorAggregator.addError('postgres', dbError, {
|
|
34
|
+
...context,
|
|
35
|
+
operation,
|
|
36
|
+
correlationId: this.correlationId
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Record successful operation with error aggregator
|
|
42
|
+
* @private
|
|
43
|
+
*/ recordSuccess() {
|
|
44
|
+
if (this.errorAggregator) {
|
|
45
|
+
this.errorAggregator.recordSuccess('postgres');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async connect() {
|
|
49
|
+
// SECURITY: Validate password is provided for authentication
|
|
50
|
+
if (!this.config.password && !this.config.connectionString) {
|
|
51
|
+
throw createDatabaseError(DatabaseErrorCode.CONNECTION_FAILED, 'PostgreSQL password is required. Set POSTGRES_PASSWORD environment variable.', undefined, {
|
|
52
|
+
reason: 'missing_authentication'
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// Wrap connection with retry logic for transient failures
|
|
56
|
+
await withDatabaseRetry(async ()=>{
|
|
57
|
+
try {
|
|
58
|
+
const poolConfig = {
|
|
59
|
+
host: this.config.host,
|
|
60
|
+
port: this.config.port,
|
|
61
|
+
database: this.config.database,
|
|
62
|
+
user: this.config.username,
|
|
63
|
+
password: this.config.password,
|
|
64
|
+
max: this.config.poolSize || 10,
|
|
65
|
+
idleTimeoutMillis: this.config.timeout || 30000,
|
|
66
|
+
connectionTimeoutMillis: 5000
|
|
67
|
+
};
|
|
68
|
+
// Use connectionString if provided, otherwise build from components
|
|
69
|
+
if (this.config.connectionString) {
|
|
70
|
+
poolConfig.connectionString = this.config.connectionString;
|
|
71
|
+
}
|
|
72
|
+
this.pool = new Pool(poolConfig);
|
|
73
|
+
// Test connection
|
|
74
|
+
const client = await this.pool.connect();
|
|
75
|
+
client.release();
|
|
76
|
+
this.connected = true;
|
|
77
|
+
this.recordSuccess();
|
|
78
|
+
} catch (err) {
|
|
79
|
+
const error = createDatabaseError(DatabaseErrorCode.CONNECTION_FAILED, 'Failed to connect to PostgreSQL', err instanceof Error ? err : new Error(String(err)), {
|
|
80
|
+
config: this.config,
|
|
81
|
+
correlationId: this.correlationId
|
|
82
|
+
});
|
|
83
|
+
this.trackError(error, 'connect');
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async disconnect() {
|
|
89
|
+
if (this.pool) {
|
|
90
|
+
await this.pool.end();
|
|
91
|
+
this.pool = null;
|
|
92
|
+
this.connected = false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
isConnected() {
|
|
96
|
+
return this.connected && this.pool !== null;
|
|
97
|
+
}
|
|
98
|
+
async get(key, transactionId) {
|
|
99
|
+
this.ensureConnected();
|
|
100
|
+
try {
|
|
101
|
+
// Parse correlation key format: table:id or table:id:entity:subtype
|
|
102
|
+
// For SQL adapters, we use only table:id for lookup
|
|
103
|
+
const parts = key.split(':');
|
|
104
|
+
const table = parts[0];
|
|
105
|
+
const id = parts.slice(1).join(':'); // Rejoin remaining parts as ID
|
|
106
|
+
if (!table || !id) {
|
|
107
|
+
throw new Error('Invalid key format. Expected "table:id" or "table:id:entity:subtype"');
|
|
108
|
+
}
|
|
109
|
+
const query = `SELECT * FROM ${this.sanitizeIdentifier(table)} WHERE id = $1`;
|
|
110
|
+
const client = this.getQueryClient(transactionId);
|
|
111
|
+
const result = await client.query(query, [
|
|
112
|
+
id
|
|
113
|
+
]);
|
|
114
|
+
this.recordSuccess();
|
|
115
|
+
return result.rows.length > 0 ? result.rows[0] : null;
|
|
116
|
+
} catch (err) {
|
|
117
|
+
const errorCode = mapPostgresError(err);
|
|
118
|
+
const error = createDatabaseError(errorCode, `Failed to get record: ${key}`, err instanceof Error ? err : new Error(String(err)), {
|
|
119
|
+
key,
|
|
120
|
+
correlationId: this.correlationId
|
|
121
|
+
});
|
|
122
|
+
this.trackError(error, 'get', {
|
|
123
|
+
key
|
|
124
|
+
});
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async list(table, options, transactionId) {
|
|
129
|
+
this.ensureConnected();
|
|
130
|
+
try {
|
|
131
|
+
let query = `SELECT * FROM ${this.sanitizeIdentifier(table)}`;
|
|
132
|
+
const params = [];
|
|
133
|
+
let paramIndex = 1;
|
|
134
|
+
// Apply filters
|
|
135
|
+
if (options?.filters && options.filters.length > 0) {
|
|
136
|
+
const whereClauses = options.filters.map((filter)=>{
|
|
137
|
+
return this.buildWhereClause(filter, params);
|
|
138
|
+
});
|
|
139
|
+
query += ` WHERE ${whereClauses.join(' AND ')}`;
|
|
140
|
+
}
|
|
141
|
+
// Apply ordering
|
|
142
|
+
if (options?.orderBy) {
|
|
143
|
+
const order = options.order || 'asc';
|
|
144
|
+
query += ` ORDER BY ${this.sanitizeIdentifier(String(options.orderBy))} ${order.toUpperCase()}`;
|
|
145
|
+
}
|
|
146
|
+
// Apply limit and offset
|
|
147
|
+
if (options?.limit) {
|
|
148
|
+
query += ` LIMIT $${paramIndex++}`;
|
|
149
|
+
params.push(options.limit);
|
|
150
|
+
}
|
|
151
|
+
if (options?.offset) {
|
|
152
|
+
query += ` OFFSET $${paramIndex++}`;
|
|
153
|
+
params.push(options.offset);
|
|
154
|
+
}
|
|
155
|
+
const client = this.getQueryClient(transactionId);
|
|
156
|
+
const result = await client.query(query, params);
|
|
157
|
+
this.recordSuccess();
|
|
158
|
+
return result.rows;
|
|
159
|
+
} catch (err) {
|
|
160
|
+
const errorCode = mapPostgresError(err);
|
|
161
|
+
const error = createDatabaseError(errorCode, `Failed to list records from table: ${table}`, err instanceof Error ? err : new Error(String(err)), {
|
|
162
|
+
table,
|
|
163
|
+
options,
|
|
164
|
+
correlationId: this.correlationId
|
|
165
|
+
});
|
|
166
|
+
this.trackError(error, 'list', {
|
|
167
|
+
table
|
|
168
|
+
});
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async query(table, filters, transactionId) {
|
|
173
|
+
return this.list(table, {
|
|
174
|
+
filters
|
|
175
|
+
}, transactionId);
|
|
176
|
+
}
|
|
177
|
+
async insert(table, data, transactionId) {
|
|
178
|
+
this.ensureConnected();
|
|
179
|
+
try {
|
|
180
|
+
const keys = Object.keys(data);
|
|
181
|
+
const values = Object.values(data);
|
|
182
|
+
const placeholders = keys.map((_, i)=>`$${i + 1}`).join(', ');
|
|
183
|
+
const columns = keys.map((k)=>this.sanitizeIdentifier(k)).join(', ');
|
|
184
|
+
const query = `INSERT INTO ${this.sanitizeIdentifier(table)} (${columns}) VALUES (${placeholders}) RETURNING *`;
|
|
185
|
+
const client = this.getQueryClient(transactionId);
|
|
186
|
+
const result = await client.query(query, values);
|
|
187
|
+
this.recordSuccess();
|
|
188
|
+
return createSuccessResult(result.rows[0], result.rowCount || 0, result.rows[0] ? result.rows[0].id : undefined);
|
|
189
|
+
} catch (err) {
|
|
190
|
+
const errorCode = mapPostgresError(err);
|
|
191
|
+
const error = createDatabaseError(errorCode, `Failed to insert record into table: ${table}`, err instanceof Error ? err : new Error(String(err)), {
|
|
192
|
+
table,
|
|
193
|
+
data,
|
|
194
|
+
correlationId: this.correlationId
|
|
195
|
+
});
|
|
196
|
+
this.trackError(error, 'insert', {
|
|
197
|
+
table
|
|
198
|
+
});
|
|
199
|
+
return createFailedResult(error);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async insertMany(table, data, transactionId) {
|
|
203
|
+
this.ensureConnected();
|
|
204
|
+
// Check if we're already in a transaction
|
|
205
|
+
const hasActiveTransaction = transactionId && this.transactions.has(transactionId);
|
|
206
|
+
const client = hasActiveTransaction ? this.getQueryClient(transactionId) : await this.pool.connect();
|
|
207
|
+
try {
|
|
208
|
+
// Only begin transaction if not already in one
|
|
209
|
+
if (!hasActiveTransaction) {
|
|
210
|
+
await client.query('BEGIN');
|
|
211
|
+
}
|
|
212
|
+
const results = [];
|
|
213
|
+
for (const item of data){
|
|
214
|
+
const keys = Object.keys(item);
|
|
215
|
+
const values = Object.values(item);
|
|
216
|
+
const placeholders = keys.map((_, i)=>`$${i + 1}`).join(', ');
|
|
217
|
+
const columns = keys.map((k)=>this.sanitizeIdentifier(k)).join(', ');
|
|
218
|
+
const query = `INSERT INTO ${this.sanitizeIdentifier(table)} (${columns}) VALUES (${placeholders}) RETURNING *`;
|
|
219
|
+
const result = await client.query(query, values);
|
|
220
|
+
results.push(result.rows[0]);
|
|
221
|
+
}
|
|
222
|
+
// Only commit if we started the transaction
|
|
223
|
+
if (!hasActiveTransaction) {
|
|
224
|
+
await client.query('COMMIT');
|
|
225
|
+
}
|
|
226
|
+
this.recordSuccess();
|
|
227
|
+
return createSuccessResult(results, results.length);
|
|
228
|
+
} catch (err) {
|
|
229
|
+
// Only rollback if we started the transaction
|
|
230
|
+
if (!hasActiveTransaction) {
|
|
231
|
+
await client.query('ROLLBACK');
|
|
232
|
+
}
|
|
233
|
+
const errorCode = mapPostgresError(err);
|
|
234
|
+
const error = createDatabaseError(errorCode, `Failed to insert multiple records into table: ${table}`, err instanceof Error ? err : new Error(String(err)), {
|
|
235
|
+
table,
|
|
236
|
+
count: data.length,
|
|
237
|
+
correlationId: this.correlationId
|
|
238
|
+
});
|
|
239
|
+
this.trackError(error, 'insertMany', {
|
|
240
|
+
table,
|
|
241
|
+
count: data.length
|
|
242
|
+
});
|
|
243
|
+
return createFailedResult(error);
|
|
244
|
+
} finally{
|
|
245
|
+
// Only release client if we acquired it (not using existing transaction client)
|
|
246
|
+
if (!hasActiveTransaction) {
|
|
247
|
+
client.release();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
async update(table, key, data, transactionId) {
|
|
252
|
+
this.ensureConnected();
|
|
253
|
+
try {
|
|
254
|
+
const keys = Object.keys(data);
|
|
255
|
+
const values = Object.values(data);
|
|
256
|
+
const setClauses = keys.map((k, i)=>`${this.sanitizeIdentifier(k)} = $${i + 1}`).join(', ');
|
|
257
|
+
const query = `UPDATE ${this.sanitizeIdentifier(table)} SET ${setClauses} WHERE id = $${keys.length + 1} RETURNING *`;
|
|
258
|
+
const client = this.getQueryClient(transactionId);
|
|
259
|
+
const result = await client.query(query, [
|
|
260
|
+
...values,
|
|
261
|
+
key
|
|
262
|
+
]);
|
|
263
|
+
if (result.rowCount === 0) {
|
|
264
|
+
const error = createDatabaseError(DatabaseErrorCode.NOT_FOUND, `Record not found in table: ${table}`, undefined, {
|
|
265
|
+
table,
|
|
266
|
+
key,
|
|
267
|
+
correlationId: this.correlationId
|
|
268
|
+
});
|
|
269
|
+
this.trackError(error, 'update', {
|
|
270
|
+
table,
|
|
271
|
+
key
|
|
272
|
+
});
|
|
273
|
+
return createFailedResult(error);
|
|
274
|
+
}
|
|
275
|
+
this.recordSuccess();
|
|
276
|
+
return createSuccessResult(result.rows[0], result.rowCount || 0);
|
|
277
|
+
} catch (err) {
|
|
278
|
+
const errorCode = mapPostgresError(err);
|
|
279
|
+
const error = createDatabaseError(errorCode, `Failed to update record in table: ${table}`, err instanceof Error ? err : new Error(String(err)), {
|
|
280
|
+
table,
|
|
281
|
+
key,
|
|
282
|
+
data,
|
|
283
|
+
correlationId: this.correlationId
|
|
284
|
+
});
|
|
285
|
+
this.trackError(error, 'update', {
|
|
286
|
+
table,
|
|
287
|
+
key
|
|
288
|
+
});
|
|
289
|
+
return createFailedResult(error);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
async delete(table, key, transactionId) {
|
|
293
|
+
this.ensureConnected();
|
|
294
|
+
try {
|
|
295
|
+
const query = `DELETE FROM ${this.sanitizeIdentifier(table)} WHERE id = $1`;
|
|
296
|
+
const client = this.getQueryClient(transactionId);
|
|
297
|
+
const result = await client.query(query, [
|
|
298
|
+
key
|
|
299
|
+
]);
|
|
300
|
+
if (result.rowCount === 0) {
|
|
301
|
+
const error = createDatabaseError(DatabaseErrorCode.NOT_FOUND, `Record not found in table: ${table}`, undefined, {
|
|
302
|
+
table,
|
|
303
|
+
key,
|
|
304
|
+
correlationId: this.correlationId
|
|
305
|
+
});
|
|
306
|
+
this.trackError(error, 'delete', {
|
|
307
|
+
table,
|
|
308
|
+
key
|
|
309
|
+
});
|
|
310
|
+
return createFailedResult(error);
|
|
311
|
+
}
|
|
312
|
+
this.recordSuccess();
|
|
313
|
+
return createSuccessResult(undefined, result.rowCount || 0);
|
|
314
|
+
} catch (err) {
|
|
315
|
+
const errorCode = mapPostgresError(err);
|
|
316
|
+
const error = createDatabaseError(errorCode, `Failed to delete record from table: ${table}`, err instanceof Error ? err : new Error(String(err)), {
|
|
317
|
+
table,
|
|
318
|
+
key,
|
|
319
|
+
correlationId: this.correlationId
|
|
320
|
+
});
|
|
321
|
+
this.trackError(error, 'delete', {
|
|
322
|
+
table,
|
|
323
|
+
key
|
|
324
|
+
});
|
|
325
|
+
return createFailedResult(error);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async raw(query, params, transactionId) {
|
|
329
|
+
this.ensureConnected();
|
|
330
|
+
try {
|
|
331
|
+
const client = this.getQueryClient(transactionId);
|
|
332
|
+
const result = await client.query(query, params);
|
|
333
|
+
this.recordSuccess();
|
|
334
|
+
return result.rows;
|
|
335
|
+
} catch (err) {
|
|
336
|
+
const errorCode = mapPostgresError(err);
|
|
337
|
+
const error = createDatabaseError(errorCode, `Failed to execute raw query`, err instanceof Error ? err : new Error(String(err)), {
|
|
338
|
+
query,
|
|
339
|
+
params,
|
|
340
|
+
correlationId: this.correlationId
|
|
341
|
+
});
|
|
342
|
+
this.trackError(error, 'raw', {
|
|
343
|
+
query
|
|
344
|
+
});
|
|
345
|
+
throw error;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
async beginTransaction() {
|
|
349
|
+
this.ensureConnected();
|
|
350
|
+
const client = await this.pool.connect();
|
|
351
|
+
const context = {
|
|
352
|
+
id: `postgres-tx-${Date.now()}`,
|
|
353
|
+
databases: [
|
|
354
|
+
'postgres'
|
|
355
|
+
],
|
|
356
|
+
startTime: new Date(),
|
|
357
|
+
status: 'pending'
|
|
358
|
+
};
|
|
359
|
+
await client.query('BEGIN');
|
|
360
|
+
this.transactions.set(context.id, {
|
|
361
|
+
context,
|
|
362
|
+
client
|
|
363
|
+
});
|
|
364
|
+
return context;
|
|
365
|
+
}
|
|
366
|
+
async prepareTransaction(context) {
|
|
367
|
+
const transaction = this.transactions.get(context.id);
|
|
368
|
+
if (!transaction) {
|
|
369
|
+
throw createDatabaseError(DatabaseErrorCode.TRANSACTION_FAILED, 'Transaction not found', undefined, {
|
|
370
|
+
transactionId: context.id
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
try {
|
|
374
|
+
// PostgreSQL supports PREPARE TRANSACTION for two-phase commit
|
|
375
|
+
// Note: This requires max_prepared_transactions > 0 in postgresql.conf
|
|
376
|
+
await transaction.client.query(`PREPARE TRANSACTION '${context.id}'`);
|
|
377
|
+
context.status = 'prepared';
|
|
378
|
+
context.preparedAt = new Date();
|
|
379
|
+
return true;
|
|
380
|
+
} catch (err) {
|
|
381
|
+
// If prepare fails, the transaction is still active and can be rolled back
|
|
382
|
+
throw createDatabaseError(DatabaseErrorCode.TRANSACTION_FAILED, 'Failed to prepare transaction', err instanceof Error ? err : new Error(String(err)), {
|
|
383
|
+
transactionId: context.id
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async commitTransaction(context) {
|
|
388
|
+
const transaction = this.transactions.get(context.id);
|
|
389
|
+
if (!transaction) {
|
|
390
|
+
throw createDatabaseError(DatabaseErrorCode.TRANSACTION_FAILED, 'Transaction not found', undefined, {
|
|
391
|
+
transactionId: context.id
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
try {
|
|
395
|
+
// If transaction was prepared, use COMMIT PREPARED
|
|
396
|
+
if (context.status === 'prepared') {
|
|
397
|
+
await transaction.client.query(`COMMIT PREPARED '${context.id}'`);
|
|
398
|
+
} else {
|
|
399
|
+
await transaction.client.query('COMMIT');
|
|
400
|
+
}
|
|
401
|
+
context.status = 'committed';
|
|
402
|
+
} finally{
|
|
403
|
+
transaction.client.release();
|
|
404
|
+
this.transactions.delete(context.id);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
async rollbackTransaction(context) {
|
|
408
|
+
const transaction = this.transactions.get(context.id);
|
|
409
|
+
if (!transaction) {
|
|
410
|
+
throw createDatabaseError(DatabaseErrorCode.TRANSACTION_FAILED, 'Transaction not found', undefined, {
|
|
411
|
+
transactionId: context.id
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
try {
|
|
415
|
+
// If transaction was prepared, use ROLLBACK PREPARED
|
|
416
|
+
if (context.status === 'prepared') {
|
|
417
|
+
await transaction.client.query(`ROLLBACK PREPARED '${context.id}'`);
|
|
418
|
+
} else {
|
|
419
|
+
await transaction.client.query('ROLLBACK');
|
|
420
|
+
}
|
|
421
|
+
context.status = 'rolled_back';
|
|
422
|
+
} finally{
|
|
423
|
+
transaction.client.release();
|
|
424
|
+
this.transactions.delete(context.id);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
ensureConnected() {
|
|
428
|
+
if (!this.isConnected()) {
|
|
429
|
+
throw createDatabaseError(DatabaseErrorCode.CONNECTION_FAILED, 'Not connected to PostgreSQL', undefined, {
|
|
430
|
+
config: this.config
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
sanitizeIdentifier(identifier) {
|
|
435
|
+
// Remove any characters that aren't alphanumeric or underscore
|
|
436
|
+
return identifier.replace(/[^a-zA-Z0-9_]/g, '');
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Get client for query execution (transaction client if available, otherwise pool)
|
|
440
|
+
*/ getQueryClient(transactionId) {
|
|
441
|
+
if (transactionId) {
|
|
442
|
+
const transaction = this.transactions.get(transactionId);
|
|
443
|
+
if (transaction) {
|
|
444
|
+
return transaction.client;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return this.pool;
|
|
448
|
+
}
|
|
449
|
+
buildWhereClause(filter, params) {
|
|
450
|
+
const field = this.sanitizeIdentifier(String(filter.field));
|
|
451
|
+
switch(filter.operator){
|
|
452
|
+
case 'eq':
|
|
453
|
+
{
|
|
454
|
+
const paramIndex = params.length + 1;
|
|
455
|
+
params.push(filter.value);
|
|
456
|
+
return `${field} = $${paramIndex}`;
|
|
457
|
+
}
|
|
458
|
+
case 'ne':
|
|
459
|
+
{
|
|
460
|
+
const paramIndex = params.length + 1;
|
|
461
|
+
params.push(filter.value);
|
|
462
|
+
return `${field} != $${paramIndex}`;
|
|
463
|
+
}
|
|
464
|
+
case 'gt':
|
|
465
|
+
{
|
|
466
|
+
const paramIndex = params.length + 1;
|
|
467
|
+
params.push(filter.value);
|
|
468
|
+
return `${field} > $${paramIndex}`;
|
|
469
|
+
}
|
|
470
|
+
case 'gte':
|
|
471
|
+
{
|
|
472
|
+
const paramIndex = params.length + 1;
|
|
473
|
+
params.push(filter.value);
|
|
474
|
+
return `${field} >= $${paramIndex}`;
|
|
475
|
+
}
|
|
476
|
+
case 'lt':
|
|
477
|
+
{
|
|
478
|
+
const paramIndex = params.length + 1;
|
|
479
|
+
params.push(filter.value);
|
|
480
|
+
return `${field} < $${paramIndex}`;
|
|
481
|
+
}
|
|
482
|
+
case 'lte':
|
|
483
|
+
{
|
|
484
|
+
const paramIndex = params.length + 1;
|
|
485
|
+
params.push(filter.value);
|
|
486
|
+
return `${field} <= $${paramIndex}`;
|
|
487
|
+
}
|
|
488
|
+
case 'in':
|
|
489
|
+
{
|
|
490
|
+
if (!Array.isArray(filter.value)) {
|
|
491
|
+
throw new TypeError(`Field '${String(filter.field)}' with operator 'in' requires an array value`);
|
|
492
|
+
}
|
|
493
|
+
if (filter.value.length === 0) {
|
|
494
|
+
// Empty IN list - return false condition without invalid SQL
|
|
495
|
+
return '1=0';
|
|
496
|
+
}
|
|
497
|
+
const startIndex = params.length + 1;
|
|
498
|
+
const placeholders = filter.value.map((_, i)=>`$${startIndex + i}`).join(', ');
|
|
499
|
+
params.push(...filter.value);
|
|
500
|
+
return `${field} IN (${placeholders})`;
|
|
501
|
+
}
|
|
502
|
+
case 'like':
|
|
503
|
+
{
|
|
504
|
+
const paramIndex = params.length + 1;
|
|
505
|
+
params.push(`%${filter.value}%`);
|
|
506
|
+
return `${field} LIKE $${paramIndex}`;
|
|
507
|
+
}
|
|
508
|
+
case 'between':
|
|
509
|
+
{
|
|
510
|
+
if (!Array.isArray(filter.value)) {
|
|
511
|
+
throw new TypeError(`Field '${String(filter.field)}' with operator 'between' requires an array value`);
|
|
512
|
+
}
|
|
513
|
+
if (filter.value.length !== 2) {
|
|
514
|
+
throw new TypeError(`Field '${String(filter.field)}' with operator 'between' requires exactly 2 elements, got ${filter.value.length}`);
|
|
515
|
+
}
|
|
516
|
+
const paramIndex = params.length + 1;
|
|
517
|
+
params.push(filter.value[0], filter.value[1]);
|
|
518
|
+
return `${field} BETWEEN $${paramIndex} AND $${paramIndex + 1}`;
|
|
519
|
+
}
|
|
520
|
+
default:
|
|
521
|
+
return '1=1';
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
//# sourceMappingURL=postgres-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/database-service/postgres-adapter.ts"],"sourcesContent":["/**\r\n * PostgreSQL Database Adapter\r\n *\r\n * Implements IDatabaseAdapter for PostgreSQL with connection pooling and parameterized queries.\r\n * Part of Task 0.4: Database Query Abstraction Layer (MVP)\r\n *\r\n * SECURITY: Requires password authentication and uses parameterized queries for SQL injection prevention\r\n */\r\n\r\nimport { Pool, PoolClient, QueryResult } from 'pg';\r\nimport {\r\n IDatabaseAdapter,\r\n DatabaseConfig,\r\n QueryOptions,\r\n QueryFilter,\r\n OperationResult,\r\n TransactionContext,\r\n} from './types.js';\r\nimport {\r\n DatabaseErrorCode,\r\n createDatabaseError,\r\n createSuccessResult,\r\n createFailedResult,\r\n mapPostgresError,\r\n} from './errors.js';\r\nimport { withDatabaseRetry } from '../retry-manager.js';\r\nimport { ErrorAggregator } from '../error-aggregator.js';\r\nimport { v4 as uuidv4 } from 'uuid';\r\n\r\nexport class PostgresAdapter implements IDatabaseAdapter {\r\n private pool: Pool | null = null;\r\n private config: DatabaseConfig;\r\n private connected: boolean = false;\r\n private transactions: Map<string, { context: TransactionContext; client: PoolClient }> = new Map();\r\n private errorAggregator?: ErrorAggregator;\r\n private correlationId: string;\r\n\r\n constructor(config: DatabaseConfig, errorAggregator?: ErrorAggregator) {\r\n this.config = config;\r\n this.errorAggregator = errorAggregator;\r\n this.correlationId = uuidv4();\r\n }\r\n\r\n getType(): 'postgres' {\r\n return 'postgres';\r\n }\r\n\r\n /**\r\n * Track error with error aggregator\r\n * @private\r\n */\r\n private trackError(error: any, operation: string, context?: Record<string, any>): void {\r\n if (this.errorAggregator) {\r\n const dbError = error.code ? error : createDatabaseError(\r\n DatabaseErrorCode.QUERY_FAILED,\r\n `PostgreSQL ${operation} failed`,\r\n error instanceof Error ? error : new Error(String(error)),\r\n context\r\n );\r\n\r\n this.errorAggregator.addError('postgres', dbError, {\r\n ...context,\r\n operation,\r\n correlationId: this.correlationId,\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Record successful operation with error aggregator\r\n * @private\r\n */\r\n private recordSuccess(): void {\r\n if (this.errorAggregator) {\r\n this.errorAggregator.recordSuccess('postgres');\r\n }\r\n }\r\n\r\n async connect(): Promise<void> {\r\n // SECURITY: Validate password is provided for authentication\r\n if (!this.config.password && !this.config.connectionString) {\r\n throw createDatabaseError(\r\n DatabaseErrorCode.CONNECTION_FAILED,\r\n 'PostgreSQL password is required. Set POSTGRES_PASSWORD environment variable.',\r\n undefined,\r\n { reason: 'missing_authentication' }\r\n );\r\n }\r\n\r\n // Wrap connection with retry logic for transient failures\r\n await withDatabaseRetry(async () => {\r\n try {\r\n const poolConfig: any = {\r\n host: this.config.host,\r\n port: this.config.port,\r\n database: this.config.database,\r\n user: this.config.username,\r\n password: this.config.password,\r\n max: this.config.poolSize || 10,\r\n idleTimeoutMillis: this.config.timeout || 30000,\r\n connectionTimeoutMillis: 5000,\r\n };\r\n\r\n // Use connectionString if provided, otherwise build from components\r\n if (this.config.connectionString) {\r\n poolConfig.connectionString = this.config.connectionString;\r\n }\r\n\r\n this.pool = new Pool(poolConfig);\r\n\r\n // Test connection\r\n const client = await this.pool.connect();\r\n client.release();\r\n\r\n this.connected = true;\r\n this.recordSuccess();\r\n } catch (err) {\r\n const error = createDatabaseError(\r\n DatabaseErrorCode.CONNECTION_FAILED,\r\n 'Failed to connect to PostgreSQL',\r\n err instanceof Error ? err : new Error(String(err)),\r\n { config: this.config, correlationId: this.correlationId }\r\n );\r\n this.trackError(error, 'connect');\r\n throw error;\r\n }\r\n });\r\n }\r\n\r\n async disconnect(): Promise<void> {\r\n if (this.pool) {\r\n await this.pool.end();\r\n this.pool = null;\r\n this.connected = false;\r\n }\r\n }\r\n\r\n isConnected(): boolean {\r\n return this.connected && this.pool !== null;\r\n }\r\n\r\n async get<T = any>(key: string, transactionId?: string): Promise<T | null> {\r\n this.ensureConnected();\r\n\r\n try {\r\n // Parse correlation key format: table:id or table:id:entity:subtype\r\n // For SQL adapters, we use only table:id for lookup\r\n const parts = key.split(':');\r\n const table = parts[0];\r\n const id = parts.slice(1).join(':'); // Rejoin remaining parts as ID\r\n\r\n if (!table || !id) {\r\n throw new Error('Invalid key format. Expected \"table:id\" or \"table:id:entity:subtype\"');\r\n }\r\n\r\n const query = `SELECT * FROM ${this.sanitizeIdentifier(table)} WHERE id = $1`;\r\n const client = this.getQueryClient(transactionId);\r\n const result = await client.query<T>(query, [id]);\r\n\r\n this.recordSuccess();\r\n return result.rows.length > 0 ? result.rows[0] : null;\r\n } catch (err) {\r\n const errorCode = mapPostgresError(err);\r\n const error = createDatabaseError(\r\n errorCode,\r\n `Failed to get record: ${key}`,\r\n err instanceof Error ? err : new Error(String(err)),\r\n { key, correlationId: this.correlationId }\r\n );\r\n this.trackError(error, 'get', { key });\r\n throw error;\r\n }\r\n }\r\n\r\n async list<T = any>(table: string, options?: QueryOptions<T>, transactionId?: string): Promise<T[]> {\r\n this.ensureConnected();\r\n\r\n try {\r\n let query = `SELECT * FROM ${this.sanitizeIdentifier(table)}`;\r\n const params: any[] = [];\r\n let paramIndex = 1;\r\n\r\n // Apply filters\r\n if (options?.filters && options.filters.length > 0) {\r\n const whereClauses = options.filters.map(filter => {\r\n return this.buildWhereClause(filter, params);\r\n });\r\n query += ` WHERE ${whereClauses.join(' AND ')}`;\r\n }\r\n\r\n // Apply ordering\r\n if (options?.orderBy) {\r\n const order = options.order || 'asc';\r\n query += ` ORDER BY ${this.sanitizeIdentifier(String(options.orderBy))} ${order.toUpperCase()}`;\r\n }\r\n\r\n // Apply limit and offset\r\n if (options?.limit) {\r\n query += ` LIMIT $${paramIndex++}`;\r\n params.push(options.limit);\r\n }\r\n\r\n if (options?.offset) {\r\n query += ` OFFSET $${paramIndex++}`;\r\n params.push(options.offset);\r\n }\r\n\r\n const client = this.getQueryClient(transactionId);\r\n const result = await client.query<T>(query, params);\r\n this.recordSuccess();\r\n return result.rows;\r\n } catch (err) {\r\n const errorCode = mapPostgresError(err);\r\n const error = createDatabaseError(\r\n errorCode,\r\n `Failed to list records from table: ${table}`,\r\n err instanceof Error ? err : new Error(String(err)),\r\n { table, options, correlationId: this.correlationId }\r\n );\r\n this.trackError(error, 'list', { table });\r\n throw error;\r\n }\r\n }\r\n\r\n async query<T = any>(table: string, filters: QueryFilter<T>[], transactionId?: string): Promise<T[]> {\r\n return this.list(table, { filters }, transactionId);\r\n }\r\n\r\n async insert<T = any>(table: string, data: T, transactionId?: string): Promise<OperationResult<T>> {\r\n this.ensureConnected();\r\n\r\n try {\r\n const keys = Object.keys(data as any);\r\n const values = Object.values(data as any);\r\n\r\n const placeholders = keys.map((_, i) => `$${i + 1}`).join(', ');\r\n const columns = keys.map(k => this.sanitizeIdentifier(k)).join(', ');\r\n\r\n const query = `INSERT INTO ${this.sanitizeIdentifier(table)} (${columns}) VALUES (${placeholders}) RETURNING *`;\r\n\r\n const client = this.getQueryClient(transactionId);\r\n const result = await client.query<T>(query, values);\r\n\r\n this.recordSuccess();\r\n return createSuccessResult(result.rows[0], result.rowCount || 0, result.rows[0] ? (result.rows[0] as any).id : undefined);\r\n } catch (err) {\r\n const errorCode = mapPostgresError(err);\r\n const error = createDatabaseError(\r\n errorCode,\r\n `Failed to insert record into table: ${table}`,\r\n err instanceof Error ? err : new Error(String(err)),\r\n { table, data, correlationId: this.correlationId }\r\n );\r\n this.trackError(error, 'insert', { table });\r\n return createFailedResult(error);\r\n }\r\n }\r\n\r\n async insertMany<T = any>(table: string, data: T[], transactionId?: string): Promise<OperationResult<T[]>> {\r\n this.ensureConnected();\r\n\r\n // Check if we're already in a transaction\r\n const hasActiveTransaction = transactionId && this.transactions.has(transactionId);\r\n const client = hasActiveTransaction ? this.getQueryClient(transactionId) as PoolClient : await this.pool!.connect();\r\n\r\n try {\r\n // Only begin transaction if not already in one\r\n if (!hasActiveTransaction) {\r\n await client.query('BEGIN');\r\n }\r\n\r\n const results: T[] = [];\r\n\r\n for (const item of data) {\r\n const keys = Object.keys(item as any);\r\n const values = Object.values(item as any);\r\n\r\n const placeholders = keys.map((_, i) => `$${i + 1}`).join(', ');\r\n const columns = keys.map(k => this.sanitizeIdentifier(k)).join(', ');\r\n\r\n const query = `INSERT INTO ${this.sanitizeIdentifier(table)} (${columns}) VALUES (${placeholders}) RETURNING *`;\r\n\r\n const result = await client.query<T>(query, values);\r\n results.push(result.rows[0]);\r\n }\r\n\r\n // Only commit if we started the transaction\r\n if (!hasActiveTransaction) {\r\n await client.query('COMMIT');\r\n }\r\n\r\n this.recordSuccess();\r\n return createSuccessResult(results, results.length);\r\n } catch (err) {\r\n // Only rollback if we started the transaction\r\n if (!hasActiveTransaction) {\r\n await client.query('ROLLBACK');\r\n }\r\n\r\n const errorCode = mapPostgresError(err);\r\n const error = createDatabaseError(\r\n errorCode,\r\n `Failed to insert multiple records into table: ${table}`,\r\n err instanceof Error ? err : new Error(String(err)),\r\n { table, count: data.length, correlationId: this.correlationId }\r\n );\r\n this.trackError(error, 'insertMany', { table, count: data.length });\r\n return createFailedResult(error);\r\n } finally {\r\n // Only release client if we acquired it (not using existing transaction client)\r\n if (!hasActiveTransaction) {\r\n client.release();\r\n }\r\n }\r\n }\r\n\r\n async update<T = any>(table: string, key: string, data: Partial<T>, transactionId?: string): Promise<OperationResult<T>> {\r\n this.ensureConnected();\r\n\r\n try {\r\n const keys = Object.keys(data as any);\r\n const values = Object.values(data as any);\r\n\r\n const setClauses = keys.map((k, i) => `${this.sanitizeIdentifier(k)} = $${i + 1}`).join(', ');\r\n\r\n const query = `UPDATE ${this.sanitizeIdentifier(table)} SET ${setClauses} WHERE id = $${keys.length + 1} RETURNING *`;\r\n\r\n const client = this.getQueryClient(transactionId);\r\n const result = await client.query<T>(query, [...values, key]);\r\n\r\n if (result.rowCount === 0) {\r\n const error = createDatabaseError(\r\n DatabaseErrorCode.NOT_FOUND,\r\n `Record not found in table: ${table}`,\r\n undefined,\r\n { table, key, correlationId: this.correlationId }\r\n );\r\n this.trackError(error, 'update', { table, key });\r\n return createFailedResult(error);\r\n }\r\n\r\n this.recordSuccess();\r\n return createSuccessResult(result.rows[0], result.rowCount || 0);\r\n } catch (err) {\r\n const errorCode = mapPostgresError(err);\r\n const error = createDatabaseError(\r\n errorCode,\r\n `Failed to update record in table: ${table}`,\r\n err instanceof Error ? err : new Error(String(err)),\r\n { table, key, data, correlationId: this.correlationId }\r\n );\r\n this.trackError(error, 'update', { table, key });\r\n return createFailedResult(error);\r\n }\r\n }\r\n\r\n async delete(table: string, key: string, transactionId?: string): Promise<OperationResult<void>> {\r\n this.ensureConnected();\r\n\r\n try {\r\n const query = `DELETE FROM ${this.sanitizeIdentifier(table)} WHERE id = $1`;\r\n\r\n const client = this.getQueryClient(transactionId);\r\n const result = await client.query(query, [key]);\r\n\r\n if (result.rowCount === 0) {\r\n const error = createDatabaseError(\r\n DatabaseErrorCode.NOT_FOUND,\r\n `Record not found in table: ${table}`,\r\n undefined,\r\n { table, key, correlationId: this.correlationId }\r\n );\r\n this.trackError(error, 'delete', { table, key });\r\n return createFailedResult(error);\r\n }\r\n\r\n this.recordSuccess();\r\n return createSuccessResult(undefined, result.rowCount || 0);\r\n } catch (err) {\r\n const errorCode = mapPostgresError(err);\r\n const error = createDatabaseError(\r\n errorCode,\r\n `Failed to delete record from table: ${table}`,\r\n err instanceof Error ? err : new Error(String(err)),\r\n { table, key, correlationId: this.correlationId }\r\n );\r\n this.trackError(error, 'delete', { table, key });\r\n return createFailedResult(error);\r\n }\r\n }\r\n\r\n async raw<T = any>(query: string, params?: any[], transactionId?: string): Promise<T> {\r\n this.ensureConnected();\r\n\r\n try {\r\n const client = this.getQueryClient(transactionId);\r\n const result = await client.query<T>(query, params);\r\n this.recordSuccess();\r\n return result.rows as T;\r\n } catch (err) {\r\n const errorCode = mapPostgresError(err);\r\n const error = createDatabaseError(\r\n errorCode,\r\n `Failed to execute raw query`,\r\n err instanceof Error ? err : new Error(String(err)),\r\n { query, params, correlationId: this.correlationId }\r\n );\r\n this.trackError(error, 'raw', { query });\r\n throw error;\r\n }\r\n }\r\n\r\n async beginTransaction(): Promise<TransactionContext> {\r\n this.ensureConnected();\r\n\r\n const client = await this.pool!.connect();\r\n\r\n const context: TransactionContext = {\r\n id: `postgres-tx-${Date.now()}`,\r\n databases: ['postgres'],\r\n startTime: new Date(),\r\n status: 'pending',\r\n };\r\n\r\n await client.query('BEGIN');\r\n this.transactions.set(context.id, { context, client });\r\n\r\n return context;\r\n }\r\n\r\n async prepareTransaction(context: TransactionContext): Promise<boolean> {\r\n const transaction = this.transactions.get(context.id);\r\n\r\n if (!transaction) {\r\n throw createDatabaseError(\r\n DatabaseErrorCode.TRANSACTION_FAILED,\r\n 'Transaction not found',\r\n undefined,\r\n { transactionId: context.id }\r\n );\r\n }\r\n\r\n try {\r\n // PostgreSQL supports PREPARE TRANSACTION for two-phase commit\r\n // Note: This requires max_prepared_transactions > 0 in postgresql.conf\r\n await transaction.client.query(`PREPARE TRANSACTION '${context.id}'`);\r\n context.status = 'prepared';\r\n context.preparedAt = new Date();\r\n return true;\r\n } catch (err) {\r\n // If prepare fails, the transaction is still active and can be rolled back\r\n throw createDatabaseError(\r\n DatabaseErrorCode.TRANSACTION_FAILED,\r\n 'Failed to prepare transaction',\r\n err instanceof Error ? err : new Error(String(err)),\r\n { transactionId: context.id }\r\n );\r\n }\r\n }\r\n\r\n async commitTransaction(context: TransactionContext): Promise<void> {\r\n const transaction = this.transactions.get(context.id);\r\n\r\n if (!transaction) {\r\n throw createDatabaseError(\r\n DatabaseErrorCode.TRANSACTION_FAILED,\r\n 'Transaction not found',\r\n undefined,\r\n { transactionId: context.id }\r\n );\r\n }\r\n\r\n try {\r\n // If transaction was prepared, use COMMIT PREPARED\r\n if (context.status === 'prepared') {\r\n await transaction.client.query(`COMMIT PREPARED '${context.id}'`);\r\n } else {\r\n await transaction.client.query('COMMIT');\r\n }\r\n context.status = 'committed';\r\n } finally {\r\n transaction.client.release();\r\n this.transactions.delete(context.id);\r\n }\r\n }\r\n\r\n async rollbackTransaction(context: TransactionContext): Promise<void> {\r\n const transaction = this.transactions.get(context.id);\r\n\r\n if (!transaction) {\r\n throw createDatabaseError(\r\n DatabaseErrorCode.TRANSACTION_FAILED,\r\n 'Transaction not found',\r\n undefined,\r\n { transactionId: context.id }\r\n );\r\n }\r\n\r\n try {\r\n // If transaction was prepared, use ROLLBACK PREPARED\r\n if (context.status === 'prepared') {\r\n await transaction.client.query(`ROLLBACK PREPARED '${context.id}'`);\r\n } else {\r\n await transaction.client.query('ROLLBACK');\r\n }\r\n context.status = 'rolled_back';\r\n } finally {\r\n transaction.client.release();\r\n this.transactions.delete(context.id);\r\n }\r\n }\r\n\r\n private ensureConnected(): void {\r\n if (!this.isConnected()) {\r\n throw createDatabaseError(\r\n DatabaseErrorCode.CONNECTION_FAILED,\r\n 'Not connected to PostgreSQL',\r\n undefined,\r\n { config: this.config }\r\n );\r\n }\r\n }\r\n\r\n private sanitizeIdentifier(identifier: string): string {\r\n // Remove any characters that aren't alphanumeric or underscore\r\n return identifier.replace(/[^a-zA-Z0-9_]/g, '');\r\n }\r\n\r\n /**\r\n * Get client for query execution (transaction client if available, otherwise pool)\r\n */\r\n private getQueryClient(transactionId?: string): Pool | PoolClient {\r\n if (transactionId) {\r\n const transaction = this.transactions.get(transactionId);\r\n if (transaction) {\r\n return transaction.client;\r\n }\r\n }\r\n return this.pool!;\r\n }\r\n\r\n private buildWhereClause<T>(filter: QueryFilter<T>, params: any[]): string {\r\n const field = this.sanitizeIdentifier(String(filter.field));\r\n\r\n switch (filter.operator) {\r\n case 'eq': {\r\n const paramIndex = params.length + 1;\r\n params.push(filter.value);\r\n return `${field} = $${paramIndex}`;\r\n }\r\n case 'ne': {\r\n const paramIndex = params.length + 1;\r\n params.push(filter.value);\r\n return `${field} != $${paramIndex}`;\r\n }\r\n case 'gt': {\r\n const paramIndex = params.length + 1;\r\n params.push(filter.value);\r\n return `${field} > $${paramIndex}`;\r\n }\r\n case 'gte': {\r\n const paramIndex = params.length + 1;\r\n params.push(filter.value);\r\n return `${field} >= $${paramIndex}`;\r\n }\r\n case 'lt': {\r\n const paramIndex = params.length + 1;\r\n params.push(filter.value);\r\n return `${field} < $${paramIndex}`;\r\n }\r\n case 'lte': {\r\n const paramIndex = params.length + 1;\r\n params.push(filter.value);\r\n return `${field} <= $${paramIndex}`;\r\n }\r\n case 'in': {\r\n if (!Array.isArray(filter.value)) {\r\n throw new TypeError(`Field '${String(filter.field)}' with operator 'in' requires an array value`);\r\n }\r\n if (filter.value.length === 0) {\r\n // Empty IN list - return false condition without invalid SQL\r\n return '1=0';\r\n }\r\n const startIndex = params.length + 1;\r\n const placeholders = filter.value.map((_, i) => `$${startIndex + i}`).join(', ');\r\n params.push(...filter.value);\r\n return `${field} IN (${placeholders})`;\r\n }\r\n case 'like': {\r\n const paramIndex = params.length + 1;\r\n params.push(`%${filter.value}%`);\r\n return `${field} LIKE $${paramIndex}`;\r\n }\r\n case 'between': {\r\n if (!Array.isArray(filter.value)) {\r\n throw new TypeError(`Field '${String(filter.field)}' with operator 'between' requires an array value`);\r\n }\r\n if (filter.value.length !== 2) {\r\n throw new TypeError(`Field '${String(filter.field)}' with operator 'between' requires exactly 2 elements, got ${filter.value.length}`);\r\n }\r\n const paramIndex = params.length + 1;\r\n params.push(filter.value[0], filter.value[1]);\r\n return `${field} BETWEEN $${paramIndex} AND $${paramIndex + 1}`;\r\n }\r\n default:\r\n return '1=1';\r\n }\r\n }\r\n}\r\n"],"names":["Pool","DatabaseErrorCode","createDatabaseError","createSuccessResult","createFailedResult","mapPostgresError","withDatabaseRetry","v4","uuidv4","PostgresAdapter","pool","config","connected","transactions","Map","errorAggregator","correlationId","getType","trackError","error","operation","context","dbError","code","QUERY_FAILED","Error","String","addError","recordSuccess","connect","password","connectionString","CONNECTION_FAILED","undefined","reason","poolConfig","host","port","database","user","username","max","poolSize","idleTimeoutMillis","timeout","connectionTimeoutMillis","client","release","err","disconnect","end","isConnected","get","key","transactionId","ensureConnected","parts","split","table","id","slice","join","query","sanitizeIdentifier","getQueryClient","result","rows","length","errorCode","list","options","params","paramIndex","filters","whereClauses","map","filter","buildWhereClause","orderBy","order","toUpperCase","limit","push","offset","insert","data","keys","Object","values","placeholders","_","i","columns","k","rowCount","insertMany","hasActiveTransaction","has","results","item","count","update","setClauses","NOT_FOUND","delete","raw","beginTransaction","Date","now","databases","startTime","status","set","prepareTransaction","transaction","TRANSACTION_FAILED","preparedAt","commitTransaction","rollbackTransaction","identifier","replace","field","operator","value","Array","isArray","TypeError","startIndex"],"mappings":"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"}
|