forgeos 0.1.0-alpha.4 → 0.1.0-alpha.41
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/.npmignore +10 -0
- package/AGENTS.md +142 -90
- package/CHANGELOG.md +433 -0
- package/LICENSE +21 -0
- package/README.md +67 -8
- package/adapters/go/README.md +23 -0
- package/adapters/go/go.mod +3 -0
- package/adapters/go/http.go +149 -0
- package/adapters/go/registry.go +234 -0
- package/adapters/go/types.go +136 -0
- package/adapters/java/README.md +68 -0
- package/adapters/java/pom.xml +34 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Auth.java +20 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Diagnostic.java +16 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Entry.java +38 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/EntryKind.java +16 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ErrorInfo.java +4 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Forge.java +94 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeCall.java +12 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeContext.java +11 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHandler.java +8 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHttpHandler.java +179 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeRegistry.java +121 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Json.java +14 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Manifest.java +14 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/RequestEnvelope.java +6 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ResponseEnvelope.java +25 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Risk.java +18 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Schemas.java +36 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Service.java +65 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/TransactionMode.java +18 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/TypedForgeHandler.java +6 -0
- package/adapters/java-spring-boot-starter/README.md +32 -0
- package/adapters/java-spring-boot-starter/pom.xml +36 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeCommand.java +22 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeExternalService.java +15 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeQuery.java +16 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeServiceBeanCondition.java +18 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringAutoConfiguration.java +16 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringRuntime.java +104 -0
- package/adapters/java-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +1 -0
- package/bin/forge.mjs +18 -0
- package/docs/cair-protocol.md +103 -0
- package/docs/changelog.md +548 -0
- package/docs/forge-protocol.md +189 -0
- package/examples/go-billing/go.mod +7 -0
- package/examples/go-billing/main.go +120 -0
- package/examples/java-billing/pom.xml +52 -0
- package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/CreateInvoiceInput.java +4 -0
- package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Invoice.java +11 -0
- package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Main.java +127 -0
- package/package.json +31 -3
- package/schemas/forge-manifest.schema.json +57 -0
- package/src/forge/_generated/releaseManifest.json +1 -2
- package/src/forge/_generated/releaseManifest.ts +3 -3
- package/src/forge/agent-adapters/index.ts +1584 -125
- package/src/forge/agent-adapters/types.ts +224 -1
- package/src/forge/agent-memory/bridge.ts +1333 -0
- package/src/forge/agent-memory/context-pack.ts +277 -0
- package/src/forge/agent-memory/hook-runner.ts +312 -0
- package/src/forge/agent-memory/mcp.ts +224 -0
- package/src/forge/agent-memory/normalize.ts +498 -0
- package/src/forge/agent-memory/redaction.ts +103 -0
- package/src/forge/agent-memory/sources/claude-code.ts +51 -0
- package/src/forge/agent-memory/sources/codex-hook-runner.mjs +273 -0
- package/src/forge/agent-memory/sources/codex.ts +119 -0
- package/src/forge/agent-memory/sources/cursor.ts +35 -0
- package/src/forge/agent-memory/types.ts +204 -0
- package/src/forge/bench.ts +248 -0
- package/src/forge/brownfield-import/index.ts +801 -0
- package/src/forge/brownfield-import/types.ts +127 -0
- package/src/forge/cair/action-journal.ts +61 -0
- package/src/forge/cair/action-parser.ts +314 -0
- package/src/forge/cair/action-validator.ts +40 -0
- package/src/forge/cair/actions.ts +1818 -0
- package/src/forge/cair/format.ts +77 -0
- package/src/forge/cair/index.ts +106 -0
- package/src/forge/cair/query.ts +478 -0
- package/src/forge/cair/snapshot.ts +315 -0
- package/src/forge/cair/types.ts +248 -0
- package/src/forge/cli/ai.ts +339 -21
- package/src/forge/cli/auth.ts +348 -10
- package/src/forge/cli/authmd.ts +441 -0
- package/src/forge/cli/baseline.ts +112 -0
- package/src/forge/cli/build.ts +20 -4
- package/src/forge/cli/changed.ts +437 -0
- package/src/forge/cli/codex-app-server.ts +877 -0
- package/src/forge/cli/commands.ts +1620 -20
- package/src/forge/cli/db.ts +209 -2
- package/src/forge/cli/deploy.ts +887 -0
- package/src/forge/cli/deps.ts +82 -13
- package/src/forge/cli/dev.ts +961 -46
- package/src/forge/cli/docs.ts +265 -0
- package/src/forge/cli/doctor.ts +297 -0
- package/src/forge/cli/field-test.ts +329 -0
- package/src/forge/cli/handoff.ts +336 -0
- package/src/forge/cli/index.ts +1 -0
- package/src/forge/cli/last-run.ts +84 -0
- package/src/forge/cli/main.ts +107 -3
- package/src/forge/cli/new.ts +152 -13
- package/src/forge/cli/next-actions.ts +23 -0
- package/src/forge/cli/output.ts +389 -6
- package/src/forge/cli/parse.ts +1050 -34
- package/src/forge/cli/progress.ts +51 -0
- package/src/forge/cli/query.ts +32 -0
- package/src/forge/cli/release.ts +35 -11
- package/src/forge/cli/rls.ts +43 -4
- package/src/forge/cli/run.ts +41 -0
- package/src/forge/cli/security.ts +112 -0
- package/src/forge/cli/self-host.ts +56 -14
- package/src/forge/cli/studio.ts +2218 -0
- package/src/forge/cli/verify.ts +1455 -32
- package/src/forge/cli/windows.ts +23 -0
- package/src/forge/cli/workos.ts +1020 -0
- package/src/forge/compiler/agent-contract/build.ts +670 -64
- package/src/forge/compiler/agent-contract/types.ts +45 -2
- package/src/forge/compiler/ai-registry/parse.ts +71 -2
- package/src/forge/compiler/api-surface/build.ts +47 -0
- package/src/forge/compiler/app-graph/build.ts +112 -9
- package/src/forge/compiler/app-graph/extract.ts +107 -0
- package/src/forge/compiler/app-graph/module-graph.ts +73 -78
- package/src/forge/compiler/app-graph/parser.ts +24 -24
- package/src/forge/compiler/app-graph/profile.ts +26 -0
- package/src/forge/compiler/app-graph/versions.ts +1 -1
- package/src/forge/compiler/classifier/capabilities.ts +3 -2
- package/src/forge/compiler/classifier/classify.ts +32 -8
- package/src/forge/compiler/classifier/secrets.ts +3 -2
- package/src/forge/compiler/classifier/signals.ts +91 -1
- package/src/forge/compiler/client-sdk/build-manifest.ts +59 -0
- package/src/forge/compiler/client-sdk/render-client.ts +295 -13
- package/src/forge/compiler/data-graph/parse.ts +20 -4
- package/src/forge/compiler/data-graph/sql/ddl.ts +144 -31
- package/src/forge/compiler/data-graph/sql/serialize.ts +4 -0
- package/src/forge/compiler/data-graph/sql/types.ts +1 -0
- package/src/forge/compiler/diagnostics/codes.ts +30 -0
- package/src/forge/compiler/diagnostics/create.ts +21 -2
- package/src/forge/compiler/diagnostics/index.ts +2 -0
- package/src/forge/compiler/emitter/barrel.ts +3 -0
- package/src/forge/compiler/emitter/render.ts +9 -0
- package/src/forge/compiler/external-manifest/registry.ts +205 -0
- package/src/forge/compiler/external-manifest/types.ts +91 -0
- package/src/forge/compiler/external-manifest/validate.ts +373 -0
- package/src/forge/compiler/frontend-graph/build.ts +142 -14
- package/src/forge/compiler/integration/add.ts +618 -24
- package/src/forge/compiler/integration/plan.ts +43 -12
- package/src/forge/compiler/integration/render.ts +29 -0
- package/src/forge/compiler/integration/snapshot.ts +2 -0
- package/src/forge/compiler/integration/templates/convex.ts +70 -0
- package/src/forge/compiler/integration/templates/index.ts +1 -0
- package/src/forge/compiler/integration/templates/render.ts +38 -0
- package/src/forge/compiler/integration/templates/types.ts +3 -0
- package/src/forge/compiler/integration/templates/workos.ts +1219 -0
- package/src/forge/compiler/make-registry/build.ts +8 -9
- package/src/forge/compiler/orchestrator/fast-check.ts +4 -0
- package/src/forge/compiler/orchestrator/generate-lock.ts +14 -3
- package/src/forge/compiler/orchestrator/manifest.ts +1 -1
- package/src/forge/compiler/orchestrator/plan-profile.ts +23 -0
- package/src/forge/compiler/orchestrator/plan.ts +247 -10
- package/src/forge/compiler/orchestrator/profile.ts +65 -0
- package/src/forge/compiler/orchestrator/run.ts +97 -31
- package/src/forge/compiler/orchestrator/serialize.ts +33 -8
- package/src/forge/compiler/orchestrator/types.ts +1 -1
- package/src/forge/compiler/package-graph/compiler.ts +3 -1
- package/src/forge/compiler/package-graph/constants.ts +1 -1
- package/src/forge/compiler/package-manager/adapter.ts +4 -1
- package/src/forge/compiler/package-manager/commands.ts +4 -0
- package/src/forge/compiler/package-manager/executor.ts +30 -1
- package/src/forge/compiler/package-manager/version.ts +36 -12
- package/src/forge/compiler/package-upgrades/planner.ts +155 -18
- package/src/forge/compiler/package-upgrades/types.ts +2 -0
- package/src/forge/compiler/policy-registry/build.ts +47 -2
- package/src/forge/compiler/policy-registry/parse.ts +32 -2
- package/src/forge/compiler/recipes/definitions.ts +63 -0
- package/src/forge/compiler/recipes/index.ts +2 -0
- package/src/forge/compiler/recipes/registry.ts +6 -0
- package/src/forge/compiler/test-graph/build.ts +11 -3
- package/src/forge/compiler/types/app-graph.ts +8 -2
- package/src/forge/compiler/types/cli.ts +75 -1
- package/src/forge/compiler/types/dev-manifest.ts +4 -0
- package/src/forge/compiler/types/emit.ts +2 -0
- package/src/forge/compiler/types/frontend-graph.ts +2 -2
- package/src/forge/compiler/types/integration.ts +1 -0
- package/src/forge/compiler/types/package-graph.ts +6 -0
- package/src/forge/compiler/types/policy-registry.ts +3 -1
- package/src/forge/delta/classifier.ts +52 -0
- package/src/forge/delta/explain.ts +238 -0
- package/src/forge/delta/git-observer.ts +43 -0
- package/src/forge/delta/ids.ts +44 -0
- package/src/forge/delta/index.ts +25 -0
- package/src/forge/delta/recorder.ts +540 -0
- package/src/forge/delta/redaction.ts +50 -0
- package/src/forge/delta/schema.ts +240 -0
- package/src/forge/delta/session.ts +142 -0
- package/src/forge/delta/status.ts +1202 -0
- package/src/forge/delta/store.ts +3284 -0
- package/src/forge/delta/timeline.ts +178 -0
- package/src/forge/dev/server.ts +808 -17
- package/src/forge/dev/types.ts +15 -1
- package/src/forge/dev/watch.ts +17 -7
- package/src/forge/dev-console/cycle.ts +280 -33
- package/src/forge/dev-console/types.ts +47 -1
- package/src/forge/impact/index.ts +204 -9
- package/src/forge/impact/types.ts +33 -1
- package/src/forge/intent/index.ts +35 -16
- package/src/forge/make/fields.ts +26 -0
- package/src/forge/make/index.ts +40 -6
- package/src/forge/make/templates.ts +155 -2
- package/src/forge/make/types.ts +2 -1
- package/src/forge/policy.ts +1 -1
- package/src/forge/react/index.ts +160 -2
- package/src/forge/refactor/index.ts +1 -0
- package/src/forge/repair/rules/index.ts +2 -2
- package/src/forge/review/index.ts +158 -12
- package/src/forge/review/types.ts +15 -0
- package/src/forge/runtime/auth/config.ts +17 -0
- package/src/forge/runtime/auth/evaluate.ts +15 -2
- package/src/forge/runtime/auth/resolve.ts +61 -6
- package/src/forge/runtime/auth/types.ts +1 -0
- package/src/forge/runtime/db/factory.ts +1 -3
- package/src/forge/runtime/db/generated-client.ts +13 -2
- package/src/forge/runtime/db/memory-adapter.ts +139 -32
- package/src/forge/runtime/db/pglite-adapter.ts +263 -2
- package/src/forge/runtime/db/postgres-adapter.ts +6 -3
- package/src/forge/runtime/executor.ts +129 -16
- package/src/forge/runtime/external/bridge.ts +649 -0
- package/src/forge/runtime/runner/run-entry.ts +16 -7
- package/src/forge/runtime/telemetry/buffer.ts +4 -1
- package/src/forge/runtime/telemetry/scrubber.ts +35 -5
- package/src/forge/runtime/webhooks/security.ts +12 -7
- package/src/forge/server.ts +17 -3
- package/src/forge/ui/index.ts +209 -0
- package/src/forge/ui/types.ts +1 -0
- package/src/forge/version.ts +1 -1
- package/src/forge/vue/index.ts +422 -0
- package/src/forge/workspace/baseline.ts +112 -0
- package/src/forge/workspace/change-summary.ts +250 -0
- package/src/forge/workspace/forge-cli.ts +42 -0
- package/src/forge/workspace/git-summary.ts +391 -0
- package/templates/agent-workroom/AGENTS.md +29 -0
- package/templates/agent-workroom/README.md +34 -0
- package/templates/agent-workroom/forge.config.ts +3 -0
- package/templates/agent-workroom/package.json +33 -0
- package/templates/agent-workroom/src/actions/indexAgentSignal.ts +10 -0
- package/templates/agent-workroom/src/commands/openWorkroom.ts +61 -0
- package/templates/agent-workroom/src/commands/recordAgentSignal.ts +119 -0
- package/templates/agent-workroom/src/commands/recordCheckRun.ts +52 -0
- package/templates/agent-workroom/src/forge/schema.ts +54 -0
- package/templates/agent-workroom/src/policies.ts +6 -0
- package/templates/agent-workroom/src/queries/listWorkrooms.ts +11 -0
- package/templates/agent-workroom/src/queries/liveWorkroom.ts +63 -0
- package/templates/agent-workroom/tsconfig.json +16 -0
- package/templates/agent-workroom/web/index.html +12 -0
- package/templates/agent-workroom/web/package.json +21 -0
- package/templates/agent-workroom/web/src/App.tsx +345 -0
- package/templates/agent-workroom/web/src/lib/forge.ts +13 -0
- package/templates/agent-workroom/web/src/main.tsx +13 -0
- package/templates/agent-workroom/web/src/styles.css +545 -0
- package/templates/agent-workroom/web/tsconfig.json +27 -0
- package/templates/b2b-support-web/package.json +1 -0
- package/templates/b2b-support-web/web/package.json +1 -1
- package/templates/minimal-web/package.json +1 -1
- package/templates/minimal-web/web/package.json +2 -2
- package/templates/nuxt-web/.vscode/settings.json +14 -0
- package/templates/nuxt-web/README.md +30 -0
- package/templates/nuxt-web/forge.config.ts +3 -0
- package/templates/nuxt-web/package.json +33 -0
- package/templates/nuxt-web/src/actions/logNoteCreated.ts +11 -0
- package/templates/nuxt-web/src/commands/createNote.ts +26 -0
- package/templates/nuxt-web/src/forge/schema.ts +12 -0
- package/templates/nuxt-web/src/policies.ts +6 -0
- package/templates/nuxt-web/src/queries/listNotes.ts +8 -0
- package/templates/nuxt-web/src/queries/liveNotes.ts +8 -0
- package/templates/nuxt-web/tsconfig.json +17 -0
- package/templates/nuxt-web/web/app.vue +67 -0
- package/templates/nuxt-web/web/components/LiveNotes.vue +89 -0
- package/templates/nuxt-web/web/components/NoteComposer.vue +100 -0
- package/templates/nuxt-web/web/composables/forge.ts +13 -0
- package/templates/nuxt-web/web/composables/useNotes.ts +24 -0
- package/templates/nuxt-web/web/nuxt.config.ts +11 -0
- package/templates/nuxt-web/web/package.json +18 -0
- package/templates/nuxt-web/web/plugins/forge.client.ts +10 -0
- package/templates/nuxt-web/web/plugins/forge.server.ts +10 -0
- package/templates/nuxt-web/web/server/api/forge-health.get.ts +7 -0
- package/templates/nuxt-web/web/tsconfig.json +6 -0
- package/src/forge/_generated/actionSubscriptions.json +0 -2
- package/src/forge/_generated/actionSubscriptions.ts +0 -10
- package/src/forge/_generated/agentAdapterManifest.json +0 -2
- package/src/forge/_generated/agentAdapterManifest.ts +0 -73
- package/src/forge/_generated/agentContract.json +0 -2
- package/src/forge/_generated/agentContract.ts +0 -7829
- package/src/forge/_generated/agentQuickstart.md +0 -34
- package/src/forge/_generated/agentTools.json +0 -2
- package/src/forge/_generated/agentTools.md +0 -16
- package/src/forge/_generated/agentTools.ts +0 -12
- package/src/forge/_generated/aiContext.ts +0 -125
- package/src/forge/_generated/aiModels.json +0 -2
- package/src/forge/_generated/aiModels.ts +0 -51
- package/src/forge/_generated/aiProviders.json +0 -2
- package/src/forge/_generated/aiProviders.ts +0 -23
- package/src/forge/_generated/aiRegistry.json +0 -2
- package/src/forge/_generated/aiRegistry.ts +0 -31
- package/src/forge/_generated/api.json +0 -2
- package/src/forge/_generated/api.ts +0 -8
- package/src/forge/_generated/appGraph.json +0 -2
- package/src/forge/_generated/appGraph.ts +0 -14919
- package/src/forge/_generated/appMap.md +0 -55
- package/src/forge/_generated/artifactManifest.json +0 -2
- package/src/forge/_generated/artifactManifest.ts +0 -7
- package/src/forge/_generated/authClaims.json +0 -2
- package/src/forge/_generated/authClaims.ts +0 -13
- package/src/forge/_generated/authConfig.json +0 -2
- package/src/forge/_generated/authConfig.ts +0 -17
- package/src/forge/_generated/authContext.ts +0 -23
- package/src/forge/_generated/authRegistry.json +0 -2
- package/src/forge/_generated/authRegistry.ts +0 -25
- package/src/forge/_generated/buildInfo.json +0 -2
- package/src/forge/_generated/buildInfo.ts +0 -9
- package/src/forge/_generated/capabilityMap.json +0 -2
- package/src/forge/_generated/capabilityMap.md +0 -15
- package/src/forge/_generated/capabilityMap.ts +0 -17
- package/src/forge/_generated/client.ts +0 -282
- package/src/forge/_generated/clientApi.ts +0 -9
- package/src/forge/_generated/clientManifest.json +0 -2
- package/src/forge/_generated/clientManifest.ts +0 -39
- package/src/forge/_generated/clientTypes.ts +0 -78
- package/src/forge/_generated/configRegistry.json +0 -2
- package/src/forge/_generated/configRegistry.ts +0 -4
- package/src/forge/_generated/dataGraph.json +0 -2
- package/src/forge/_generated/dataGraph.ts +0 -8
- package/src/forge/_generated/db.json +0 -2
- package/src/forge/_generated/db.ts +0 -2
- package/src/forge/_generated/dbSecurityManifest.json +0 -2
- package/src/forge/_generated/dbSecurityManifest.ts +0 -15
- package/src/forge/_generated/dbSessionContext.json +0 -2
- package/src/forge/_generated/dbSessionContext.ts +0 -39
- package/src/forge/_generated/deployManifest.json +0 -2
- package/src/forge/_generated/deployManifest.ts +0 -14
- package/src/forge/_generated/devManifest.json +0 -2
- package/src/forge/_generated/devManifest.ts +0 -62
- package/src/forge/_generated/envSchema.json +0 -2
- package/src/forge/_generated/envSchema.ts +0 -59
- package/src/forge/_generated/frontendGraph.json +0 -2
- package/src/forge/_generated/frontendGraph.ts +0 -27
- package/src/forge/_generated/importGuards.json +0 -2
- package/src/forge/_generated/importGuards.ts +0 -686
- package/src/forge/_generated/index.ts +0 -68
- package/src/forge/_generated/liveProductionManifest.json +0 -2
- package/src/forge/_generated/liveProductionManifest.ts +0 -23
- package/src/forge/_generated/liveProtocol.json +0 -2
- package/src/forge/_generated/liveProtocol.ts +0 -21
- package/src/forge/_generated/liveQueryRegistry.json +0 -2
- package/src/forge/_generated/liveQueryRegistry.ts +0 -9
- package/src/forge/_generated/liveTransportConfig.json +0 -2
- package/src/forge/_generated/liveTransportConfig.ts +0 -19
- package/src/forge/_generated/makeRegistry.json +0 -2
- package/src/forge/_generated/makeRegistry.ts +0 -177
- package/src/forge/_generated/makeTemplates.json +0 -2
- package/src/forge/_generated/makeTemplates.ts +0 -66
- package/src/forge/_generated/mockMap.json +0 -2
- package/src/forge/_generated/mockMap.ts +0 -7
- package/src/forge/_generated/operationPlaybooks.md +0 -167
- package/src/forge/_generated/packageGraph.json +0 -2
- package/src/forge/_generated/packageGraph.ts +0 -249334
- package/src/forge/_generated/packageUpgradeRegistry.json +0 -2
- package/src/forge/_generated/packageUpgradeRegistry.ts +0 -15
- package/src/forge/_generated/permissionMatrix.json +0 -2
- package/src/forge/_generated/permissionMatrix.ts +0 -7
- package/src/forge/_generated/policyRegistry.json +0 -2
- package/src/forge/_generated/policyRegistry.ts +0 -11
- package/src/forge/_generated/queryRegistry.json +0 -2
- package/src/forge/_generated/queryRegistry.ts +0 -9
- package/src/forge/_generated/react.d.ts +0 -22
- package/src/forge/_generated/react.ts +0 -29
- package/src/forge/_generated/reactManifest.json +0 -2
- package/src/forge/_generated/reactManifest.ts +0 -19
- package/src/forge/_generated/rlsPolicies.json +0 -2
- package/src/forge/_generated/rlsPolicies.sql +0 -34
- package/src/forge/_generated/rlsPolicies.ts +0 -6
- package/src/forge/_generated/runtimeGraph.json +0 -2
- package/src/forge/_generated/runtimeGraph.ts +0 -8
- package/src/forge/_generated/runtimeMatrix.json +0 -2
- package/src/forge/_generated/runtimeMatrix.ts +0 -334130
- package/src/forge/_generated/runtimeRegistry.ts +0 -2
- package/src/forge/_generated/runtimeRules.md +0 -91
- package/src/forge/_generated/secretRegistry.json +0 -2
- package/src/forge/_generated/secretRegistry.ts +0 -50
- package/src/forge/_generated/secretsContext.ts +0 -11
- package/src/forge/_generated/serverApi.ts +0 -10
- package/src/forge/_generated/sourceMapManifest.json +0 -2
- package/src/forge/_generated/sourceMapManifest.ts +0 -7
- package/src/forge/_generated/sqlPlan.json +0 -2
- package/src/forge/_generated/sqlPlan.ts +0 -88
- package/src/forge/_generated/subscriptionManifest.json +0 -2
- package/src/forge/_generated/subscriptionManifest.ts +0 -7
- package/src/forge/_generated/symbolicationManifest.json +0 -2
- package/src/forge/_generated/symbolicationManifest.ts +0 -17
- package/src/forge/_generated/telemetryRegistry.json +0 -2
- package/src/forge/_generated/telemetryRegistry.ts +0 -9
- package/src/forge/_generated/telemetrySinks.json +0 -2
- package/src/forge/_generated/telemetrySinks.ts +0 -11
- package/src/forge/_generated/tenantScope.json +0 -2
- package/src/forge/_generated/tenantScope.ts +0 -8
- package/src/forge/_generated/testGraph.json +0 -2
- package/src/forge/_generated/testGraph.ts +0 -3430
- package/src/forge/_generated/testPlanRegistry.json +0 -2
- package/src/forge/_generated/testPlanRegistry.ts +0 -33
- package/src/forge/_generated/uiRoutes.json +0 -2
- package/src/forge/_generated/uiRoutes.ts +0 -16
- package/src/forge/_generated/uiScenarios.json +0 -2
- package/src/forge/_generated/uiScenarios.ts +0 -30
- package/src/forge/_generated/uiTestManifest.json +0 -2
- package/src/forge/_generated/uiTestManifest.ts +0 -27
- package/src/forge/_generated/workflowRegistry.json +0 -2
- package/src/forge/_generated/workflowRegistry.ts +0 -9
- package/src/forge/_generated/workflowSubscriptions.json +0 -2
- package/src/forge/_generated/workflowSubscriptions.ts +0 -10
|
@@ -7,6 +7,7 @@ interface MemoryRow {
|
|
|
7
7
|
interface MemoryTable {
|
|
8
8
|
rows: MemoryRow[];
|
|
9
9
|
nextSerial: number;
|
|
10
|
+
columnTypes: Map<string, string>;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
function normalizeRows(rows: MemoryRow[]): DbQueryResult {
|
|
@@ -16,8 +17,8 @@ function normalizeRows(rows: MemoryRow[]): DbQueryResult {
|
|
|
16
17
|
};
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
function parseNow():
|
|
20
|
-
return new Date()
|
|
20
|
+
function parseNow(): Date {
|
|
21
|
+
return new Date();
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
function compareValue(left: unknown, right: unknown): boolean {
|
|
@@ -52,6 +53,42 @@ function parseTableName(sql: string): string | null {
|
|
|
52
53
|
return null;
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
function parseColumnTypes(sql: string): Map<string, string> {
|
|
57
|
+
if (!parseTableName(sql)) {
|
|
58
|
+
return new Map();
|
|
59
|
+
}
|
|
60
|
+
const tableNameMatch =
|
|
61
|
+
sql.match(/CREATE TABLE IF NOT EXISTS\s+"?([^"\s(]+)"?/i) ??
|
|
62
|
+
sql.match(/CREATE TABLE\s+"?([^"\s(]+)"?/i);
|
|
63
|
+
if (!tableNameMatch?.[0]) {
|
|
64
|
+
return new Map();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const start = sql.indexOf("(", tableNameMatch.index! + tableNameMatch[0].length);
|
|
68
|
+
const end = sql.lastIndexOf(")");
|
|
69
|
+
if (start < 0 || end <= start) {
|
|
70
|
+
return new Map();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const types = new Map<string, string>();
|
|
74
|
+
for (const definition of splitSqlList(sql.slice(start + 1, end))) {
|
|
75
|
+
const trimmed = definition.trim();
|
|
76
|
+
if (/^(CONSTRAINT|PRIMARY|UNIQUE|FOREIGN|CHECK)\b/i.test(trimmed)) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const match = trimmed.match(/^"?(\w+)"?\s+([a-zA-Z][a-zA-Z0-9_]*(?:\s+precision)?)/);
|
|
80
|
+
if (match?.[1] && match?.[2]) {
|
|
81
|
+
types.set(match[1], match[2].toLowerCase());
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return types;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isTimestampType(sqlType: string | undefined): boolean {
|
|
88
|
+
if (!sqlType) return false;
|
|
89
|
+
return ["timestamp", "timestamptz", "timestamp with time zone", "timestamp without time zone"].includes(sqlType);
|
|
90
|
+
}
|
|
91
|
+
|
|
55
92
|
function splitSqlList(value: string): string[] {
|
|
56
93
|
const parts: string[] = [];
|
|
57
94
|
let current = "";
|
|
@@ -111,10 +148,68 @@ function parseSqlLiteral(token: string, params: unknown[]): unknown {
|
|
|
111
148
|
return Number.isFinite(numeric) ? numeric : token;
|
|
112
149
|
}
|
|
113
150
|
|
|
151
|
+
function parseUpdateExpression(
|
|
152
|
+
row: MemoryRow,
|
|
153
|
+
column: string,
|
|
154
|
+
expression: string,
|
|
155
|
+
params: unknown[],
|
|
156
|
+
): unknown {
|
|
157
|
+
const paramMatch = expression.match(/\$(\d+)/);
|
|
158
|
+
const normalizedExpression = expression.replace(/"/g, "").replace(/\s+/g, " ");
|
|
159
|
+
|
|
160
|
+
if (normalizedExpression.toLowerCase() === `${column.toLowerCase()} + 1`) {
|
|
161
|
+
return Number(row[column] ?? 0) + 1;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const coalesceMatch = expression.match(/^COALESCE\(\s*"?(\w+)"?\s*,\s*(.+)\)$/i);
|
|
165
|
+
if (coalesceMatch?.[1] && coalesceMatch[2]) {
|
|
166
|
+
const existing = row[coalesceMatch[1]];
|
|
167
|
+
return existing ?? parseUpdateExpression(row, column, coalesceMatch[2].trim(), params);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (paramMatch?.[1]) {
|
|
171
|
+
return params[Number(paramMatch[1]) - 1];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return parseSqlLiteral(expression, params);
|
|
175
|
+
}
|
|
176
|
+
|
|
114
177
|
function deterministicUuid(serial: number): string {
|
|
115
178
|
return `00000000-0000-0000-0000-${String(serial).padStart(12, "0")}`;
|
|
116
179
|
}
|
|
117
180
|
|
|
181
|
+
function coerceColumnValue(tableName: string, table: MemoryTable, column: string, value: unknown): unknown {
|
|
182
|
+
if (!isTimestampType(table.columnTypes.get(column))) {
|
|
183
|
+
return value;
|
|
184
|
+
}
|
|
185
|
+
if (value === null || value === undefined) {
|
|
186
|
+
return value;
|
|
187
|
+
}
|
|
188
|
+
if (value === "") {
|
|
189
|
+
throw new Error(`FORGE_DB_INVALID_TIMESTAMP: empty timestamp value for ${tableName}.${column}`);
|
|
190
|
+
}
|
|
191
|
+
if (value instanceof Date) {
|
|
192
|
+
if (Number.isNaN(value.getTime())) {
|
|
193
|
+
throw new Error(`FORGE_DB_INVALID_TIMESTAMP: invalid timestamp value for ${tableName}.${column}`);
|
|
194
|
+
}
|
|
195
|
+
return value;
|
|
196
|
+
}
|
|
197
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
198
|
+
const date = new Date(value);
|
|
199
|
+
if (Number.isNaN(date.getTime())) {
|
|
200
|
+
throw new Error(`FORGE_DB_INVALID_TIMESTAMP: invalid timestamp value for ${tableName}.${column}`);
|
|
201
|
+
}
|
|
202
|
+
return date;
|
|
203
|
+
}
|
|
204
|
+
throw new Error(`FORGE_DB_INVALID_TIMESTAMP: unsupported timestamp value for ${tableName}.${column}`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function coerceRowValues(tableName: string, table: MemoryTable, row: MemoryRow): void {
|
|
208
|
+
for (const column of Object.keys(row)) {
|
|
209
|
+
row[column] = coerceColumnValue(tableName, table, column, row[column]);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
118
213
|
function applySystemDefaults(tableName: string, row: MemoryRow): void {
|
|
119
214
|
const now = parseNow();
|
|
120
215
|
|
|
@@ -203,6 +298,16 @@ function projectRows(sql: string, rows: MemoryRow[]): MemoryRow[] {
|
|
|
203
298
|
});
|
|
204
299
|
}
|
|
205
300
|
|
|
301
|
+
function extractWhereClause(sql: string): string | null {
|
|
302
|
+
const whereIndex = sql.search(/\bWHERE\b/i);
|
|
303
|
+
if (whereIndex < 0) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
const afterWhere = sql.slice(whereIndex);
|
|
307
|
+
const endMatch = afterWhere.match(/\b(ORDER\s+BY|GROUP\s+BY|LIMIT|RETURNING)\b/i);
|
|
308
|
+
return endMatch?.index === undefined ? afterWhere : afterWhere.slice(0, endMatch.index);
|
|
309
|
+
}
|
|
310
|
+
|
|
206
311
|
export class MemoryAdapter implements DbAdapter {
|
|
207
312
|
readonly kind = "memory" as const;
|
|
208
313
|
private tables = new Map<string, MemoryTable>();
|
|
@@ -215,8 +320,11 @@ export class MemoryAdapter implements DbAdapter {
|
|
|
215
320
|
const match =
|
|
216
321
|
trimmed.match(/CREATE TABLE IF NOT EXISTS "([^"]+)"/i) ??
|
|
217
322
|
trimmed.match(/CREATE TABLE IF NOT EXISTS ([a-z_][a-z0-9_]*)/i);
|
|
218
|
-
if (match?.[1]
|
|
219
|
-
this.
|
|
323
|
+
if (match?.[1]) {
|
|
324
|
+
const table = this.ensureTable(match[1]);
|
|
325
|
+
for (const [column, sqlType] of parseColumnTypes(trimmed)) {
|
|
326
|
+
table.columnTypes.set(column, sqlType);
|
|
327
|
+
}
|
|
220
328
|
}
|
|
221
329
|
return { rows: [], rowCount: 0 };
|
|
222
330
|
}
|
|
@@ -331,6 +439,7 @@ export class MemoryAdapter implements DbAdapter {
|
|
|
331
439
|
}
|
|
332
440
|
|
|
333
441
|
applySystemDefaults(tableName, row);
|
|
442
|
+
coerceRowValues(tableName, table, row);
|
|
334
443
|
|
|
335
444
|
table.rows.push(row);
|
|
336
445
|
|
|
@@ -474,31 +583,33 @@ export class MemoryAdapter implements DbAdapter {
|
|
|
474
583
|
}
|
|
475
584
|
|
|
476
585
|
private filterRows(rows: MemoryRow[], sql: string, params: unknown[]): MemoryRow[] {
|
|
477
|
-
|
|
586
|
+
const whereSql = extractWhereClause(sql) ?? sql;
|
|
587
|
+
|
|
588
|
+
if (/status\s*=\s*'pending'/i.test(whereSql) && /next_attempt_at\s*<=\s*now\(\)/i.test(whereSql)) {
|
|
478
589
|
return rows.filter((row) => row.status === "pending");
|
|
479
590
|
}
|
|
480
591
|
|
|
481
|
-
if (/trace_id\s*=\s*\$\d+/i.test(
|
|
592
|
+
if (/trace_id\s*=\s*\$\d+/i.test(whereSql)) {
|
|
482
593
|
return rows.filter((row) => row.trace_id === params[0]);
|
|
483
594
|
}
|
|
484
595
|
|
|
485
|
-
if (/status\s+IN\s*\(\s*'pending'\s*,\s*'running'\s*,\s*'failed'\s*\)/i.test(
|
|
596
|
+
if (/status\s+IN\s*\(\s*'pending'\s*,\s*'running'\s*,\s*'failed'\s*\)/i.test(whereSql)) {
|
|
486
597
|
return rows.filter((row) =>
|
|
487
598
|
["pending", "running", "failed"].includes(String(row.status)),
|
|
488
599
|
);
|
|
489
600
|
}
|
|
490
601
|
|
|
491
|
-
if (/status\s+NOT\s+IN\s*\(\s*'completed'\s*,\s*'skipped'\s*\)/i.test(
|
|
602
|
+
if (/status\s+NOT\s+IN\s*\(\s*'completed'\s*,\s*'skipped'\s*\)/i.test(whereSql)) {
|
|
492
603
|
return rows.filter(
|
|
493
604
|
(row) => !["completed", "skipped"].includes(String(row.status)),
|
|
494
605
|
);
|
|
495
606
|
}
|
|
496
607
|
|
|
497
|
-
if (/status\s*=\s*'dead'/i.test(
|
|
608
|
+
if (/status\s*=\s*'dead'/i.test(whereSql) && /COUNT/i.test(sql)) {
|
|
498
609
|
return rows.filter((row) => row.status === "dead");
|
|
499
610
|
}
|
|
500
611
|
|
|
501
|
-
if (/step_index\s*<\s*\$\d+/i.test(
|
|
612
|
+
if (/step_index\s*<\s*\$\d+/i.test(whereSql) && /status\s*!=\s*'completed'/i.test(whereSql)) {
|
|
502
613
|
const threshold = Number(params[1]);
|
|
503
614
|
const runId = params[0];
|
|
504
615
|
return rows.filter(
|
|
@@ -509,16 +620,16 @@ export class MemoryAdapter implements DbAdapter {
|
|
|
509
620
|
);
|
|
510
621
|
}
|
|
511
622
|
|
|
512
|
-
if (/idempotency_key\s*=\s*\$\d+/i.test(
|
|
623
|
+
if (/idempotency_key\s*=\s*\$\d+/i.test(whereSql)) {
|
|
513
624
|
return rows.filter((row) => row.idempotency_key === params[0]);
|
|
514
625
|
}
|
|
515
626
|
|
|
516
|
-
if (/status\s*=\s*'dead'/i.test(
|
|
627
|
+
if (/status\s*=\s*'dead'/i.test(whereSql)) {
|
|
517
628
|
return rows.filter((row) => row.status === "dead");
|
|
518
629
|
}
|
|
519
630
|
|
|
520
631
|
const conditions = [
|
|
521
|
-
...
|
|
632
|
+
...whereSql.matchAll(
|
|
522
633
|
/(?:^|\s|AND\s)(?:\w+\.)?"?(\w+)"?\s*(=|<=|>=|>|<|!=)\s*(\$\d+|now\(\)|'[^']*'|"[^"]*"|\d+)/gi,
|
|
523
634
|
),
|
|
524
635
|
];
|
|
@@ -569,14 +680,12 @@ export class MemoryAdapter implements DbAdapter {
|
|
|
569
680
|
}
|
|
570
681
|
|
|
571
682
|
const setMatch = sql.match(/SET (.+?) WHERE/i);
|
|
572
|
-
const assignments = setMatch?.[1]
|
|
573
|
-
const
|
|
574
|
-
const whereColumn = whereMatch?.[1] ?? "id";
|
|
575
|
-
const whereValue = params[params.length - 1];
|
|
683
|
+
const assignments = setMatch?.[1] ? splitSqlList(setMatch[1]) : [];
|
|
684
|
+
const matchingRows = /WHERE/i.test(sql) ? new Set(this.filterRows(table.rows, sql, params)) : new Set(table.rows);
|
|
576
685
|
|
|
577
686
|
let updated = 0;
|
|
578
687
|
for (const row of table.rows) {
|
|
579
|
-
if (row
|
|
688
|
+
if (!matchingRows.has(row)) {
|
|
580
689
|
continue;
|
|
581
690
|
}
|
|
582
691
|
|
|
@@ -608,23 +717,20 @@ export class MemoryAdapter implements DbAdapter {
|
|
|
608
717
|
}
|
|
609
718
|
|
|
610
719
|
for (const assignment of assignments) {
|
|
611
|
-
const
|
|
612
|
-
|
|
720
|
+
const equalsIndex = assignment.indexOf("=");
|
|
721
|
+
if (equalsIndex < 0) {
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
724
|
+
const rawColumn = assignment.slice(0, equalsIndex);
|
|
725
|
+
const rawExpression = assignment.slice(equalsIndex + 1);
|
|
726
|
+
const column = rawColumn.trim().replace(/"/g, "");
|
|
613
727
|
if (!column) {
|
|
614
728
|
continue;
|
|
615
729
|
}
|
|
616
730
|
|
|
617
|
-
const expression = rawExpression
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
if (normalizedExpression.toLowerCase() === `${column.toLowerCase()} + 1`) {
|
|
622
|
-
row[column] = Number(row[column] ?? 0) + 1;
|
|
623
|
-
} else if (paramMatch?.[1]) {
|
|
624
|
-
row[column] = params[Number(paramMatch[1]) - 1];
|
|
625
|
-
} else {
|
|
626
|
-
row[column] = parseSqlLiteral(expression, params);
|
|
627
|
-
}
|
|
731
|
+
const expression = rawExpression.trim();
|
|
732
|
+
row[column] = parseUpdateExpression(row, column, expression, params);
|
|
733
|
+
row[column] = coerceColumnValue(tableName, table, column, row[column]);
|
|
628
734
|
}
|
|
629
735
|
|
|
630
736
|
updated += 1;
|
|
@@ -668,6 +774,7 @@ export class MemoryAdapter implements DbAdapter {
|
|
|
668
774
|
snapshot.set(name, {
|
|
669
775
|
rows: table.rows.map((row) => ({ ...row })),
|
|
670
776
|
nextSerial: table.nextSerial,
|
|
777
|
+
columnTypes: new Map(table.columnTypes),
|
|
671
778
|
});
|
|
672
779
|
}
|
|
673
780
|
const sequenceSnapshot = new Map(this.sequences);
|
|
@@ -695,7 +802,7 @@ export class MemoryAdapter implements DbAdapter {
|
|
|
695
802
|
if (existing) {
|
|
696
803
|
return existing;
|
|
697
804
|
}
|
|
698
|
-
const created = { rows: [], nextSerial: 1 };
|
|
805
|
+
const created = { rows: [], nextSerial: 1, columnTypes: new Map<string, string>() };
|
|
699
806
|
this.tables.set(name, created);
|
|
700
807
|
return created;
|
|
701
808
|
}
|
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
import { PGlite } from "@electric-sql/pglite";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, unlinkSync } from "node:fs";
|
|
3
|
+
import { basename, dirname, join } from "node:path";
|
|
2
4
|
import type { DbAdapter, DbQueryResult, DbTransaction } from "./adapter.ts";
|
|
3
5
|
|
|
6
|
+
export const DEFAULT_PGLITE_DIR = ".forge/pglite";
|
|
7
|
+
|
|
8
|
+
export type PgliteStoreState =
|
|
9
|
+
| "missing"
|
|
10
|
+
| "healthy"
|
|
11
|
+
| "active"
|
|
12
|
+
| "aborted"
|
|
13
|
+
| "unhealthy";
|
|
14
|
+
|
|
15
|
+
export interface PgliteStoreInspection {
|
|
16
|
+
dataDir: string;
|
|
17
|
+
exists: boolean;
|
|
18
|
+
state: PgliteStoreState;
|
|
19
|
+
openable: boolean;
|
|
20
|
+
postmasterPid?: number;
|
|
21
|
+
postmasterAlive?: boolean;
|
|
22
|
+
lockFiles: string[];
|
|
23
|
+
error?: string;
|
|
24
|
+
nextActions: string[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface PgliteStoreRepairResult {
|
|
28
|
+
ok: boolean;
|
|
29
|
+
repaired: boolean;
|
|
30
|
+
dataDir: string;
|
|
31
|
+
backupPath: string | null;
|
|
32
|
+
before: PgliteStoreInspection;
|
|
33
|
+
after: PgliteStoreInspection;
|
|
34
|
+
message: string;
|
|
35
|
+
nextActions: string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
4
38
|
function toQueryResult(result: {
|
|
5
39
|
rows: Record<string, unknown>[];
|
|
6
40
|
affectedRows?: number;
|
|
@@ -45,7 +79,234 @@ export class PgliteAdapter implements DbAdapter {
|
|
|
45
79
|
}
|
|
46
80
|
|
|
47
81
|
export async function createPgliteAdapter(dataDir: string): Promise<DbAdapter> {
|
|
82
|
+
mkdirSync(dataDir, { recursive: true });
|
|
83
|
+
repairStalePgliteStore(dataDir);
|
|
48
84
|
const adapter = new PgliteAdapter(dataDir);
|
|
49
|
-
|
|
50
|
-
|
|
85
|
+
try {
|
|
86
|
+
await adapter.query("SELECT 1");
|
|
87
|
+
return adapter;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
await adapter.close().catch(() => undefined);
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function inspectPgliteStore(dataDir: string): Promise<PgliteStoreInspection> {
|
|
95
|
+
const exists = existsSync(dataDir);
|
|
96
|
+
const lockFiles = [
|
|
97
|
+
"postmaster.pid",
|
|
98
|
+
".s.PGSQL.5432.lock",
|
|
99
|
+
".s.PGSQL.5432.lock.out",
|
|
100
|
+
].filter((file) => existsSync(join(dataDir, file)));
|
|
101
|
+
|
|
102
|
+
if (!exists) {
|
|
103
|
+
return {
|
|
104
|
+
dataDir,
|
|
105
|
+
exists: false,
|
|
106
|
+
state: "missing",
|
|
107
|
+
openable: true,
|
|
108
|
+
lockFiles,
|
|
109
|
+
nextActions: ["forge dev --db pglite --json"],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const pid = readPglitePostmasterPid(dataDir);
|
|
114
|
+
const postmasterAlive = Number.isInteger(pid) && pid > 0 ? isProcessAlive(pid) : undefined;
|
|
115
|
+
if (postmasterAlive) {
|
|
116
|
+
return {
|
|
117
|
+
dataDir,
|
|
118
|
+
exists: true,
|
|
119
|
+
state: "active",
|
|
120
|
+
openable: false,
|
|
121
|
+
postmasterPid: pid,
|
|
122
|
+
postmasterAlive,
|
|
123
|
+
lockFiles,
|
|
124
|
+
error: `PGlite store is currently owned by live pid ${pid}`,
|
|
125
|
+
nextActions: [
|
|
126
|
+
"stop the running forge dev process that owns .forge/pglite",
|
|
127
|
+
"forge doctor pglite --json",
|
|
128
|
+
],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let adapter: DbAdapter | null = null;
|
|
133
|
+
const previousExitCode = process.exitCode;
|
|
134
|
+
let restoreExitCode = false;
|
|
135
|
+
try {
|
|
136
|
+
adapter = new PgliteAdapter(dataDir);
|
|
137
|
+
await adapter.query("SELECT 1");
|
|
138
|
+
return {
|
|
139
|
+
dataDir,
|
|
140
|
+
exists: true,
|
|
141
|
+
state: "healthy",
|
|
142
|
+
openable: true,
|
|
143
|
+
...(Number.isInteger(pid) && pid > 0 ? { postmasterPid: pid, postmasterAlive: false } : {}),
|
|
144
|
+
lockFiles,
|
|
145
|
+
nextActions: ["forge dev --db pglite --json"],
|
|
146
|
+
};
|
|
147
|
+
} catch (error) {
|
|
148
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
149
|
+
const aborted = isPgliteAbortMessage(message);
|
|
150
|
+
if (aborted) {
|
|
151
|
+
restoreExitCode = true;
|
|
152
|
+
process.exitCode = previousExitCode;
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
dataDir,
|
|
156
|
+
exists: true,
|
|
157
|
+
state: aborted ? "aborted" : "unhealthy",
|
|
158
|
+
openable: false,
|
|
159
|
+
...(Number.isInteger(pid) && pid > 0 ? { postmasterPid: pid, postmasterAlive: false } : {}),
|
|
160
|
+
lockFiles,
|
|
161
|
+
error: message,
|
|
162
|
+
nextActions: [
|
|
163
|
+
"forge db repair --local --adapter pglite --json",
|
|
164
|
+
"forge dev --db memory --json",
|
|
165
|
+
],
|
|
166
|
+
};
|
|
167
|
+
} finally {
|
|
168
|
+
await adapter?.close().catch(() => undefined);
|
|
169
|
+
if (restoreExitCode) {
|
|
170
|
+
process.exitCode = previousExitCode;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function repairLocalPgliteStore(dataDir: string): Promise<PgliteStoreRepairResult> {
|
|
176
|
+
const before = await inspectPgliteStore(dataDir);
|
|
177
|
+
if (before.state === "active") {
|
|
178
|
+
return {
|
|
179
|
+
ok: false,
|
|
180
|
+
repaired: false,
|
|
181
|
+
dataDir,
|
|
182
|
+
backupPath: null,
|
|
183
|
+
before,
|
|
184
|
+
after: before,
|
|
185
|
+
message: "PGlite store is active; stop the owning forge dev process before repair.",
|
|
186
|
+
nextActions: before.nextActions,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (before.state === "missing") {
|
|
191
|
+
return {
|
|
192
|
+
ok: true,
|
|
193
|
+
repaired: false,
|
|
194
|
+
dataDir,
|
|
195
|
+
backupPath: null,
|
|
196
|
+
before,
|
|
197
|
+
after: before,
|
|
198
|
+
message: "No local PGlite store exists; nothing to repair.",
|
|
199
|
+
nextActions: before.nextActions,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (before.state === "healthy") {
|
|
204
|
+
return {
|
|
205
|
+
ok: true,
|
|
206
|
+
repaired: false,
|
|
207
|
+
dataDir,
|
|
208
|
+
backupPath: null,
|
|
209
|
+
before,
|
|
210
|
+
after: before,
|
|
211
|
+
message: "Local PGlite store is healthy; no repair needed.",
|
|
212
|
+
nextActions: before.nextActions,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const backupPath = archivePgliteStore(dataDir, before.state);
|
|
217
|
+
const after = await inspectPgliteStore(dataDir);
|
|
218
|
+
const ok = after.state === "missing" || after.state === "healthy";
|
|
219
|
+
return {
|
|
220
|
+
ok,
|
|
221
|
+
repaired: true,
|
|
222
|
+
dataDir,
|
|
223
|
+
backupPath,
|
|
224
|
+
before,
|
|
225
|
+
after,
|
|
226
|
+
message: backupPath
|
|
227
|
+
? `Archived local PGlite store to ${backupPath}.`
|
|
228
|
+
: "Removed stale local PGlite lock files; retry forge dev.",
|
|
229
|
+
nextActions: ok
|
|
230
|
+
? ["forge dev --db pglite --json"]
|
|
231
|
+
: ["forge dev --db memory --json", "forge doctor pglite --json"],
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function isPgliteAbortMessage(message: string): boolean {
|
|
236
|
+
return /Aborted\(\)\. Build with -sASSERTIONS/i.test(message) ||
|
|
237
|
+
(/pglite/i.test(message) && /abort/i.test(message));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function repairStalePgliteStore(dataDir: string): void {
|
|
241
|
+
const pidPath = join(dataDir, "postmaster.pid");
|
|
242
|
+
if (!existsSync(pidPath)) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const pid = readPglitePostmasterPid(dataDir);
|
|
247
|
+
const stale =
|
|
248
|
+
!Number.isInteger(pid) ||
|
|
249
|
+
pid <= 0 ||
|
|
250
|
+
!isProcessAlive(pid);
|
|
251
|
+
|
|
252
|
+
if (!stale) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
removeStalePgliteLocks(dataDir);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function readPglitePostmasterPid(dataDir: string): number {
|
|
260
|
+
try {
|
|
261
|
+
const rawPid = readFileSync(join(dataDir, "postmaster.pid"), "utf8").split(/\r?\n/, 1)[0]?.trim();
|
|
262
|
+
return rawPid ? Number(rawPid) : NaN;
|
|
263
|
+
} catch {
|
|
264
|
+
return NaN;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function removeStalePgliteLocks(dataDir: string): void {
|
|
269
|
+
for (const file of [
|
|
270
|
+
"postmaster.pid",
|
|
271
|
+
".s.PGSQL.5432.lock",
|
|
272
|
+
".s.PGSQL.5432.lock.out",
|
|
273
|
+
]) {
|
|
274
|
+
try {
|
|
275
|
+
unlinkSync(join(dataDir, file));
|
|
276
|
+
} catch {
|
|
277
|
+
// Best effort. If PGlite still cannot open, the caller reports the real open error.
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export function archivePgliteStore(dataDir: string, reason = "repair"): string | null {
|
|
283
|
+
if (!existsSync(dataDir)) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const parent = dirname(dataDir);
|
|
288
|
+
const backupsDir = join(parent, `${basename(dataDir)}.backups`);
|
|
289
|
+
mkdirSync(backupsDir, { recursive: true });
|
|
290
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
291
|
+
const backupPath = join(backupsDir, `${basename(dataDir)}.${reason}.${stamp}`);
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
renameSync(dataDir, backupPath);
|
|
295
|
+
mkdirSync(dataDir, { recursive: true });
|
|
296
|
+
return backupPath;
|
|
297
|
+
} catch {
|
|
298
|
+
rmSync(join(dataDir, "postmaster.pid"), { force: true });
|
|
299
|
+
rmSync(join(dataDir, ".s.PGSQL.5432.lock"), { force: true });
|
|
300
|
+
rmSync(join(dataDir, ".s.PGSQL.5432.lock.out"), { force: true });
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function isProcessAlive(pid: number): boolean {
|
|
306
|
+
try {
|
|
307
|
+
process.kill(pid, 0);
|
|
308
|
+
return true;
|
|
309
|
+
} catch {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
51
312
|
}
|
|
@@ -12,7 +12,7 @@ function loadBunSql(databaseUrl: string): PostgresClient | null {
|
|
|
12
12
|
try {
|
|
13
13
|
const bunGlobal = globalThis as {
|
|
14
14
|
Bun?: {
|
|
15
|
-
SQL?: new (url: string) => {
|
|
15
|
+
SQL?: new (options: string | { url: string; max?: number }) => {
|
|
16
16
|
unsafe: (sql: string, params?: unknown[]) => Promise<Record<string, unknown>[]>;
|
|
17
17
|
close: () => Promise<void>;
|
|
18
18
|
};
|
|
@@ -23,7 +23,7 @@ function loadBunSql(databaseUrl: string): PostgresClient | null {
|
|
|
23
23
|
return null;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const client = new bunGlobal.Bun.SQL(databaseUrl);
|
|
26
|
+
const client = new bunGlobal.Bun.SQL({ url: databaseUrl, max: 1 });
|
|
27
27
|
return {
|
|
28
28
|
query: async (sql, params = []) => {
|
|
29
29
|
const rows = await client.unsafe(sql, params);
|
|
@@ -42,7 +42,10 @@ async function loadPostgresJs(databaseUrl: string): Promise<PostgresClient | nul
|
|
|
42
42
|
try {
|
|
43
43
|
const module = await import("postgres");
|
|
44
44
|
const createSql = module.default;
|
|
45
|
-
const sql = createSql(databaseUrl
|
|
45
|
+
const sql = createSql(databaseUrl, {
|
|
46
|
+
max: 1,
|
|
47
|
+
onnotice: () => undefined,
|
|
48
|
+
});
|
|
46
49
|
return {
|
|
47
50
|
query: async (statement, params = []) => {
|
|
48
51
|
const rows = await sql.unsafe(statement, params as never[]);
|