hungry-ghost-hive 0.48.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/base-agent.d.ts +11 -11
- package/dist/agents/base-agent.d.ts.map +1 -1
- package/dist/agents/base-agent.js +25 -25
- package/dist/agents/base-agent.js.map +1 -1
- package/dist/agents/base-agent.test.js +2 -1
- package/dist/agents/base-agent.test.js.map +1 -1
- package/dist/agents/intermediate.d.ts +2 -0
- package/dist/agents/intermediate.d.ts.map +1 -1
- package/dist/agents/intermediate.js +25 -18
- package/dist/agents/intermediate.js.map +1 -1
- package/dist/agents/junior.d.ts +2 -0
- package/dist/agents/junior.d.ts.map +1 -1
- package/dist/agents/junior.js +25 -18
- package/dist/agents/junior.js.map +1 -1
- package/dist/agents/qa.d.ts +2 -0
- package/dist/agents/qa.d.ts.map +1 -1
- package/dist/agents/qa.js +47 -38
- package/dist/agents/qa.js.map +1 -1
- package/dist/agents/senior.d.ts +2 -0
- package/dist/agents/senior.d.ts.map +1 -1
- package/dist/agents/senior.js +40 -27
- package/dist/agents/senior.js.map +1 -1
- package/dist/agents/tech-lead.d.ts +2 -0
- package/dist/agents/tech-lead.d.ts.map +1 -1
- package/dist/agents/tech-lead.js +37 -31
- package/dist/agents/tech-lead.js.map +1 -1
- package/dist/cli/commands/add-repo.js +2 -2
- package/dist/cli/commands/add-repo.js.map +1 -1
- package/dist/cli/commands/add-repo.test.js +1 -1
- package/dist/cli/commands/add-repo.test.js.map +1 -1
- package/dist/cli/commands/agents.d.ts.map +1 -1
- package/dist/cli/commands/agents.js +12 -10
- package/dist/cli/commands/agents.js.map +1 -1
- package/dist/cli/commands/agents.test.js +7 -7
- package/dist/cli/commands/agents.test.js.map +1 -1
- package/dist/cli/commands/approach.js +2 -2
- package/dist/cli/commands/approach.js.map +1 -1
- package/dist/cli/commands/approvals.js +7 -7
- package/dist/cli/commands/approvals.js.map +1 -1
- package/dist/cli/commands/approvals.test.js +8 -8
- package/dist/cli/commands/approvals.test.js.map +1 -1
- package/dist/cli/commands/assign.js +4 -4
- package/dist/cli/commands/assign.js.map +1 -1
- package/dist/cli/commands/assign.test.js +18 -16
- package/dist/cli/commands/assign.test.js.map +1 -1
- package/dist/cli/commands/cleanup.d.ts.map +1 -1
- package/dist/cli/commands/cleanup.js +8 -8
- package/dist/cli/commands/cleanup.js.map +1 -1
- package/dist/cli/commands/cleanup.test.js +5 -1
- package/dist/cli/commands/cleanup.test.js.map +1 -1
- package/dist/cli/commands/escalations.js +9 -7
- package/dist/cli/commands/escalations.js.map +1 -1
- package/dist/cli/commands/escalations.test.js +2 -2
- package/dist/cli/commands/escalations.test.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +48 -5
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/init.test.js +4 -0
- package/dist/cli/commands/init.test.js.map +1 -1
- package/dist/cli/commands/manager/agent-monitoring.d.ts +2 -2
- package/dist/cli/commands/manager/agent-monitoring.d.ts.map +1 -1
- package/dist/cli/commands/manager/agent-monitoring.js +1 -1
- package/dist/cli/commands/manager/agent-monitoring.js.map +1 -1
- package/dist/cli/commands/manager/auditor-lifecycle.js +3 -3
- package/dist/cli/commands/manager/auditor-lifecycle.js.map +1 -1
- package/dist/cli/commands/manager/auditor-lifecycle.test.js +21 -14
- package/dist/cli/commands/manager/auditor-lifecycle.test.js.map +1 -1
- package/dist/cli/commands/manager/auto-reject-comment-only-reviews.test.js +28 -23
- package/dist/cli/commands/manager/auto-reject-comment-only-reviews.test.js.map +1 -1
- package/dist/cli/commands/manager/escalation-handler.d.ts +2 -2
- package/dist/cli/commands/manager/escalation-handler.d.ts.map +1 -1
- package/dist/cli/commands/manager/escalation-handler.js +11 -10
- package/dist/cli/commands/manager/escalation-handler.js.map +1 -1
- package/dist/cli/commands/manager/escalation-handler.test.js +8 -8
- package/dist/cli/commands/manager/escalation-handler.test.js.map +1 -1
- package/dist/cli/commands/manager/feature-sign-off.js +7 -7
- package/dist/cli/commands/manager/feature-sign-off.js.map +1 -1
- package/dist/cli/commands/manager/feature-sign-off.test.js +40 -31
- package/dist/cli/commands/manager/feature-sign-off.test.js.map +1 -1
- package/dist/cli/commands/manager/feature-test-result.d.ts.map +1 -1
- package/dist/cli/commands/manager/feature-test-result.js +12 -13
- package/dist/cli/commands/manager/feature-test-result.js.map +1 -1
- package/dist/cli/commands/manager/handoff-recovery.d.ts.map +1 -1
- package/dist/cli/commands/manager/handoff-recovery.js +14 -15
- package/dist/cli/commands/manager/handoff-recovery.js.map +1 -1
- package/dist/cli/commands/manager/index.d.ts.map +1 -1
- package/dist/cli/commands/manager/index.js +26 -26
- package/dist/cli/commands/manager/index.js.map +1 -1
- package/dist/cli/commands/manager/index.test.js +3 -3
- package/dist/cli/commands/manager/index.test.js.map +1 -1
- package/dist/cli/commands/manager/merged-story-cleanup.d.ts +2 -2
- package/dist/cli/commands/manager/merged-story-cleanup.d.ts.map +1 -1
- package/dist/cli/commands/manager/merged-story-cleanup.js +6 -7
- package/dist/cli/commands/manager/merged-story-cleanup.js.map +1 -1
- package/dist/cli/commands/manager/merged-story-cleanup.test.js +27 -18
- package/dist/cli/commands/manager/merged-story-cleanup.test.js.map +1 -1
- package/dist/cli/commands/manager/pr-sync-orchestrator.d.ts.map +1 -1
- package/dist/cli/commands/manager/pr-sync-orchestrator.js +46 -38
- package/dist/cli/commands/manager/pr-sync-orchestrator.js.map +1 -1
- package/dist/cli/commands/manager/qa-review-handler.d.ts.map +1 -1
- package/dist/cli/commands/manager/qa-review-handler.js +25 -22
- package/dist/cli/commands/manager/qa-review-handler.js.map +1 -1
- package/dist/cli/commands/manager/spin-down.d.ts.map +1 -1
- package/dist/cli/commands/manager/spin-down.js +23 -19
- package/dist/cli/commands/manager/spin-down.js.map +1 -1
- package/dist/cli/commands/manager/stale-escalations.d.ts +2 -3
- package/dist/cli/commands/manager/stale-escalations.d.ts.map +1 -1
- package/dist/cli/commands/manager/stale-escalations.js.map +1 -1
- package/dist/cli/commands/manager/stuck-story-helpers.js +8 -8
- package/dist/cli/commands/manager/stuck-story-helpers.js.map +1 -1
- package/dist/cli/commands/manager/stuck-story-processor.d.ts +2 -2
- package/dist/cli/commands/manager/stuck-story-processor.d.ts.map +1 -1
- package/dist/cli/commands/manager/stuck-story-processor.js +23 -22
- package/dist/cli/commands/manager/stuck-story-processor.js.map +1 -1
- package/dist/cli/commands/manager/tech-lead-lifecycle.js +6 -6
- package/dist/cli/commands/manager/tech-lead-lifecycle.js.map +1 -1
- package/dist/cli/commands/manager/types.d.ts +2 -3
- package/dist/cli/commands/manager/types.d.ts.map +1 -1
- package/dist/cli/commands/manager/types.js.map +1 -1
- package/dist/cli/commands/msg.test.js +2 -2
- package/dist/cli/commands/msg.test.js.map +1 -1
- package/dist/cli/commands/my-stories.d.ts.map +1 -1
- package/dist/cli/commands/my-stories.js +17 -18
- package/dist/cli/commands/my-stories.js.map +1 -1
- package/dist/cli/commands/my-stories.test.js +2 -2
- package/dist/cli/commands/my-stories.test.js.map +1 -1
- package/dist/cli/commands/nuke.test.js +1 -1
- package/dist/cli/commands/nuke.test.js.map +1 -1
- package/dist/cli/commands/pr.js +32 -32
- package/dist/cli/commands/pr.js.map +1 -1
- package/dist/cli/commands/pr.test.js +10 -6
- package/dist/cli/commands/pr.test.js.map +1 -1
- package/dist/cli/commands/progress.d.ts.map +1 -1
- package/dist/cli/commands/progress.js +4 -5
- package/dist/cli/commands/progress.js.map +1 -1
- package/dist/cli/commands/progress.test.js +1 -1
- package/dist/cli/commands/progress.test.js.map +1 -1
- package/dist/cli/commands/req-headless.test.d.ts +2 -0
- package/dist/cli/commands/req-headless.test.d.ts.map +1 -0
- package/dist/cli/commands/req-headless.test.js +128 -0
- package/dist/cli/commands/req-headless.test.js.map +1 -0
- package/dist/cli/commands/req-spawn.test.js +5 -1
- package/dist/cli/commands/req-spawn.test.js.map +1 -1
- package/dist/cli/commands/req.d.ts.map +1 -1
- package/dist/cli/commands/req.js +13 -14
- package/dist/cli/commands/req.js.map +1 -1
- package/dist/cli/commands/resume.d.ts.map +1 -1
- package/dist/cli/commands/resume.js +7 -8
- package/dist/cli/commands/resume.js.map +1 -1
- package/dist/cli/commands/resume.test.js +1 -1
- package/dist/cli/commands/resume.test.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +42 -40
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/status.test.js +1 -1
- package/dist/cli/commands/status.test.js.map +1 -1
- package/dist/cli/commands/stories.js +9 -9
- package/dist/cli/commands/stories.js.map +1 -1
- package/dist/cli/commands/stories.test.js +2 -2
- package/dist/cli/commands/stories.test.js.map +1 -1
- package/dist/cli/commands/teams.js +11 -11
- package/dist/cli/commands/teams.js.map +1 -1
- package/dist/cli/commands/teams.test.js +2 -2
- package/dist/cli/commands/teams.test.js.map +1 -1
- package/dist/cli/dashboard/index.d.ts +2 -2
- package/dist/cli/dashboard/index.d.ts.map +1 -1
- package/dist/cli/dashboard/index.js +29 -20
- package/dist/cli/dashboard/index.js.map +1 -1
- package/dist/cli/dashboard/index.test.js +34 -32
- package/dist/cli/dashboard/index.test.js.map +1 -1
- package/dist/cli/dashboard/panels/activity.d.ts +3 -3
- package/dist/cli/dashboard/panels/activity.d.ts.map +1 -1
- package/dist/cli/dashboard/panels/activity.js +1 -1
- package/dist/cli/dashboard/panels/activity.js.map +1 -1
- package/dist/cli/dashboard/panels/agents.d.ts +3 -3
- package/dist/cli/dashboard/panels/agents.d.ts.map +1 -1
- package/dist/cli/dashboard/panels/agents.js +2 -2
- package/dist/cli/dashboard/panels/agents.js.map +1 -1
- package/dist/cli/dashboard/panels/escalations.d.ts +3 -3
- package/dist/cli/dashboard/panels/escalations.d.ts.map +1 -1
- package/dist/cli/dashboard/panels/escalations.js +1 -1
- package/dist/cli/dashboard/panels/escalations.js.map +1 -1
- package/dist/cli/dashboard/panels/merge-queue.d.ts +3 -3
- package/dist/cli/dashboard/panels/merge-queue.d.ts.map +1 -1
- package/dist/cli/dashboard/panels/merge-queue.js +1 -1
- package/dist/cli/dashboard/panels/merge-queue.js.map +1 -1
- package/dist/cli/dashboard/panels/pipeline.d.ts +3 -3
- package/dist/cli/dashboard/panels/pipeline.d.ts.map +1 -1
- package/dist/cli/dashboard/panels/pipeline.js +1 -1
- package/dist/cli/dashboard/panels/pipeline.js.map +1 -1
- package/dist/config/schema.d.ts +85 -82
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +1 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/connectors/project-management/operations.d.ts +7 -7
- package/dist/connectors/project-management/operations.d.ts.map +1 -1
- package/dist/connectors/project-management/operations.js +2 -3
- package/dist/connectors/project-management/operations.js.map +1 -1
- package/dist/context-files/index.test.js +1 -0
- package/dist/context-files/index.test.js.map +1 -1
- package/dist/db/client.d.ts +6 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +7 -0
- package/dist/db/client.js.map +1 -1
- package/dist/db/postgres-provider.d.ts +43 -0
- package/dist/db/postgres-provider.d.ts.map +1 -0
- package/dist/db/postgres-provider.integration.test.d.ts +2 -0
- package/dist/db/postgres-provider.integration.test.d.ts.map +1 -0
- package/dist/db/postgres-provider.integration.test.js +399 -0
- package/dist/db/postgres-provider.integration.test.js.map +1 -0
- package/dist/db/postgres-provider.js +315 -0
- package/dist/db/postgres-provider.js.map +1 -0
- package/dist/db/postgres-provider.test.d.ts +2 -0
- package/dist/db/postgres-provider.test.d.ts.map +1 -0
- package/dist/db/postgres-provider.test.js +72 -0
- package/dist/db/postgres-provider.test.js.map +1 -0
- package/dist/db/provider.d.ts +59 -0
- package/dist/db/provider.d.ts.map +1 -0
- package/dist/db/provider.js +121 -0
- package/dist/db/provider.js.map +1 -0
- package/dist/db/provider.test.d.ts +2 -0
- package/dist/db/provider.test.d.ts.map +1 -0
- package/dist/db/provider.test.js +226 -0
- package/dist/db/provider.test.js.map +1 -0
- package/dist/db/queries/agents.d.ts +13 -13
- package/dist/db/queries/agents.d.ts.map +1 -1
- package/dist/db/queries/agents.js +27 -28
- package/dist/db/queries/agents.js.map +1 -1
- package/dist/db/queries/agents.test.js +113 -111
- package/dist/db/queries/agents.test.js.map +1 -1
- package/dist/db/queries/escalations.d.ts +16 -16
- package/dist/db/queries/escalations.d.ts.map +1 -1
- package/dist/db/queries/escalations.js +34 -35
- package/dist/db/queries/escalations.js.map +1 -1
- package/dist/db/queries/escalations.test.js +133 -131
- package/dist/db/queries/escalations.test.js.map +1 -1
- package/dist/db/queries/heartbeat.d.ts +5 -5
- package/dist/db/queries/heartbeat.d.ts.map +1 -1
- package/dist/db/queries/heartbeat.js +7 -23
- package/dist/db/queries/heartbeat.js.map +1 -1
- package/dist/db/queries/heartbeat.test.js +76 -76
- package/dist/db/queries/heartbeat.test.js.map +1 -1
- package/dist/db/queries/integration-sync.d.ts +7 -7
- package/dist/db/queries/integration-sync.d.ts.map +1 -1
- package/dist/db/queries/integration-sync.js +13 -14
- package/dist/db/queries/integration-sync.js.map +1 -1
- package/dist/db/queries/logs.d.ts +10 -10
- package/dist/db/queries/logs.d.ts.map +1 -1
- package/dist/db/queries/logs.js +44 -42
- package/dist/db/queries/logs.js.map +1 -1
- package/dist/db/queries/logs.test.js +149 -146
- package/dist/db/queries/logs.test.js.map +1 -1
- package/dist/db/queries/messages.d.ts +6 -6
- package/dist/db/queries/messages.d.ts.map +1 -1
- package/dist/db/queries/messages.js +12 -11
- package/dist/db/queries/messages.js.map +1 -1
- package/dist/db/queries/messages.test.js +47 -46
- package/dist/db/queries/messages.test.js.map +1 -1
- package/dist/db/queries/pull-requests.d.ts +18 -18
- package/dist/db/queries/pull-requests.d.ts.map +1 -1
- package/dist/db/queries/pull-requests.js +50 -48
- package/dist/db/queries/pull-requests.js.map +1 -1
- package/dist/db/queries/pull-requests.test.js +195 -198
- package/dist/db/queries/pull-requests.test.js.map +1 -1
- package/dist/db/queries/requirements.d.ts +8 -8
- package/dist/db/queries/requirements.d.ts.map +1 -1
- package/dist/db/queries/requirements.js +17 -18
- package/dist/db/queries/requirements.js.map +1 -1
- package/dist/db/queries/requirements.test.js +83 -81
- package/dist/db/queries/requirements.test.js.map +1 -1
- package/dist/db/queries/stories.d.ts +29 -29
- package/dist/db/queries/stories.d.ts.map +1 -1
- package/dist/db/queries/stories.js +58 -64
- package/dist/db/queries/stories.js.map +1 -1
- package/dist/db/queries/stories.test.js +172 -170
- package/dist/db/queries/stories.test.js.map +1 -1
- package/dist/db/queries/teams.d.ts +6 -6
- package/dist/db/queries/teams.d.ts.map +1 -1
- package/dist/db/queries/teams.js +11 -12
- package/dist/db/queries/teams.js.map +1 -1
- package/dist/db/queries/teams.test.js +36 -34
- package/dist/db/queries/teams.test.js.map +1 -1
- package/dist/integrations/jira/repair.test.js +26 -24
- package/dist/integrations/jira/repair.test.js.map +1 -1
- package/dist/integrations/jira/stories.d.ts +3 -3
- package/dist/integrations/jira/stories.d.ts.map +1 -1
- package/dist/integrations/jira/stories.js +12 -12
- package/dist/integrations/jira/stories.js.map +1 -1
- package/dist/integrations/jira/stories.test.js +10 -8
- package/dist/integrations/jira/stories.test.js.map +1 -1
- package/dist/integrations/jira/sync.d.ts +7 -7
- package/dist/integrations/jira/sync.d.ts.map +1 -1
- package/dist/integrations/jira/sync.js +17 -20
- package/dist/integrations/jira/sync.js.map +1 -1
- package/dist/integrations/jira/sync.test.js +63 -62
- package/dist/integrations/jira/sync.test.js.map +1 -1
- package/dist/integrations/jira/transitions.d.ts +3 -3
- package/dist/integrations/jira/transitions.d.ts.map +1 -1
- package/dist/integrations/jira/transitions.js +3 -3
- package/dist/integrations/jira/transitions.js.map +1 -1
- package/dist/orchestrator/agent-selector.d.ts +3 -3
- package/dist/orchestrator/agent-selector.d.ts.map +1 -1
- package/dist/orchestrator/agent-selector.js +5 -6
- package/dist/orchestrator/agent-selector.js.map +1 -1
- package/dist/orchestrator/dependency-resolver.d.ts +4 -4
- package/dist/orchestrator/dependency-resolver.d.ts.map +1 -1
- package/dist/orchestrator/dependency-resolver.js +6 -6
- package/dist/orchestrator/dependency-resolver.js.map +1 -1
- package/dist/orchestrator/feature-branch.d.ts +3 -3
- package/dist/orchestrator/feature-branch.d.ts.map +1 -1
- package/dist/orchestrator/feature-branch.js +9 -10
- package/dist/orchestrator/feature-branch.js.map +1 -1
- package/dist/orchestrator/feature-branch.test.js +80 -78
- package/dist/orchestrator/feature-branch.test.js.map +1 -1
- package/dist/orchestrator/orphan-recovery.d.ts +2 -2
- package/dist/orchestrator/orphan-recovery.d.ts.map +1 -1
- package/dist/orchestrator/orphan-recovery.js +10 -10
- package/dist/orchestrator/orphan-recovery.js.map +1 -1
- package/dist/orchestrator/scheduler.d.ts +4 -4
- package/dist/orchestrator/scheduler.d.ts.map +1 -1
- package/dist/orchestrator/scheduler.js +90 -76
- package/dist/orchestrator/scheduler.js.map +1 -1
- package/dist/orchestrator/scheduler.test.js +496 -374
- package/dist/orchestrator/scheduler.test.js.map +1 -1
- package/dist/utils/auto-merge.d.ts.map +1 -1
- package/dist/utils/auto-merge.js +74 -56
- package/dist/utils/auto-merge.js.map +1 -1
- package/dist/utils/auto-merge.test.js +101 -66
- package/dist/utils/auto-merge.test.js.map +1 -1
- package/dist/utils/cli-helpers.d.ts +5 -5
- package/dist/utils/cli-helpers.d.ts.map +1 -1
- package/dist/utils/cli-helpers.js +8 -9
- package/dist/utils/cli-helpers.js.map +1 -1
- package/dist/utils/cli-helpers.test.js +28 -30
- package/dist/utils/cli-helpers.test.js.map +1 -1
- package/dist/utils/paths.d.ts +6 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +12 -1
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/paths.test.js +1 -0
- package/dist/utils/paths.test.js.map +1 -1
- package/dist/utils/pr-sync.d.ts +10 -10
- package/dist/utils/pr-sync.d.ts.map +1 -1
- package/dist/utils/pr-sync.js +20 -21
- package/dist/utils/pr-sync.js.map +1 -1
- package/dist/utils/pr-sync.test.js +52 -50
- package/dist/utils/pr-sync.test.js.map +1 -1
- package/dist/utils/with-hive-context.d.ts.map +1 -1
- package/dist/utils/with-hive-context.js +70 -1
- package/dist/utils/with-hive-context.js.map +1 -1
- package/package.json +3 -1
- package/src/agents/base-agent.test.ts +2 -1
- package/src/agents/base-agent.ts +32 -28
- package/src/agents/intermediate.ts +27 -18
- package/src/agents/junior.ts +27 -18
- package/src/agents/qa.ts +54 -40
- package/src/agents/senior.ts +42 -27
- package/src/agents/tech-lead.ts +42 -32
- package/src/cli/commands/add-repo.test.ts +1 -1
- package/src/cli/commands/add-repo.ts +2 -2
- package/src/cli/commands/agents.test.ts +7 -7
- package/src/cli/commands/agents.ts +12 -10
- package/src/cli/commands/approach.ts +2 -2
- package/src/cli/commands/approvals.test.ts +8 -8
- package/src/cli/commands/approvals.ts +9 -7
- package/src/cli/commands/assign.test.ts +19 -18
- package/src/cli/commands/assign.ts +4 -4
- package/src/cli/commands/cleanup.test.ts +5 -1
- package/src/cli/commands/cleanup.ts +11 -9
- package/src/cli/commands/escalations.test.ts +2 -2
- package/src/cli/commands/escalations.ts +9 -7
- package/src/cli/commands/init.test.ts +5 -0
- package/src/cli/commands/init.ts +53 -5
- package/src/cli/commands/manager/agent-monitoring.ts +3 -3
- package/src/cli/commands/manager/auditor-lifecycle.test.ts +21 -14
- package/src/cli/commands/manager/auditor-lifecycle.ts +3 -3
- package/src/cli/commands/manager/auto-reject-comment-only-reviews.test.ts +28 -23
- package/src/cli/commands/manager/escalation-handler.test.ts +13 -13
- package/src/cli/commands/manager/escalation-handler.ts +19 -12
- package/src/cli/commands/manager/feature-sign-off.test.ts +40 -31
- package/src/cli/commands/manager/feature-sign-off.ts +7 -7
- package/src/cli/commands/manager/feature-test-result.ts +13 -16
- package/src/cli/commands/manager/handoff-recovery.ts +20 -20
- package/src/cli/commands/manager/index.test.ts +4 -4
- package/src/cli/commands/manager/index.ts +58 -59
- package/src/cli/commands/manager/merged-story-cleanup.test.ts +28 -19
- package/src/cli/commands/manager/merged-story-cleanup.ts +11 -14
- package/src/cli/commands/manager/pr-sync-orchestrator.ts +115 -110
- package/src/cli/commands/manager/qa-review-handler.ts +50 -63
- package/src/cli/commands/manager/spin-down.ts +27 -25
- package/src/cli/commands/manager/stale-escalations.ts +2 -3
- package/src/cli/commands/manager/stuck-story-helpers.ts +10 -10
- package/src/cli/commands/manager/stuck-story-processor.ts +56 -62
- package/src/cli/commands/manager/tech-lead-lifecycle.ts +6 -6
- package/src/cli/commands/manager/types.ts +2 -3
- package/src/cli/commands/msg.test.ts +2 -2
- package/src/cli/commands/my-stories.test.ts +4 -2
- package/src/cli/commands/my-stories.ts +22 -27
- package/src/cli/commands/nuke.test.ts +1 -1
- package/src/cli/commands/pr.test.ts +10 -6
- package/src/cli/commands/pr.ts +41 -32
- package/src/cli/commands/progress.test.ts +1 -1
- package/src/cli/commands/progress.ts +11 -6
- package/src/cli/commands/req-headless.test.ts +170 -0
- package/src/cli/commands/req-spawn.test.ts +12 -2
- package/src/cli/commands/req.ts +13 -14
- package/src/cli/commands/resume.test.ts +1 -1
- package/src/cli/commands/resume.ts +7 -8
- package/src/cli/commands/status.test.ts +1 -1
- package/src/cli/commands/status.ts +52 -40
- package/src/cli/commands/stories.test.ts +4 -2
- package/src/cli/commands/stories.ts +11 -11
- package/src/cli/commands/teams.test.ts +2 -2
- package/src/cli/commands/teams.ts +11 -11
- package/src/cli/dashboard/index.test.ts +35 -34
- package/src/cli/dashboard/index.ts +34 -23
- package/src/cli/dashboard/panels/activity.ts +10 -4
- package/src/cli/dashboard/panels/agents.ts +8 -5
- package/src/cli/dashboard/panels/escalations.ts +4 -4
- package/src/cli/dashboard/panels/merge-queue.ts +4 -4
- package/src/cli/dashboard/panels/pipeline.ts +10 -4
- package/src/config/schema.ts +1 -0
- package/src/connectors/project-management/operations.ts +9 -10
- package/src/context-files/index.test.ts +1 -0
- package/src/db/client.ts +17 -0
- package/src/db/pg-migrations/001-full-schema.sql +209 -0
- package/src/db/postgres-provider.integration.test.ts +574 -0
- package/src/db/postgres-provider.test.ts +97 -0
- package/src/db/postgres-provider.ts +364 -0
- package/src/db/provider.test.ts +283 -0
- package/src/db/provider.ts +161 -0
- package/src/db/queries/agents.test.ts +114 -113
- package/src/db/queries/agents.ts +50 -36
- package/src/db/queries/escalations.test.ts +134 -133
- package/src/db/queries/escalations.ts +72 -57
- package/src/db/queries/heartbeat.test.ts +77 -78
- package/src/db/queries/heartbeat.ts +24 -46
- package/src/db/queries/integration-sync.ts +26 -26
- package/src/db/queries/logs.test.ts +151 -148
- package/src/db/queries/logs.ts +78 -53
- package/src/db/queries/messages.test.ts +48 -50
- package/src/db/queries/messages.ts +26 -18
- package/src/db/queries/pull-requests.test.ts +194 -199
- package/src/db/queries/pull-requests.ts +117 -88
- package/src/db/queries/requirements.test.ts +84 -83
- package/src/db/queries/requirements.ts +33 -28
- package/src/db/queries/stories.test.ts +173 -172
- package/src/db/queries/stories.ts +141 -110
- package/src/db/queries/teams.test.ts +37 -36
- package/src/db/queries/teams.ts +22 -14
- package/src/integrations/jira/repair.test.ts +27 -26
- package/src/integrations/jira/stories.test.ts +15 -16
- package/src/integrations/jira/stories.ts +15 -15
- package/src/integrations/jira/sync.test.ts +68 -68
- package/src/integrations/jira/sync.ts +29 -39
- package/src/integrations/jira/transitions.ts +6 -6
- package/src/orchestrator/agent-selector.ts +9 -8
- package/src/orchestrator/dependency-resolver.ts +16 -7
- package/src/orchestrator/feature-branch.test.ts +85 -80
- package/src/orchestrator/feature-branch.ts +13 -14
- package/src/orchestrator/orphan-recovery.ts +14 -13
- package/src/orchestrator/scheduler.test.ts +536 -394
- package/src/orchestrator/scheduler.ts +129 -115
- package/src/utils/auto-merge.test.ts +102 -68
- package/src/utils/auto-merge.ts +161 -168
- package/src/utils/cli-helpers.test.ts +30 -32
- package/src/utils/cli-helpers.ts +15 -11
- package/src/utils/paths.test.ts +1 -0
- package/src/utils/paths.ts +14 -1
- package/src/utils/pr-sync.test.ts +55 -52
- package/src/utils/pr-sync.ts +27 -32
- package/src/utils/with-hive-context.ts +89 -1
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
// Licensed under the Hungry Ghost Hive License. See LICENSE.
|
|
2
|
+
|
|
3
|
+
import { existsSync, readFileSync } from 'fs';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import pg from 'pg';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import type { WritableDatabaseProvider } from './provider.js';
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = dirname(__filename);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Tables that require workspace_id scoping.
|
|
14
|
+
* The migrations table is intentionally excluded — it is shared across workspaces.
|
|
15
|
+
*/
|
|
16
|
+
const WORKSPACE_SCOPED_TABLES = new Set([
|
|
17
|
+
'teams',
|
|
18
|
+
'agents',
|
|
19
|
+
'requirements',
|
|
20
|
+
'stories',
|
|
21
|
+
'story_dependencies',
|
|
22
|
+
'agent_logs',
|
|
23
|
+
'escalations',
|
|
24
|
+
'pull_requests',
|
|
25
|
+
'messages',
|
|
26
|
+
'integration_sync',
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Convert SQLite-style positional parameters (?) to Postgres-style ($1, $2, ...).
|
|
31
|
+
* Handles quoted strings and avoids replacing ? inside string literals.
|
|
32
|
+
*/
|
|
33
|
+
export function convertParams(sql: string): string {
|
|
34
|
+
let paramIndex = 0;
|
|
35
|
+
let result = '';
|
|
36
|
+
let inSingleQuote = false;
|
|
37
|
+
let inDoubleQuote = false;
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < sql.length; i++) {
|
|
40
|
+
const char = sql[i];
|
|
41
|
+
const prevChar = i > 0 ? sql[i - 1] : '';
|
|
42
|
+
|
|
43
|
+
if (char === "'" && !inDoubleQuote && prevChar !== '\\') {
|
|
44
|
+
inSingleQuote = !inSingleQuote;
|
|
45
|
+
result += char;
|
|
46
|
+
} else if (char === '"' && !inSingleQuote && prevChar !== '\\') {
|
|
47
|
+
inDoubleQuote = !inDoubleQuote;
|
|
48
|
+
result += char;
|
|
49
|
+
} else if (char === '?' && !inSingleQuote && !inDoubleQuote) {
|
|
50
|
+
paramIndex++;
|
|
51
|
+
result += `$${paramIndex}`;
|
|
52
|
+
} else {
|
|
53
|
+
result += char;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Detect the target table from SQL statements.
|
|
62
|
+
* Supports INSERT INTO, UPDATE, DELETE FROM, and SELECT ... FROM patterns.
|
|
63
|
+
*/
|
|
64
|
+
function detectTable(sql: string): string | null {
|
|
65
|
+
const normalized = sql.replace(/\s+/g, ' ').trim().toUpperCase();
|
|
66
|
+
|
|
67
|
+
// INSERT INTO table
|
|
68
|
+
let match = normalized.match(/INSERT\s+INTO\s+(\w+)/);
|
|
69
|
+
if (match) return match[1].toLowerCase();
|
|
70
|
+
|
|
71
|
+
// UPDATE table
|
|
72
|
+
match = normalized.match(/UPDATE\s+(\w+)/);
|
|
73
|
+
if (match) return match[1].toLowerCase();
|
|
74
|
+
|
|
75
|
+
// DELETE FROM table
|
|
76
|
+
match = normalized.match(/DELETE\s+FROM\s+(\w+)/);
|
|
77
|
+
if (match) return match[1].toLowerCase();
|
|
78
|
+
|
|
79
|
+
// SELECT ... FROM table (first table in FROM clause)
|
|
80
|
+
match = normalized.match(/FROM\s+(\w+)/);
|
|
81
|
+
if (match) return match[1].toLowerCase();
|
|
82
|
+
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check if a SQL statement needs workspace_id injection.
|
|
88
|
+
*/
|
|
89
|
+
function needsWorkspaceScope(sql: string): boolean {
|
|
90
|
+
const table = detectTable(sql);
|
|
91
|
+
return table !== null && WORKSPACE_SCOPED_TABLES.has(table);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Inject workspace_id into INSERT statements.
|
|
96
|
+
* Transforms: INSERT INTO table (col1, col2) VALUES (?, ?)
|
|
97
|
+
* Into: INSERT INTO table (col1, col2, workspace_id) VALUES ($1, $2, $3)
|
|
98
|
+
*/
|
|
99
|
+
function injectInsertWorkspaceId(
|
|
100
|
+
sql: string,
|
|
101
|
+
workspaceId: string,
|
|
102
|
+
params: unknown[]
|
|
103
|
+
): { sql: string; params: unknown[] } {
|
|
104
|
+
const insertMatch = sql.match(/INSERT\s+INTO\s+(\w+)\s*\(([^)]+)\)\s*VALUES\s*\(([^)]+)\)/i);
|
|
105
|
+
if (!insertMatch) return { sql, params };
|
|
106
|
+
|
|
107
|
+
const columns = insertMatch[2];
|
|
108
|
+
const values = insertMatch[3];
|
|
109
|
+
|
|
110
|
+
// Don't inject if workspace_id is already present
|
|
111
|
+
if (columns.toLowerCase().includes('workspace_id')) {
|
|
112
|
+
return { sql, params };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const newSql = sql.replace(
|
|
116
|
+
/\(([^)]+)\)\s*VALUES\s*\(([^)]+)\)/i,
|
|
117
|
+
`(${columns}, workspace_id) VALUES (${values}, ?)`
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return { sql: newSql, params: [...params, workspaceId] };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Inject workspace_id into SELECT/UPDATE/DELETE WHERE clauses.
|
|
125
|
+
* Adds `AND workspace_id = ?` to existing WHERE, or `WHERE workspace_id = ?` if none.
|
|
126
|
+
*/
|
|
127
|
+
function injectWhereWorkspaceId(
|
|
128
|
+
sql: string,
|
|
129
|
+
workspaceId: string,
|
|
130
|
+
params: unknown[]
|
|
131
|
+
): { sql: string; params: unknown[] } {
|
|
132
|
+
const normalized = sql.replace(/\s+/g, ' ').trim();
|
|
133
|
+
|
|
134
|
+
// Don't inject if workspace_id is already in the query
|
|
135
|
+
if (normalized.toLowerCase().includes('workspace_id')) {
|
|
136
|
+
return { sql, params };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const upperSql = normalized.toUpperCase();
|
|
140
|
+
|
|
141
|
+
// Find WHERE clause position
|
|
142
|
+
const whereIndex = upperSql.indexOf(' WHERE ');
|
|
143
|
+
|
|
144
|
+
if (whereIndex !== -1) {
|
|
145
|
+
// Insert workspace_id condition right after WHERE
|
|
146
|
+
const before = normalized.substring(0, whereIndex + 7); // includes " WHERE "
|
|
147
|
+
const after = normalized.substring(whereIndex + 7);
|
|
148
|
+
return {
|
|
149
|
+
sql: `${before}workspace_id = ? AND ${after}`,
|
|
150
|
+
params: [workspaceId, ...params],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// No WHERE clause — find insertion point (before ORDER BY, GROUP BY, LIMIT, etc.)
|
|
155
|
+
const clausePatterns = [' ORDER BY', ' GROUP BY', ' HAVING', ' LIMIT', ' OFFSET', ' FOR UPDATE'];
|
|
156
|
+
let insertPos = normalized.length;
|
|
157
|
+
for (const pattern of clausePatterns) {
|
|
158
|
+
const idx = upperSql.indexOf(pattern);
|
|
159
|
+
if (idx !== -1 && idx < insertPos) {
|
|
160
|
+
insertPos = idx;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const before = normalized.substring(0, insertPos);
|
|
165
|
+
const after = normalized.substring(insertPos);
|
|
166
|
+
return {
|
|
167
|
+
sql: `${before} WHERE workspace_id = ?${after}`,
|
|
168
|
+
params: [workspaceId, ...params],
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Load a Postgres migration SQL file.
|
|
174
|
+
*/
|
|
175
|
+
function loadPgMigration(migrationName: string): string {
|
|
176
|
+
const candidatePaths = [
|
|
177
|
+
join(__dirname, 'pg-migrations', migrationName),
|
|
178
|
+
join(__dirname, '..', '..', 'src', 'db', 'pg-migrations', migrationName),
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
for (const migrationPath of candidatePaths) {
|
|
182
|
+
if (existsSync(migrationPath)) {
|
|
183
|
+
return readFileSync(migrationPath, 'utf-8');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
throw new Error(
|
|
188
|
+
`Postgres migration file not found: ${migrationName}. Checked: ${candidatePaths.join(', ')}`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const PG_MIGRATIONS = [{ name: '001-full-schema.sql' }];
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Postgres implementation of DatabaseProvider using node-postgres (pg).
|
|
196
|
+
* All queries are automatically scoped by workspace_id for multi-tenant isolation.
|
|
197
|
+
*/
|
|
198
|
+
export class PostgresProvider implements WritableDatabaseProvider {
|
|
199
|
+
private pool: pg.Pool;
|
|
200
|
+
private workspaceId: string;
|
|
201
|
+
|
|
202
|
+
constructor(connectionString: string, workspaceId: string) {
|
|
203
|
+
this.pool = new pg.Pool({ connectionString });
|
|
204
|
+
this.workspaceId = workspaceId;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Run Postgres migrations. Idempotent — safe to call multiple times.
|
|
209
|
+
*/
|
|
210
|
+
async runMigrations(): Promise<void> {
|
|
211
|
+
const client = await this.pool.connect();
|
|
212
|
+
try {
|
|
213
|
+
// Ensure migrations table exists (shared, no workspace_id)
|
|
214
|
+
await client.query(`
|
|
215
|
+
CREATE TABLE IF NOT EXISTS migrations (
|
|
216
|
+
id SERIAL PRIMARY KEY,
|
|
217
|
+
name TEXT NOT NULL UNIQUE,
|
|
218
|
+
applied_at TIMESTAMPTZ DEFAULT NOW()
|
|
219
|
+
)
|
|
220
|
+
`);
|
|
221
|
+
|
|
222
|
+
const { rows: applied } = await client.query('SELECT name FROM migrations');
|
|
223
|
+
const appliedSet = new Set(applied.map((r: { name: string }) => r.name));
|
|
224
|
+
|
|
225
|
+
for (const migration of PG_MIGRATIONS) {
|
|
226
|
+
if (appliedSet.has(migration.name)) continue;
|
|
227
|
+
|
|
228
|
+
const sql = loadPgMigration(migration.name);
|
|
229
|
+
await client.query('BEGIN');
|
|
230
|
+
try {
|
|
231
|
+
await client.query(sql);
|
|
232
|
+
await client.query('INSERT INTO migrations (name) VALUES ($1)', [migration.name]);
|
|
233
|
+
await client.query('COMMIT');
|
|
234
|
+
} catch (error) {
|
|
235
|
+
await client.query('ROLLBACK');
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} finally {
|
|
240
|
+
client.release();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async queryAll<T>(sql: string, params: unknown[] = []): Promise<T[]> {
|
|
245
|
+
let finalSql = sql;
|
|
246
|
+
let finalParams = params;
|
|
247
|
+
|
|
248
|
+
if (needsWorkspaceScope(sql)) {
|
|
249
|
+
const normalized = sql.replace(/\s+/g, ' ').trim().toUpperCase();
|
|
250
|
+
if (!normalized.startsWith('INSERT')) {
|
|
251
|
+
const result = injectWhereWorkspaceId(finalSql, this.workspaceId, finalParams);
|
|
252
|
+
finalSql = result.sql;
|
|
253
|
+
finalParams = result.params;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
finalSql = convertParams(finalSql);
|
|
258
|
+
const { rows } = await this.pool.query(finalSql, finalParams);
|
|
259
|
+
return rows as T[];
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async queryOne<T>(sql: string, params: unknown[] = []): Promise<T | undefined> {
|
|
263
|
+
const results = await this.queryAll<T>(sql, params);
|
|
264
|
+
return results[0];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async run(sql: string, params: unknown[] = []): Promise<void> {
|
|
268
|
+
let finalSql = sql;
|
|
269
|
+
let finalParams = params;
|
|
270
|
+
|
|
271
|
+
if (needsWorkspaceScope(sql)) {
|
|
272
|
+
const normalized = sql.replace(/\s+/g, ' ').trim().toUpperCase();
|
|
273
|
+
if (normalized.startsWith('INSERT')) {
|
|
274
|
+
const result = injectInsertWorkspaceId(finalSql, this.workspaceId, finalParams);
|
|
275
|
+
finalSql = result.sql;
|
|
276
|
+
finalParams = result.params;
|
|
277
|
+
} else {
|
|
278
|
+
const result = injectWhereWorkspaceId(finalSql, this.workspaceId, finalParams);
|
|
279
|
+
finalSql = result.sql;
|
|
280
|
+
finalParams = result.params;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
finalSql = convertParams(finalSql);
|
|
285
|
+
await this.pool.query(finalSql, finalParams);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async withTransaction<T>(fn: () => Promise<T> | T): Promise<T> {
|
|
289
|
+
const client = await this.pool.connect();
|
|
290
|
+
try {
|
|
291
|
+
await client.query('BEGIN');
|
|
292
|
+
const result = await fn();
|
|
293
|
+
await client.query('COMMIT');
|
|
294
|
+
return result;
|
|
295
|
+
} catch (error) {
|
|
296
|
+
try {
|
|
297
|
+
await client.query('ROLLBACK');
|
|
298
|
+
} catch (_error) {
|
|
299
|
+
// Ignore rollback errors
|
|
300
|
+
}
|
|
301
|
+
throw error;
|
|
302
|
+
} finally {
|
|
303
|
+
client.release();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* No-op for Postgres — data is persisted on every query.
|
|
309
|
+
*/
|
|
310
|
+
save(): void {
|
|
311
|
+
// Postgres persists automatically; no explicit save needed
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async close(): Promise<void> {
|
|
315
|
+
await this.pool.end();
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Test the database connection.
|
|
320
|
+
* @throws if the connection fails
|
|
321
|
+
*/
|
|
322
|
+
async testConnection(): Promise<void> {
|
|
323
|
+
const client = await this.pool.connect();
|
|
324
|
+
try {
|
|
325
|
+
await client.query('SELECT 1');
|
|
326
|
+
} finally {
|
|
327
|
+
client.release();
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Get the workspace_id this provider is scoped to.
|
|
333
|
+
*/
|
|
334
|
+
getWorkspaceId(): string {
|
|
335
|
+
return this.workspaceId;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Create a PostgresProvider from the HIVE_DATABASE_URL environment variable.
|
|
341
|
+
* Loads .env file via dotenv if available.
|
|
342
|
+
*/
|
|
343
|
+
export async function createPostgresProvider(workspaceId: string): Promise<PostgresProvider> {
|
|
344
|
+
// Load .env file if dotenv is available
|
|
345
|
+
try {
|
|
346
|
+
const dotenv = await import('dotenv');
|
|
347
|
+
dotenv.config();
|
|
348
|
+
} catch {
|
|
349
|
+
// dotenv not available, rely on environment variables
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const connectionString = process.env.HIVE_DATABASE_URL;
|
|
353
|
+
if (!connectionString) {
|
|
354
|
+
throw new Error(
|
|
355
|
+
'HIVE_DATABASE_URL environment variable is not set. ' +
|
|
356
|
+
'Set it in your environment or in a .env file.'
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const provider = new PostgresProvider(connectionString, workspaceId);
|
|
361
|
+
await provider.testConnection();
|
|
362
|
+
await provider.runMigrations();
|
|
363
|
+
return provider;
|
|
364
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
// Licensed under the Hungry Ghost Hive License. See LICENSE.
|
|
2
|
+
|
|
3
|
+
import initSqlJs, { type Database as SqlJsDatabase } from 'sql.js';
|
|
4
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { ReadOnlySqliteProvider, SqliteProvider } from './provider.js';
|
|
6
|
+
|
|
7
|
+
describe('SqliteProvider', () => {
|
|
8
|
+
let db: SqlJsDatabase;
|
|
9
|
+
let saveFn: ReturnType<typeof vi.fn>;
|
|
10
|
+
let provider: SqliteProvider;
|
|
11
|
+
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
const SQL = await initSqlJs();
|
|
14
|
+
db = new SQL.Database();
|
|
15
|
+
db.run('PRAGMA foreign_keys = ON');
|
|
16
|
+
db.run(`
|
|
17
|
+
CREATE TABLE IF NOT EXISTS test_items (
|
|
18
|
+
id TEXT PRIMARY KEY,
|
|
19
|
+
name TEXT NOT NULL,
|
|
20
|
+
value INTEGER
|
|
21
|
+
)
|
|
22
|
+
`);
|
|
23
|
+
saveFn = vi.fn();
|
|
24
|
+
provider = new SqliteProvider(db, saveFn);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('queryAll', () => {
|
|
28
|
+
it('should return all matching rows', async () => {
|
|
29
|
+
await provider.run('INSERT INTO test_items (id, name, value) VALUES (?, ?, ?)', [
|
|
30
|
+
'1',
|
|
31
|
+
'a',
|
|
32
|
+
10,
|
|
33
|
+
]);
|
|
34
|
+
await provider.run('INSERT INTO test_items (id, name, value) VALUES (?, ?, ?)', [
|
|
35
|
+
'2',
|
|
36
|
+
'b',
|
|
37
|
+
20,
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const results = await provider.queryAll<{ id: string; name: string; value: number }>(
|
|
41
|
+
'SELECT * FROM test_items ORDER BY id'
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
expect(results).toHaveLength(2);
|
|
45
|
+
expect(results[0]).toEqual({ id: '1', name: 'a', value: 10 });
|
|
46
|
+
expect(results[1]).toEqual({ id: '2', name: 'b', value: 20 });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should return empty array when no matches', async () => {
|
|
50
|
+
const results = await provider.queryAll('SELECT * FROM test_items');
|
|
51
|
+
expect(results).toEqual([]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should support parameterized queries', async () => {
|
|
55
|
+
await provider.run('INSERT INTO test_items (id, name, value) VALUES (?, ?, ?)', [
|
|
56
|
+
'1',
|
|
57
|
+
'a',
|
|
58
|
+
10,
|
|
59
|
+
]);
|
|
60
|
+
await provider.run('INSERT INTO test_items (id, name, value) VALUES (?, ?, ?)', [
|
|
61
|
+
'2',
|
|
62
|
+
'b',
|
|
63
|
+
20,
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
const results = await provider.queryAll<{ id: string; name: string; value: number }>(
|
|
67
|
+
'SELECT * FROM test_items WHERE value > ?',
|
|
68
|
+
[15]
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
expect(results).toHaveLength(1);
|
|
72
|
+
expect(results[0].id).toBe('2');
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('queryOne', () => {
|
|
77
|
+
it('should return the first matching row', async () => {
|
|
78
|
+
await provider.run('INSERT INTO test_items (id, name, value) VALUES (?, ?, ?)', [
|
|
79
|
+
'1',
|
|
80
|
+
'a',
|
|
81
|
+
10,
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
const result = await provider.queryOne<{ id: string; name: string; value: number }>(
|
|
85
|
+
'SELECT * FROM test_items WHERE id = ?',
|
|
86
|
+
['1']
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
expect(result).toEqual({ id: '1', name: 'a', value: 10 });
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should return undefined when no match', async () => {
|
|
93
|
+
const result = await provider.queryOne('SELECT * FROM test_items WHERE id = ?', [
|
|
94
|
+
'nonexistent',
|
|
95
|
+
]);
|
|
96
|
+
expect(result).toBeUndefined();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('run', () => {
|
|
101
|
+
it('should execute INSERT statements', async () => {
|
|
102
|
+
await provider.run('INSERT INTO test_items (id, name, value) VALUES (?, ?, ?)', [
|
|
103
|
+
'1',
|
|
104
|
+
'test',
|
|
105
|
+
42,
|
|
106
|
+
]);
|
|
107
|
+
|
|
108
|
+
const result = await provider.queryOne<{ id: string; name: string; value: number }>(
|
|
109
|
+
'SELECT * FROM test_items WHERE id = ?',
|
|
110
|
+
['1']
|
|
111
|
+
);
|
|
112
|
+
expect(result).toEqual({ id: '1', name: 'test', value: 42 });
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should execute UPDATE statements', async () => {
|
|
116
|
+
await provider.run('INSERT INTO test_items (id, name, value) VALUES (?, ?, ?)', [
|
|
117
|
+
'1',
|
|
118
|
+
'test',
|
|
119
|
+
42,
|
|
120
|
+
]);
|
|
121
|
+
await provider.run('UPDATE test_items SET value = ? WHERE id = ?', [99, '1']);
|
|
122
|
+
|
|
123
|
+
const result = await provider.queryOne<{ value: number }>(
|
|
124
|
+
'SELECT value FROM test_items WHERE id = ?',
|
|
125
|
+
['1']
|
|
126
|
+
);
|
|
127
|
+
expect(result?.value).toBe(99);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should execute DELETE statements', async () => {
|
|
131
|
+
await provider.run('INSERT INTO test_items (id, name, value) VALUES (?, ?, ?)', [
|
|
132
|
+
'1',
|
|
133
|
+
'test',
|
|
134
|
+
42,
|
|
135
|
+
]);
|
|
136
|
+
await provider.run('DELETE FROM test_items WHERE id = ?', ['1']);
|
|
137
|
+
|
|
138
|
+
const result = await provider.queryOne('SELECT * FROM test_items WHERE id = ?', ['1']);
|
|
139
|
+
expect(result).toBeUndefined();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('withTransaction', () => {
|
|
144
|
+
it('should commit on success and call save', async () => {
|
|
145
|
+
await provider.withTransaction(async () => {
|
|
146
|
+
await provider.run('INSERT INTO test_items (id, name, value) VALUES (?, ?, ?)', [
|
|
147
|
+
'1',
|
|
148
|
+
'a',
|
|
149
|
+
10,
|
|
150
|
+
]);
|
|
151
|
+
await provider.run('INSERT INTO test_items (id, name, value) VALUES (?, ?, ?)', [
|
|
152
|
+
'2',
|
|
153
|
+
'b',
|
|
154
|
+
20,
|
|
155
|
+
]);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const results = await provider.queryAll('SELECT * FROM test_items');
|
|
159
|
+
expect(results).toHaveLength(2);
|
|
160
|
+
expect(saveFn).toHaveBeenCalledOnce();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should rollback on error and not call save', async () => {
|
|
164
|
+
await expect(
|
|
165
|
+
provider.withTransaction(async () => {
|
|
166
|
+
await provider.run('INSERT INTO test_items (id, name, value) VALUES (?, ?, ?)', [
|
|
167
|
+
'1',
|
|
168
|
+
'a',
|
|
169
|
+
10,
|
|
170
|
+
]);
|
|
171
|
+
throw new Error('test error');
|
|
172
|
+
})
|
|
173
|
+
).rejects.toThrow('test error');
|
|
174
|
+
|
|
175
|
+
const results = await provider.queryAll('SELECT * FROM test_items');
|
|
176
|
+
expect(results).toHaveLength(0);
|
|
177
|
+
expect(saveFn).not.toHaveBeenCalled();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should support async functions', async () => {
|
|
181
|
+
await provider.withTransaction(async () => {
|
|
182
|
+
await provider.run('INSERT INTO test_items (id, name, value) VALUES (?, ?, ?)', [
|
|
183
|
+
'1',
|
|
184
|
+
'a',
|
|
185
|
+
10,
|
|
186
|
+
]);
|
|
187
|
+
await Promise.resolve();
|
|
188
|
+
await provider.run('INSERT INTO test_items (id, name, value) VALUES (?, ?, ?)', [
|
|
189
|
+
'2',
|
|
190
|
+
'b',
|
|
191
|
+
20,
|
|
192
|
+
]);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const results = await provider.queryAll('SELECT * FROM test_items');
|
|
196
|
+
expect(results).toHaveLength(2);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
describe('save', () => {
|
|
201
|
+
it('should call the save function', () => {
|
|
202
|
+
provider.save();
|
|
203
|
+
expect(saveFn).toHaveBeenCalledOnce();
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('should be a no-op when no save function provided', () => {
|
|
207
|
+
const providerNoSave = new SqliteProvider(db);
|
|
208
|
+
expect(() => providerNoSave.save()).not.toThrow();
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe('close', () => {
|
|
213
|
+
it('should close the underlying database', async () => {
|
|
214
|
+
await provider.close();
|
|
215
|
+
await expect(provider.queryAll('SELECT 1')).rejects.toThrow();
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe('db property', () => {
|
|
220
|
+
it('should expose the underlying sql.js database', () => {
|
|
221
|
+
expect(provider.db).toBe(db);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe('ReadOnlySqliteProvider', () => {
|
|
227
|
+
let db: SqlJsDatabase;
|
|
228
|
+
let provider: ReadOnlySqliteProvider;
|
|
229
|
+
|
|
230
|
+
beforeEach(async () => {
|
|
231
|
+
const SQL = await initSqlJs();
|
|
232
|
+
db = new SQL.Database();
|
|
233
|
+
db.run('PRAGMA foreign_keys = ON');
|
|
234
|
+
db.run(`
|
|
235
|
+
CREATE TABLE IF NOT EXISTS test_items (
|
|
236
|
+
id TEXT PRIMARY KEY,
|
|
237
|
+
name TEXT NOT NULL,
|
|
238
|
+
value INTEGER
|
|
239
|
+
)
|
|
240
|
+
`);
|
|
241
|
+
// Pre-populate some data
|
|
242
|
+
db.run("INSERT INTO test_items (id, name, value) VALUES ('1', 'a', 10)");
|
|
243
|
+
db.run("INSERT INTO test_items (id, name, value) VALUES ('2', 'b', 20)");
|
|
244
|
+
provider = new ReadOnlySqliteProvider(db);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe('queryAll', () => {
|
|
248
|
+
it('should return all matching rows', async () => {
|
|
249
|
+
const results = await provider.queryAll<{ id: string; name: string; value: number }>(
|
|
250
|
+
'SELECT * FROM test_items ORDER BY id'
|
|
251
|
+
);
|
|
252
|
+
expect(results).toHaveLength(2);
|
|
253
|
+
expect(results[0].name).toBe('a');
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe('queryOne', () => {
|
|
258
|
+
it('should return the first matching row', async () => {
|
|
259
|
+
const result = await provider.queryOne<{ name: string }>(
|
|
260
|
+
'SELECT name FROM test_items WHERE id = ?',
|
|
261
|
+
['1']
|
|
262
|
+
);
|
|
263
|
+
expect(result?.name).toBe('a');
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe('withTransaction', () => {
|
|
268
|
+
it('should commit on success without saving', async () => {
|
|
269
|
+
await provider.withTransaction(async () => {
|
|
270
|
+
await provider.run("INSERT INTO test_items (id, name, value) VALUES ('3', 'c', 30)");
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const results = await provider.queryAll('SELECT * FROM test_items');
|
|
274
|
+
expect(results).toHaveLength(3);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
describe('db property', () => {
|
|
279
|
+
it('should expose the underlying sql.js database', () => {
|
|
280
|
+
expect(provider.db).toBe(db);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
});
|