@treeseed/sdk 0.12.62 → 0.13.0-rc.10
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/LICENSE +13 -0
- package/README.md +33 -359
- package/dist/accounts/account-contracts.d.ts +170 -0
- package/dist/accounts/account-contracts.js +33 -0
- package/dist/agent-capacity/allocation.d.ts +186 -0
- package/dist/agent-capacity/allocation.js +226 -0
- package/dist/agent-capacity/artifact-mutation-receipt.d.ts +49 -0
- package/dist/agent-capacity/artifact-mutation-receipt.js +36 -0
- package/dist/agent-capacity/artifacts.d.ts +107 -0
- package/dist/agent-capacity/artifacts.js +39 -0
- package/dist/agent-capacity/authority/agent-authority-presets.d.ts +13 -0
- package/dist/agent-capacity/authority/agent-authority-presets.js +106 -0
- package/dist/agent-capacity/contracts/capacity/assignments/assignment-records.d.ts +269 -0
- package/dist/agent-capacity/contracts/capacity/assignments/assignment-records.js +10 -0
- package/dist/agent-capacity/contracts/capacity/communication/communication-records.d.ts +101 -0
- package/dist/agent-capacity/contracts/capacity/communication/communication-records.js +9 -0
- package/dist/agent-capacity/contracts/capacity/providers/supply-policy.d.ts +36 -0
- package/dist/agent-capacity/contracts/capacity/providers/supply-policy.js +4 -0
- package/dist/agent-capacity/contracts/capacity/workdays/workday-records.d.ts +188 -0
- package/dist/agent-capacity/contracts/configuration/configuration.d.ts +11 -0
- package/dist/agent-capacity/contracts/configuration/configuration.js +20 -0
- package/dist/agent-capacity/contracts/operations/research-workflow.d.ts +59 -0
- package/dist/agent-capacity/contracts/operations/research-workflow.js +16 -0
- package/dist/agent-capacity/contracts/operations/workflow-promotion.d.ts +24 -0
- package/dist/agent-capacity/contracts/projects/agents/project-agent-class.d.ts +37 -0
- package/dist/agent-capacity/contracts/runtime/runtime-observability.d.ts +335 -0
- package/dist/agent-capacity/contracts/support/agent-atlas.d.ts +185 -0
- package/dist/agent-capacity/contracts/support/agent-atlas.js +47 -0
- package/dist/agent-capacity/contracts/support/agent-lab-monitoring.d.ts +96 -0
- package/dist/agent-capacity/contracts/support/agent-lab-monitoring.js +14 -0
- package/dist/agent-capacity/contracts/support/authority/execution-mode.d.ts +10 -0
- package/dist/agent-capacity/contracts/support/authority/execution-mode.js +17 -0
- package/dist/agent-capacity/contracts/support/decision-work.d.ts +251 -0
- package/dist/agent-capacity/contracts/support/failure-records.d.ts +9 -0
- package/dist/agent-capacity/contracts/support/failure-records.js +0 -0
- package/dist/agent-capacity/contracts/support/financial-records.d.ts +124 -0
- package/dist/agent-capacity/contracts/support/financial-records.js +0 -0
- package/dist/agent-capacity/contracts/support/planning-records.d.ts +121 -0
- package/dist/agent-capacity/contracts/support/planning-records.js +0 -0
- package/dist/agent-capacity/contracts/support/research-citation.d.ts +15 -0
- package/dist/agent-capacity/contracts/support/research-citation.js +0 -0
- package/dist/agent-capacity/contracts/support/research-source-policy.d.ts +8 -0
- package/dist/agent-capacity/contracts/support/research-source-policy.js +0 -0
- package/dist/agent-capacity/contracts/support/time-capacity.d.ts +163 -0
- package/dist/agent-capacity/contracts/support/time-capacity.js +63 -0
- package/dist/agent-capacity/policy/capacity/allocations/allocation-hierarchy.d.ts +29 -0
- package/dist/agent-capacity/policy/capacity/allocations/allocation-hierarchy.js +110 -0
- package/dist/agent-capacity/validation/activity-profile.d.ts +12 -0
- package/dist/agent-capacity/validation/activity-profile.js +264 -0
- package/dist/agent-capacity/validation/agent-definition-schema.d.ts +9140 -0
- package/dist/agent-capacity/validation/agent-definition-schema.js +198 -0
- package/dist/agent-capacity/validation/agent-lab-forensics-schema.d.ts +234 -0
- package/dist/agent-capacity/validation/agent-lab-forensics-schema.js +104 -0
- package/dist/agent-capacity/validation/agent-signal.d.ts +30 -0
- package/dist/agent-capacity/validation/agent-signal.js +31 -0
- package/dist/agent-capacity/validation/allocation.d.ts +12 -0
- package/dist/agent-capacity/validation/allocation.js +115 -0
- package/dist/agent-capacity/validation/assignment-records.d.ts +16 -0
- package/dist/agent-capacity/validation/assignment-records.js +92 -0
- package/dist/agent-capacity/validation/configuration.d.ts +14 -0
- package/dist/agent-capacity/validation/configuration.js +23 -0
- package/dist/agent-capacity/validation/index.d.ts +11 -0
- package/dist/agent-capacity/validation/index.js +11 -0
- package/dist/agent-capacity/validation/proposal-type.d.ts +22 -0
- package/dist/agent-capacity/validation/proposal-type.js +21 -0
- package/dist/agent-capacity/validation/research/citation.d.ts +16 -0
- package/dist/agent-capacity/validation/research/citation.js +47 -0
- package/dist/agent-capacity/validation/research/source-policy.d.ts +11 -0
- package/dist/agent-capacity/validation/research/source-policy.js +26 -0
- package/dist/agent-capacity/validation/research/workflow.d.ts +10 -0
- package/dist/agent-capacity/validation/research/workflow.js +31 -0
- package/dist/agent-capacity/validation/workflow-promotion.d.ts +2 -0
- package/dist/agent-capacity/validation/workflow-promotion.js +34 -0
- package/dist/agent-capacity/workday.d.ts +52 -0
- package/dist/agent-capacity/workday.js +105 -0
- package/dist/ai-appliance/contracts.d.ts +90 -0
- package/dist/ai-appliance/contracts.js +16 -0
- package/dist/ai-appliance/validation.d.ts +4 -0
- package/dist/ai-appliance/validation.js +62 -0
- package/dist/capacity/agents/agent-capacity.d.ts +21 -0
- package/dist/capacity/agents/agent-capacity.js +20 -0
- package/dist/capacity/capacity-core/capacity-pagination.d.ts +18 -0
- package/dist/capacity/capacity-core/capacity-pagination.js +33 -0
- package/dist/capacity/providers/capacity-provider.d.ts +1 -0
- package/dist/capacity/providers/capacity-provider.js +1 -0
- package/dist/capacity-provider/client.d.ts +56 -0
- package/dist/capacity-provider/client.js +163 -0
- package/dist/capacity-provider/contracts/capacity-facts.d.ts +64 -0
- package/dist/capacity-provider/contracts/capacity-facts.js +0 -0
- package/dist/capacity-provider/contracts/governance.d.ts +399 -0
- package/dist/capacity-provider/contracts/governance.js +18 -0
- package/dist/capacity-provider/contracts/index.d.ts +2 -0
- package/dist/capacity-provider/contracts/index.js +2 -0
- package/dist/capacity-provider/index.d.ts +3 -0
- package/dist/capacity-provider/index.js +3 -0
- package/dist/capacity-provider/validation.d.ts +27 -0
- package/dist/capacity-provider/validation.js +184 -0
- package/dist/configuration/secrets-capability.d.ts +4 -0
- package/dist/configuration/secrets-capability.js +4 -0
- package/dist/content/contracts.d.ts +12 -0
- package/dist/content/contracts.js +0 -0
- package/dist/content/validation/agent-operational-content-schemas.d.ts +1351 -0
- package/dist/content/validation/agent-operational-content-schemas.js +146 -0
- package/dist/content/validation/auxiliary-content-schemas.d.ts +1715 -0
- package/dist/content/validation/auxiliary-content-schemas.js +173 -0
- package/dist/content/validation/content-model-schemas.d.ts +21728 -0
- package/dist/content/validation/content-model-schemas.js +271 -0
- package/dist/content/validation/index.d.ts +3 -0
- package/dist/content/validation/index.js +3 -0
- package/dist/content/validation/portable-content-data.d.ts +2101 -0
- package/dist/content/validation/portable-content-data.js +49 -0
- package/dist/entrypoints/clients/control-plane-client.d.ts +112 -0
- package/dist/entrypoints/clients/control-plane-client.js +180 -0
- package/dist/entrypoints/models/field-aliases.d.ts +11 -0
- package/dist/entrypoints/models/model-registry.d.ts +3 -0
- package/dist/entrypoints/models/model-registry.js +3 -0
- package/dist/entrypoints/models/sdk-fields.d.ts +11 -0
- package/dist/entrypoints/models/sdk-types.d.ts +10 -0
- package/dist/entrypoints/models/sdk-types.js +10 -0
- package/dist/feedback/contracts.d.ts +142 -0
- package/dist/feedback/contracts.js +18 -0
- package/dist/feedback/index.d.ts +1 -0
- package/dist/feedback/index.js +1 -0
- package/dist/governance/groups/contracts.d.ts +102 -0
- package/dist/governance/groups/contracts.js +6 -0
- package/dist/graph/context-query-contracts.d.ts +19 -1
- package/dist/graph/context-query-contracts.js +49 -8
- package/dist/knowledge/contracts.d.ts +173 -0
- package/dist/knowledge/contracts.js +16 -0
- package/dist/knowledge/index.d.ts +1 -0
- package/dist/knowledge/index.js +1 -0
- package/dist/model-registry/agent-operational-models.d.ts +4 -0
- package/dist/model-registry/agent-operational-models.js +38 -0
- package/dist/model-registry/build-builtin-model-registry.d.ts +2 -0
- package/dist/model-registry/build-builtin-model-registry.js +426 -0
- package/dist/model-registry/content-root.d.ts +9 -0
- package/dist/model-registry/content-root.js +33 -0
- package/dist/model-registry/discussion-models.d.ts +4 -0
- package/dist/model-registry/discussion-models.js +84 -0
- package/dist/model-registry/group-models.d.ts +2 -0
- package/dist/model-registry/group-models.js +52 -0
- package/dist/model-registry/knowledge-models.d.ts +2 -0
- package/dist/model-registry/knowledge-models.js +89 -0
- package/dist/model-registry/normalize-definition.d.ts +8 -0
- package/dist/model-registry/normalize-definition.js +69 -0
- package/dist/operator-contracts/canonical-command-tree.d.ts +3 -0
- package/dist/operator-contracts/canonical-command-tree.js +81 -0
- package/dist/operator-contracts/command-tree.d.ts +79 -0
- package/dist/operator-contracts/command-tree.js +53 -0
- package/dist/operator-contracts/control-plane-operation.d.ts +80 -0
- package/dist/operator-contracts/control-plane-operation.js +75 -0
- package/dist/operator-contracts/control-plane-operations.d.ts +546 -0
- package/dist/operator-contracts/control-plane-operations.js +352 -0
- package/dist/operator-contracts/index.d.ts +8 -0
- package/dist/operator-contracts/index.js +8 -0
- package/dist/operator-contracts/mcp.d.ts +78 -0
- package/dist/operator-contracts/mcp.js +23 -0
- package/dist/operator-contracts/oauth.d.ts +46 -0
- package/dist/operator-contracts/oauth.js +0 -0
- package/dist/operator-contracts/workday-lifecycle.d.ts +157 -0
- package/dist/operator-contracts/workday-lifecycle.js +65 -0
- package/dist/operator-contracts/workday-profile.d.ts +72 -0
- package/dist/operator-contracts/workday-profile.js +106 -0
- package/dist/projects/projects-core/project-workflow.d.ts +179 -0
- package/dist/projects/projects-core/project-workflow.js +66 -0
- package/dist/sdk-types/configuration/sdk-dispatch-credential-source.d.ts +201 -0
- package/dist/sdk-types/configuration/sdk-dispatch-credential-source.js +0 -0
- package/dist/sdk-types/control-plane/resource-requests.d.ts +124 -0
- package/dist/sdk-types/control-plane/resource-requests.js +0 -0
- package/dist/sdk-types/governance/commons/commons-question-input.d.ts +61 -0
- package/dist/sdk-types/governance/commons/commons-question-input.js +0 -0
- package/dist/sdk-types/support/planning-policy.d.ts +210 -0
- package/dist/sdk-types/support/planning-policy.js +0 -0
- package/dist/sdk-types/support/platform-contracts.d.ts +178 -0
- package/dist/sdk-types/support/platform-contracts.js +15 -0
- package/dist/sdk-types/support/sdk-model-names.d.ts +44 -0
- package/dist/sdk-types/support/sdk-model-names.js +80 -0
- package/dist/sdk-types/support/sdk-search-request.d.ts +182 -0
- package/dist/sdk-types/support/sdk-search-request.js +0 -0
- package/dist/sdk-types/support/task-planning.d.ts +189 -0
- package/dist/sdk-types/support/task-planning.js +0 -0
- package/dist/sdk-types/treedx/graph/sdk-graph-query-request.d.ts +157 -0
- package/dist/sdk-types/treedx/graph/sdk-graph-query-request.js +0 -0
- package/dist/secrets-capability/provider-operation-contracts.d.ts +90 -0
- package/dist/secrets-capability/provider-operation-contracts.js +10 -0
- package/dist/secrets-capability/service-provider-contracts.d.ts +92 -0
- package/dist/secrets-capability/service-provider-contracts.js +183 -0
- package/dist/secrets-capability/service-vault-contracts.d.ts +61 -0
- package/dist/secrets-capability/service-vault-contracts.js +39 -0
- package/dist/secrets-capability/workflow-operation-contracts.d.ts +95 -0
- package/dist/secrets-capability/workflow-operation-contracts.js +0 -0
- package/dist/standards/canonicalize.d.ts +10 -0
- package/dist/standards/canonicalize.js +43 -0
- package/dist/standards/compatibility-attestation.d.ts +23 -0
- package/dist/standards/compatibility-attestation.js +40 -0
- package/dist/standards/composition.d.ts +15 -0
- package/dist/standards/composition.js +14 -0
- package/dist/standards/contract-bundle.d.ts +8 -0
- package/dist/standards/contract-bundle.js +23 -0
- package/dist/standards/contracts.d.ts +39 -0
- package/dist/standards/contracts.js +0 -0
- package/dist/standards/errors.d.ts +6 -0
- package/dist/standards/errors.js +13 -0
- package/dist/standards/index.d.ts +8 -0
- package/dist/standards/index.js +8 -0
- package/dist/standards/mcp/compare.d.ts +2 -0
- package/dist/standards/mcp/compare.js +38 -0
- package/dist/standards/mcp/contracts.d.ts +34 -0
- package/dist/standards/mcp/contracts.js +0 -0
- package/dist/standards/mcp/index.d.ts +3 -0
- package/dist/standards/mcp/index.js +3 -0
- package/dist/standards/mcp/normalize.d.ts +4 -0
- package/dist/standards/mcp/normalize.js +30 -0
- package/dist/standards/openapi/compare.d.ts +2 -0
- package/dist/standards/openapi/compare.js +112 -0
- package/dist/standards/openapi/contracts.d.ts +24 -0
- package/dist/standards/openapi/contracts.js +0 -0
- package/dist/standards/openapi/index.d.ts +3 -0
- package/dist/standards/openapi/index.js +3 -0
- package/dist/standards/openapi/normalize.d.ts +2 -0
- package/dist/standards/openapi/normalize.js +57 -0
- package/dist/standards/package-manifest.d.ts +37 -0
- package/dist/standards/package-manifest.js +89 -0
- package/dist/standards/portfolio-entry.d.ts +12 -0
- package/dist/standards/portfolio-entry.js +11 -0
- package/dist/standards/typescript/compare.d.ts +2 -0
- package/dist/standards/typescript/compare.js +88 -0
- package/dist/standards/typescript/contracts.d.ts +44 -0
- package/dist/standards/typescript/contracts.js +0 -0
- package/dist/standards/typescript/extract.d.ts +10 -0
- package/dist/standards/typescript/extract.js +244 -0
- package/dist/standards/typescript/index.d.ts +3 -0
- package/dist/standards/typescript/index.js +3 -0
- package/dist/treedx/accounts/auth.d.ts +17 -0
- package/dist/treedx/accounts/auth.js +48 -0
- package/dist/treedx/client/accounts/contracts/auth-mode.d.ts +3 -0
- package/dist/treedx/client/accounts/contracts/auth-mode.js +6 -0
- package/dist/treedx/client/build/creation/build-context.d.ts +3 -0
- package/dist/treedx/client/build/creation/build-context.js +7 -0
- package/dist/treedx/client/build/creation/build-snapshot.d.ts +3 -0
- package/dist/treedx/client/build/creation/build-snapshot.js +8 -0
- package/dist/treedx/client/capacity/allocations/contracts/put-capability-grant.d.ts +3 -0
- package/dist/treedx/client/capacity/allocations/contracts/put-capability-grant.js +7 -0
- package/dist/treedx/client/capacity/allocations/queries/list-capability-grants.d.ts +6 -0
- package/dist/treedx/client/capacity/allocations/queries/list-capability-grants.js +10 -0
- package/dist/treedx/client/methods.d.ts +102 -0
- package/dist/treedx/client/methods.js +209 -0
- package/dist/treedx/client/persistence/contracts/restore-storage.d.ts +3 -0
- package/dist/treedx/client/persistence/contracts/restore-storage.js +6 -0
- package/dist/treedx/client/persistence/lifecycle/verify-storage-restore.d.ts +3 -0
- package/dist/treedx/client/persistence/lifecycle/verify-storage-restore.js +6 -0
- package/dist/treedx/client/repositories/creation/register-repository.d.ts +3 -0
- package/dist/treedx/client/repositories/creation/register-repository.js +7 -0
- package/dist/treedx/client/repositories/queries/get-repository.d.ts +3 -0
- package/dist/treedx/client/repositories/queries/get-repository.js +7 -0
- package/dist/treedx/client/repositories/queries/list-repository-paths.d.ts +3 -0
- package/dist/treedx/client/repositories/queries/list-repository-paths.js +7 -0
- package/dist/treedx/client/repositories/queries/list-repository-refs.d.ts +3 -0
- package/dist/treedx/client/repositories/queries/list-repository-refs.js +12 -0
- package/dist/treedx/client/repositories/queries/query-repository.d.ts +3 -0
- package/dist/treedx/client/repositories/queries/query-repository.js +7 -0
- package/dist/treedx/client/repositories/queries/read-repository-file.d.ts +3 -0
- package/dist/treedx/client/repositories/queries/read-repository-file.js +6 -0
- package/dist/treedx/client/repositories/queries/read-repository-files.d.ts +3 -0
- package/dist/treedx/client/repositories/queries/read-repository-files.js +7 -0
- package/dist/treedx/client/repositories/queries/search-repository-files.d.ts +3 -0
- package/dist/treedx/client/repositories/queries/search-repository-files.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/artifacts/contracts/download-artifact.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/artifacts/contracts/download-artifact.js +9 -0
- package/dist/treedx/client/support/contracts/treedx/authorization/contracts/whoami.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/authorization/contracts/whoami.js +6 -0
- package/dist/treedx/client/support/contracts/treedx/graph/contracts/parse-context-dsl.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/graph/contracts/parse-context-dsl.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/deep-health.d.ts +5 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/deep-health.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/health.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/health.js +6 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/metrics.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/metrics.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/prometheus-metrics.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/prometheus-metrics.js +19 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/ready.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/ready.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/status.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/status.js +6 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/commit.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/commit.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/diff.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/diff.js +6 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/fetch-remote.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/fetch-remote.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/push.d.ts +6 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/push.js +23 -0
- package/dist/treedx/client/support/contracts/treedx/search/contracts/compact-search-index.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/search/contracts/compact-search-index.js +8 -0
- package/dist/treedx/client/support/contracts/treedx/search/contracts/search.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/search/contracts/search.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/storage/contracts/backup-storage.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/storage/contracts/backup-storage.js +8 -0
- package/dist/treedx/client/support/contracts/treedx/storage/contracts/compact-storage.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/storage/contracts/compact-storage.js +8 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/assert-binary-ok.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/assert-binary-ok.js +15 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/fetch-with-timeout.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/fetch-with-timeout.js +38 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/headers.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/headers.js +15 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/parse-json-response.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/parse-json-response.js +15 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/query.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/query.js +14 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/repo-id.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/repo-id.js +14 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/request.d.ts +6 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/request.js +41 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/throw-api-error.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/throw-api-error.js +14 -0
- package/dist/treedx/client/support/contracts/treedx/workspaces/contracts/exec.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/workspaces/contracts/exec.js +7 -0
- package/dist/treedx/client/support/creation/apply-changeset.d.ts +3 -0
- package/dist/treedx/client/support/creation/apply-changeset.js +10 -0
- package/dist/treedx/client/support/creation/apply-storage-migration.d.ts +3 -0
- package/dist/treedx/client/support/creation/apply-storage-migration.js +7 -0
- package/dist/treedx/client/support/creation/create-migration.d.ts +6 -0
- package/dist/treedx/client/support/creation/create-migration.js +7 -0
- package/dist/treedx/client/support/creation/export-artifact.d.ts +3 -0
- package/dist/treedx/client/support/creation/export-artifact.js +8 -0
- package/dist/treedx/client/support/creation/plan-storage-migration.d.ts +3 -0
- package/dist/treedx/client/support/creation/plan-storage-migration.js +7 -0
- package/dist/treedx/client/support/creation/request-binary.d.ts +6 -0
- package/dist/treedx/client/support/creation/request-binary.js +42 -0
- package/dist/treedx/client/support/creation/write-file.d.ts +3 -0
- package/dist/treedx/client/support/creation/write-file.js +10 -0
- package/dist/treedx/client/support/creation/write-files.d.ts +3 -0
- package/dist/treedx/client/support/creation/write-files.js +12 -0
- package/dist/treedx/client/support/queries/treedx/artifacts/queries/get-artifact.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/artifacts/queries/get-artifact.js +7 -0
- package/dist/treedx/client/support/queries/treedx/artifacts/queries/list-artifacts.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/artifacts/queries/list-artifacts.js +7 -0
- package/dist/treedx/client/support/queries/treedx/authorization/queries/effective-scope.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/authorization/queries/effective-scope.js +9 -0
- package/dist/treedx/client/support/queries/treedx/authorization/queries/list-capabilities.d.ts +4 -0
- package/dist/treedx/client/support/queries/treedx/authorization/queries/list-capabilities.js +6 -0
- package/dist/treedx/client/support/queries/treedx/federation/queries/get-placement.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/federation/queries/get-placement.js +7 -0
- package/dist/treedx/client/support/queries/treedx/graph/queries/get-node.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/graph/queries/get-node.js +7 -0
- package/dist/treedx/client/support/queries/treedx/graph/queries/get-related.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/graph/queries/get-related.js +7 -0
- package/dist/treedx/client/support/queries/treedx/graph/queries/list-nodes.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/graph/queries/list-nodes.js +7 -0
- package/dist/treedx/client/support/queries/treedx/repositories/queries/list-tree.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/repositories/queries/list-tree.js +10 -0
- package/dist/treedx/client/support/queries/treedx/repositories/queries/read-file.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/repositories/queries/read-file.js +9 -0
- package/dist/treedx/client/support/queries/treedx/search/queries/get-search-index-status.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/search/queries/get-search-index-status.js +7 -0
- package/dist/treedx/client/support/queries/treedx/storage/queries/get-migration.d.ts +6 -0
- package/dist/treedx/client/support/queries/treedx/storage/queries/get-migration.js +7 -0
- package/dist/treedx/client/support/queries/treedx/storage/queries/get-snapshot.d.ts +6 -0
- package/dist/treedx/client/support/queries/treedx/storage/queries/get-snapshot.js +7 -0
- package/dist/treedx/client/support/queries/treedx/storage/queries/list-storage-migrations.d.ts +6 -0
- package/dist/treedx/client/support/queries/treedx/storage/queries/list-storage-migrations.js +6 -0
- package/dist/treedx/client/support/queries/treedx/transport/queries/list-audit-events.d.ts +15 -0
- package/dist/treedx/client/support/queries/treedx/transport/queries/list-audit-events.js +6 -0
- package/dist/treedx/client/support/queries/treedx/transport/queries/list-repositories.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/transport/queries/list-repositories.js +7 -0
- package/dist/treedx/client/support/retirement/cleanup-artifacts.d.ts +3 -0
- package/dist/treedx/client/support/retirement/cleanup-artifacts.js +7 -0
- package/dist/treedx/client/support/retirement/delete-artifact.d.ts +3 -0
- package/dist/treedx/client/support/retirement/delete-artifact.js +7 -0
- package/dist/treedx/client/support/retirement/delete-file.d.ts +3 -0
- package/dist/treedx/client/support/retirement/delete-file.js +10 -0
- package/dist/treedx/client/support/retirement/rollback-storage-migration.d.ts +3 -0
- package/dist/treedx/client/support/retirement/rollback-storage-migration.js +7 -0
- package/dist/treedx/client/support/updates/patch-file.d.ts +3 -0
- package/dist/treedx/client/support/updates/patch-file.js +10 -0
- package/dist/treedx/client/support/updates/refresh-search-index.d.ts +3 -0
- package/dist/treedx/client/support/updates/refresh-search-index.js +8 -0
- package/dist/treedx/client/treedx/blobs/contracts/download-blob.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/contracts/download-blob.js +6 -0
- package/dist/treedx/client/treedx/blobs/creation/create-blob-upload.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/creation/create-blob-upload.js +8 -0
- package/dist/treedx/client/treedx/blobs/creation/request-blob-download.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/creation/request-blob-download.js +27 -0
- package/dist/treedx/client/treedx/blobs/creation/request-blob-upload.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/creation/request-blob-upload.js +41 -0
- package/dist/treedx/client/treedx/blobs/creation/upload-blob-part.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/creation/upload-blob-part.js +27 -0
- package/dist/treedx/client/treedx/blobs/creation/upload-blob.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/creation/upload-blob.js +6 -0
- package/dist/treedx/client/treedx/blobs/creation/write-blob.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/creation/write-blob.js +8 -0
- package/dist/treedx/client/treedx/blobs/lifecycle/complete-blob-upload.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/lifecycle/complete-blob-upload.js +8 -0
- package/dist/treedx/client/treedx/blobs/queries/read-blob.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/queries/read-blob.js +10 -0
- package/dist/treedx/client/treedx/blobs/retirement/abort-blob-upload.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/retirement/abort-blob-upload.js +7 -0
- package/dist/treedx/client/treedx/blobs/retirement/delete-blob.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/retirement/delete-blob.js +8 -0
- package/dist/treedx/client/treedx/federation/contracts/federated-query.d.ts +3 -0
- package/dist/treedx/client/treedx/federation/contracts/federated-query.js +7 -0
- package/dist/treedx/client/treedx/federation/creation/plan-federated-query.d.ts +3 -0
- package/dist/treedx/client/treedx/federation/creation/plan-federated-query.js +6 -0
- package/dist/treedx/client/treedx/federation/queries/check-mirror-health.d.ts +3 -0
- package/dist/treedx/client/treedx/federation/queries/check-mirror-health.js +7 -0
- package/dist/treedx/client/treedx/federation/updates/promote-mirror.d.ts +3 -0
- package/dist/treedx/client/treedx/federation/updates/promote-mirror.js +7 -0
- package/dist/treedx/client/treedx/federation/updates/sync-mirror.d.ts +3 -0
- package/dist/treedx/client/treedx/federation/updates/sync-mirror.js +7 -0
- package/dist/treedx/client/treedx/graph/contracts/federated-context.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/contracts/federated-context.js +7 -0
- package/dist/treedx/client/treedx/graph/contracts/federated-graph.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/contracts/federated-graph.js +7 -0
- package/dist/treedx/client/treedx/graph/contracts/graph-search.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/contracts/graph-search.js +8 -0
- package/dist/treedx/client/treedx/graph/queries/get-graph-node.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/queries/get-graph-node.js +10 -0
- package/dist/treedx/client/treedx/graph/queries/get-graph-refresh-job.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/queries/get-graph-refresh-job.js +7 -0
- package/dist/treedx/client/treedx/graph/queries/get-subgraph.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/queries/get-subgraph.js +7 -0
- package/dist/treedx/client/treedx/graph/queries/query-graph.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/queries/query-graph.js +7 -0
- package/dist/treedx/client/treedx/graph/queries/search-graph-entities.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/queries/search-graph-entities.js +6 -0
- package/dist/treedx/client/treedx/graph/queries/search-graph-files.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/queries/search-graph-files.js +6 -0
- package/dist/treedx/client/treedx/graph/queries/search-graph-sections.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/queries/search-graph-sections.js +6 -0
- package/dist/treedx/client/treedx/graph/updates/refresh-graph.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/updates/refresh-graph.js +7 -0
- package/dist/treedx/client/treedx/search/contracts/federated-search.d.ts +3 -0
- package/dist/treedx/client/treedx/search/contracts/federated-search.js +7 -0
- package/dist/treedx/client/treedx/workspaces/creation/create-workspace.d.ts +3 -0
- package/dist/treedx/client/treedx/workspaces/creation/create-workspace.js +7 -0
- package/dist/treedx/client/treedx/workspaces/retirement/abandon-workspace.d.ts +3 -0
- package/dist/treedx/client/treedx/workspaces/retirement/abandon-workspace.js +12 -0
- package/dist/treedx/client/treedx/workspaces/retirement/close-workspace.d.ts +2 -0
- package/dist/treedx/client/treedx/workspaces/retirement/close-workspace.js +6 -0
- package/dist/treedx/generated/openapi-types.d.ts +104 -0
- package/dist/treedx/support/client.d.ts +251 -0
- package/dist/treedx/support/client.js +252 -0
- package/dist/treedx/support/errors.d.ts +13 -0
- package/dist/treedx/types/tree-dx-actor.d.ts +209 -0
- package/dist/treedx/types/tree-dx-actor.js +8 -0
- package/dist/treedx/types/tree-dx-artifact-list-response.d.ts +13 -0
- package/dist/treedx/types/tree-dx-artifact-list-response.js +0 -0
- package/dist/treedx/types/tree-dx-changeset.d.ts +51 -0
- package/dist/treedx/types/tree-dx-changeset.js +6 -0
- package/dist/treedx/types/tree-dx-context-request.d.ts +245 -0
- package/dist/treedx/types/tree-dx-context-request.js +0 -0
- package/dist/treedx/types/tree-dx-exec-result.d.ts +249 -0
- package/dist/treedx/types/tree-dx-exec-result.js +0 -0
- package/dist/treedx/types/tree-dx-list-tree-request.d.ts +222 -0
- package/dist/treedx/types/tree-dx-list-tree-request.js +0 -0
- package/dist/treedx/types/tree-dx-push-request.d.ts +229 -0
- package/dist/treedx/types/tree-dx-push-request.js +0 -0
- package/dist/treedx/types.d.ts +7 -1084
- package/dist/treedx/types.js +7 -8
- package/dist/types/agents/agent-activity-profile.d.ts +267 -0
- package/dist/types/agents/agent-activity-profile.js +0 -0
- package/dist/types/agents/agent-trigger-kinds.d.ts +189 -0
- package/dist/types/agents/agent-trigger-kinds.js +107 -0
- package/dist/types/agents/execution-run-ref.d.ts +136 -0
- package/dist/types/agents/execution-run-ref.js +0 -0
- package/dist/types/agents.d.ts +3 -493
- package/dist/types/agents.js +3 -95
- package/package.json +77 -362
- package/dist/agent-capacity.d.ts +0 -1100
- package/dist/agent-capacity.js +0 -1822
- package/dist/agent-tools.d.ts +0 -37
- package/dist/agent-tools.js +0 -301
- package/dist/api/app.d.ts +0 -8
- package/dist/api/app.js +0 -404
- package/dist/api/auth/d1-database.d.ts +0 -3
- package/dist/api/auth/d1-database.js +0 -20
- package/dist/api/auth/d1-provider.d.ts +0 -85
- package/dist/api/auth/d1-provider.js +0 -95
- package/dist/api/auth/d1-store.d.ts +0 -121
- package/dist/api/auth/d1-store.js +0 -990
- package/dist/api/auth/memory-provider.d.ts +0 -82
- package/dist/api/auth/memory-provider.js +0 -297
- package/dist/api/auth/rbac.d.ts +0 -22
- package/dist/api/auth/rbac.js +0 -162
- package/dist/api/auth/tokens.d.ts +0 -18
- package/dist/api/auth/tokens.js +0 -56
- package/dist/api/config.d.ts +0 -4
- package/dist/api/config.js +0 -149
- package/dist/api/http.d.ts +0 -28
- package/dist/api/http.js +0 -51
- package/dist/api/index.d.ts +0 -10
- package/dist/api/index.js +0 -29
- package/dist/api/operations-routes.d.ts +0 -11
- package/dist/api/operations-routes.js +0 -39
- package/dist/api/operations.d.ts +0 -3
- package/dist/api/operations.js +0 -26
- package/dist/api/providers.d.ts +0 -2
- package/dist/api/providers.js +0 -85
- package/dist/api/railway.d.ts +0 -53
- package/dist/api/railway.js +0 -71
- package/dist/api/sdk-dispatch.d.ts +0 -6
- package/dist/api/sdk-dispatch.js +0 -14
- package/dist/api/sdk-routes.d.ts +0 -11
- package/dist/api/sdk-routes.js +0 -29
- package/dist/api/templates.d.ts +0 -3
- package/dist/api/templates.js +0 -31
- package/dist/api/types.d.ts +0 -238
- package/dist/capacity-provider.d.ts +0 -494
- package/dist/capacity-provider.js +0 -727
- package/dist/capacity.d.ts +0 -483
- package/dist/capacity.js +0 -2331
- package/dist/cli-tools.d.ts +0 -3
- package/dist/cli-tools.js +0 -46
- package/dist/content-operations.d.ts +0 -97
- package/dist/content-operations.js +0 -250
- package/dist/content-store.d.ts +0 -25
- package/dist/content-store.js +0 -269
- package/dist/control-plane-client.d.ts +0 -203
- package/dist/control-plane-client.js +0 -540
- package/dist/control-plane.d.ts +0 -101
- package/dist/control-plane.js +0 -201
- package/dist/copilot.d.ts +0 -18
- package/dist/copilot.js +0 -90
- package/dist/d1-http.d.ts +0 -38
- package/dist/d1-http.js +0 -95
- package/dist/d1-store.d.ts +0 -336
- package/dist/d1-store.js +0 -1431
- package/dist/db/d1.d.ts +0 -246
- package/dist/db/d1.js +0 -8
- package/dist/db/index.d.ts +0 -3
- package/dist/db/index.js +0 -3
- package/dist/db/market-schema.d.ts +0 -83651
- package/dist/db/market-schema.js +0 -3725
- package/dist/db/node-sqlite.d.ts +0 -297
- package/dist/db/node-sqlite.js +0 -133
- package/dist/db/schema.d.ts +0 -483
- package/dist/db/schema.js +0 -29
- package/dist/dispatch.d.ts +0 -4
- package/dist/dispatch.js +0 -174
- package/dist/field-aliases.d.ts +0 -11
- package/dist/fixture-support.d.ts +0 -24
- package/dist/fixture-support.js +0 -385
- package/dist/frontmatter.d.ts +0 -6
- package/dist/frontmatter.js +0 -33
- package/dist/git-runtime.d.ts +0 -16
- package/dist/git-runtime.js +0 -69
- package/dist/governance.d.ts +0 -133
- package/dist/governance.js +0 -285
- package/dist/graph/build.d.ts +0 -19
- package/dist/graph/build.js +0 -949
- package/dist/graph/dsl.d.ts +0 -2
- package/dist/graph/dsl.js +0 -243
- package/dist/graph/query.d.ts +0 -47
- package/dist/graph/query.js +0 -447
- package/dist/graph/ranking.d.ts +0 -3
- package/dist/graph/ranking.js +0 -483
- package/dist/graph/schema.d.ts +0 -142
- package/dist/graph/schema.js +0 -137
- package/dist/graph.d.ts +0 -52
- package/dist/graph.js +0 -133
- package/dist/guarantees/index.d.ts +0 -512
- package/dist/guarantees/index.js +0 -1782
- package/dist/hosting/apps.d.ts +0 -12
- package/dist/hosting/apps.js +0 -107
- package/dist/hosting/builtins.d.ts +0 -25
- package/dist/hosting/builtins.js +0 -685
- package/dist/hosting/contracts.d.ts +0 -190
- package/dist/hosting/graph.d.ts +0 -110
- package/dist/hosting/graph.js +0 -993
- package/dist/hosting/index.d.ts +0 -4
- package/dist/hosting/index.js +0 -4
- package/dist/index.d.ts +0 -88
- package/dist/index.js +0 -757
- package/dist/local-dev/managed-dev.d.ts +0 -85
- package/dist/local-dev/managed-dev.js +0 -431
- package/dist/managed-dependencies.d.ts +0 -91
- package/dist/managed-dependencies.js +0 -1186
- package/dist/market-client.d.ts +0 -831
- package/dist/market-client.js +0 -1219
- package/dist/model-registry.d.ts +0 -8
- package/dist/model-registry.js +0 -674
- package/dist/operations/agent-tools.d.ts +0 -105
- package/dist/operations/agent-tools.js +0 -171
- package/dist/operations/providers/default.d.ts +0 -10
- package/dist/operations/providers/default.js +0 -1245
- package/dist/operations/repository-operations.d.ts +0 -158
- package/dist/operations/repository-operations.js +0 -851
- package/dist/operations/runtime.d.ts +0 -7
- package/dist/operations/runtime.js +0 -61
- package/dist/operations/services/bootstrap-runner.d.ts +0 -39
- package/dist/operations/services/bootstrap-runner.js +0 -191
- package/dist/operations/services/build-warning-policy.d.ts +0 -34
- package/dist/operations/services/build-warning-policy.js +0 -91
- package/dist/operations/services/commit-message-provider.d.ts +0 -65
- package/dist/operations/services/commit-message-provider.js +0 -496
- package/dist/operations/services/config-runtime.d.ts +0 -558
- package/dist/operations/services/config-runtime.js +0 -3494
- package/dist/operations/services/d1-migration.d.ts +0 -6
- package/dist/operations/services/d1-migration.js +0 -120
- package/dist/operations/services/deploy.d.ts +0 -645
- package/dist/operations/services/deploy.js +0 -3826
- package/dist/operations/services/deployment-readiness.d.ts +0 -30
- package/dist/operations/services/deployment-readiness.js +0 -217
- package/dist/operations/services/export-runtime.d.ts +0 -18
- package/dist/operations/services/export-runtime.js +0 -140
- package/dist/operations/services/git-remote-policy.d.ts +0 -9
- package/dist/operations/services/git-remote-policy.js +0 -63
- package/dist/operations/services/git-runner.d.ts +0 -68
- package/dist/operations/services/git-runner.js +0 -500
- package/dist/operations/services/git-workflow.d.ts +0 -255
- package/dist/operations/services/git-workflow.js +0 -845
- package/dist/operations/services/github-actions-verification.d.ts +0 -148
- package/dist/operations/services/github-actions-verification.js +0 -589
- package/dist/operations/services/github-api.d.ts +0 -279
- package/dist/operations/services/github-api.js +0 -936
- package/dist/operations/services/github-automation.d.ts +0 -230
- package/dist/operations/services/github-automation.js +0 -520
- package/dist/operations/services/github-credentials.d.ts +0 -13
- package/dist/operations/services/github-credentials.js +0 -59
- package/dist/operations/services/hosted-service-checks.d.ts +0 -82
- package/dist/operations/services/hosted-service-checks.js +0 -527
- package/dist/operations/services/hosting-audit.d.ts +0 -69
- package/dist/operations/services/hosting-audit.js +0 -638
- package/dist/operations/services/hub-launch.d.ts +0 -182
- package/dist/operations/services/hub-launch.js +0 -309
- package/dist/operations/services/hub-provider-launch.d.ts +0 -160
- package/dist/operations/services/hub-provider-launch.js +0 -1056
- package/dist/operations/services/key-agent.d.ts +0 -137
- package/dist/operations/services/key-agent.js +0 -641
- package/dist/operations/services/live-hosted-service-checks.d.ts +0 -30
- package/dist/operations/services/live-hosted-service-checks.js +0 -611
- package/dist/operations/services/local-cleanup.d.ts +0 -30
- package/dist/operations/services/local-cleanup.js +0 -77
- package/dist/operations/services/local-dev.d.ts +0 -8
- package/dist/operations/services/local-dev.js +0 -97
- package/dist/operations/services/managed-host-security.d.ts +0 -13
- package/dist/operations/services/managed-host-security.js +0 -56
- package/dist/operations/services/managed-repositories.d.ts +0 -37
- package/dist/operations/services/managed-repositories.js +0 -177
- package/dist/operations/services/market-packaging.d.ts +0 -59
- package/dist/operations/services/market-packaging.js +0 -234
- package/dist/operations/services/operations-runner-smoke.d.ts +0 -31
- package/dist/operations/services/operations-runner-smoke.js +0 -212
- package/dist/operations/services/package-adapters.d.ts +0 -184
- package/dist/operations/services/package-adapters.js +0 -912
- package/dist/operations/services/package-artifacts.d.ts +0 -37
- package/dist/operations/services/package-artifacts.js +0 -99
- package/dist/operations/services/package-reference-policy.d.ts +0 -64
- package/dist/operations/services/package-reference-policy.js +0 -364
- package/dist/operations/services/project-host-operations.d.ts +0 -153
- package/dist/operations/services/project-host-operations.js +0 -365
- package/dist/operations/services/project-platform.d.ts +0 -727
- package/dist/operations/services/project-platform.js +0 -1603
- package/dist/operations/services/project-web-monitor.d.ts +0 -15
- package/dist/operations/services/project-web-monitor.js +0 -377
- package/dist/operations/services/railway-api.d.ts +0 -494
- package/dist/operations/services/railway-api.js +0 -1874
- package/dist/operations/services/railway-cli.d.ts +0 -47
- package/dist/operations/services/railway-cli.js +0 -142
- package/dist/operations/services/railway-deploy.d.ts +0 -161
- package/dist/operations/services/railway-deploy.js +0 -1808
- package/dist/operations/services/railway-source-policy.d.ts +0 -25
- package/dist/operations/services/railway-source-policy.js +0 -111
- package/dist/operations/services/release-history.d.ts +0 -59
- package/dist/operations/services/release-history.js +0 -158
- package/dist/operations/services/release-proof-ledger.d.ts +0 -13
- package/dist/operations/services/release-proof-ledger.js +0 -81
- package/dist/operations/services/release-proof-planner.d.ts +0 -48
- package/dist/operations/services/release-proof-planner.js +0 -195
- package/dist/operations/services/release-proof-runner.d.ts +0 -16
- package/dist/operations/services/release-proof-runner.js +0 -169
- package/dist/operations/services/release-proof.d.ts +0 -81
- package/dist/operations/services/release-proof.js +0 -79
- package/dist/operations/services/repository-save-orchestrator.d.ts +0 -193
- package/dist/operations/services/repository-save-orchestrator.js +0 -2110
- package/dist/operations/services/runtime-paths.d.ts +0 -19
- package/dist/operations/services/runtime-paths.js +0 -54
- package/dist/operations/services/runtime-tools.d.ts +0 -35
- package/dist/operations/services/runtime-tools.js +0 -212
- package/dist/operations/services/save-deploy-preflight.d.ts +0 -32
- package/dist/operations/services/save-deploy-preflight.js +0 -82
- package/dist/operations/services/template-host-bindings.d.ts +0 -68
- package/dist/operations/services/template-host-bindings.js +0 -401
- package/dist/operations/services/template-registry.d.ts +0 -110
- package/dist/operations/services/template-registry.js +0 -595
- package/dist/operations/services/template-secret-sync.d.ts +0 -97
- package/dist/operations/services/template-secret-sync.js +0 -292
- package/dist/operations/services/verification-cache.d.ts +0 -25
- package/dist/operations/services/verification-cache.js +0 -71
- package/dist/operations/services/watch-dev.d.ts +0 -21
- package/dist/operations/services/watch-dev.js +0 -282
- package/dist/operations/services/workflow-timing.d.ts +0 -25
- package/dist/operations/services/workflow-timing.js +0 -79
- package/dist/operations/services/workspace-dependency-mode.d.ts +0 -74
- package/dist/operations/services/workspace-dependency-mode.js +0 -478
- package/dist/operations/services/workspace-preflight.d.ts +0 -40
- package/dist/operations/services/workspace-preflight.js +0 -172
- package/dist/operations/services/workspace-save-script.d.ts +0 -26
- package/dist/operations/services/workspace-save-script.js +0 -50
- package/dist/operations/services/workspace-save.d.ts +0 -76
- package/dist/operations/services/workspace-save.js +0 -453
- package/dist/operations/services/workspace-tools.d.ts +0 -17
- package/dist/operations/services/workspace-tools.js +0 -320
- package/dist/operations-registry.d.ts +0 -5
- package/dist/operations-registry.js +0 -98
- package/dist/operations-types.d.ts +0 -75
- package/dist/operations-types.js +0 -17
- package/dist/operations.d.ts +0 -17
- package/dist/operations.js +0 -114
- package/dist/platform/book-export.d.ts +0 -78
- package/dist/platform/book-export.js +0 -451
- package/dist/platform/books-data.d.ts +0 -28
- package/dist/platform/books-data.js +0 -111
- package/dist/platform/config.d.ts +0 -27
- package/dist/platform/config.js +0 -100
- package/dist/platform/content-runtime-source.d.ts +0 -54
- package/dist/platform/content-runtime-source.js +0 -169
- package/dist/platform/contracts.d.ts +0 -361
- package/dist/platform/deploy-config.d.ts +0 -17
- package/dist/platform/deploy-config.js +0 -731
- package/dist/platform/deploy-runtime.d.ts +0 -22
- package/dist/platform/deploy-runtime.js +0 -93
- package/dist/platform/desired-state.d.ts +0 -66
- package/dist/platform/desired-state.js +0 -1071
- package/dist/platform/env.yaml +0 -1126
- package/dist/platform/environment.d.ts +0 -159
- package/dist/platform/environment.js +0 -778
- package/dist/platform/local-content-materialization.d.ts +0 -9
- package/dist/platform/local-content-materialization.js +0 -212
- package/dist/platform/plugin.d.ts +0 -103
- package/dist/platform/plugin.js +0 -6
- package/dist/platform/plugins/constants.d.ts +0 -26
- package/dist/platform/plugins/constants.js +0 -33
- package/dist/platform/plugins/runtime.d.ts +0 -41
- package/dist/platform/plugins/runtime.js +0 -257
- package/dist/platform/plugins.d.ts +0 -6
- package/dist/platform/plugins.js +0 -42
- package/dist/platform/published-content-pipeline.d.ts +0 -84
- package/dist/platform/published-content-pipeline.js +0 -547
- package/dist/platform/published-content.d.ts +0 -224
- package/dist/platform/published-content.js +0 -611
- package/dist/platform/site-config-schema.d.ts +0 -1
- package/dist/platform/site-config-schema.js +0 -4
- package/dist/platform/tenant/runtime-config.d.ts +0 -154
- package/dist/platform/tenant/runtime-config.js +0 -69
- package/dist/platform/tenant-config.d.ts +0 -8
- package/dist/platform/tenant-config.js +0 -207
- package/dist/platform/utils/site-config-schema.d.ts +0 -154
- package/dist/platform/utils/site-config-schema.js +0 -450
- package/dist/platform-operation-store.d.ts +0 -94
- package/dist/platform-operation-store.js +0 -531
- package/dist/platform-operations.d.ts +0 -273
- package/dist/platform-operations.js +0 -440
- package/dist/plugin-default.d.ts +0 -28
- package/dist/plugin-default.js +0 -55
- package/dist/project-import.d.ts +0 -91
- package/dist/project-import.js +0 -257
- package/dist/project-workflow.d.ts +0 -233
- package/dist/project-workflow.js +0 -82
- package/dist/reconcile/bootstrap-systems.d.ts +0 -32
- package/dist/reconcile/bootstrap-systems.js +0 -175
- package/dist/reconcile/builtin-adapters.d.ts +0 -9
- package/dist/reconcile/builtin-adapters.js +0 -5545
- package/dist/reconcile/contracts.d.ts +0 -172
- package/dist/reconcile/desired-state.d.ts +0 -10
- package/dist/reconcile/desired-state.js +0 -322
- package/dist/reconcile/engine.d.ts +0 -118
- package/dist/reconcile/engine.js +0 -645
- package/dist/reconcile/errors.d.ts +0 -5
- package/dist/reconcile/errors.js +0 -13
- package/dist/reconcile/index.d.ts +0 -10
- package/dist/reconcile/index.js +0 -10
- package/dist/reconcile/live-acceptance.d.ts +0 -100
- package/dist/reconcile/live-acceptance.js +0 -2018
- package/dist/reconcile/platform.d.ts +0 -104
- package/dist/reconcile/platform.js +0 -100
- package/dist/reconcile/providers/docker-private.d.ts +0 -54
- package/dist/reconcile/providers/docker-private.js +0 -101
- package/dist/reconcile/providers/github-private.d.ts +0 -69
- package/dist/reconcile/providers/github-private.js +0 -145
- package/dist/reconcile/providers/local-private.d.ts +0 -28
- package/dist/reconcile/providers/local-private.js +0 -75
- package/dist/reconcile/providers/railway-iac.d.ts +0 -220
- package/dist/reconcile/providers/railway-iac.js +0 -621
- package/dist/reconcile/providers/release-private.d.ts +0 -89
- package/dist/reconcile/providers/release-private.js +0 -140
- package/dist/reconcile/registry.d.ts +0 -7
- package/dist/reconcile/registry.js +0 -79
- package/dist/reconcile/state.d.ts +0 -7
- package/dist/reconcile/state.js +0 -270
- package/dist/reconcile/units.d.ts +0 -6
- package/dist/reconcile/units.js +0 -72
- package/dist/remote.d.ts +0 -244
- package/dist/remote.js +0 -284
- package/dist/scenes/artifacts.d.ts +0 -16
- package/dist/scenes/artifacts.js +0 -61
- package/dist/scenes/auth.d.ts +0 -2
- package/dist/scenes/auth.js +0 -28
- package/dist/scenes/base-url.d.ts +0 -12
- package/dist/scenes/base-url.js +0 -123
- package/dist/scenes/builtin-plugins.d.ts +0 -2
- package/dist/scenes/builtin-plugins.js +0 -499
- package/dist/scenes/checkpoints.d.ts +0 -14
- package/dist/scenes/checkpoints.js +0 -33
- package/dist/scenes/device-matrix.d.ts +0 -2
- package/dist/scenes/device-matrix.js +0 -100
- package/dist/scenes/devices.d.ts +0 -11
- package/dist/scenes/devices.js +0 -40
- package/dist/scenes/diagnostics.d.ts +0 -5
- package/dist/scenes/diagnostics.js +0 -22
- package/dist/scenes/diagram-providers.d.ts +0 -2
- package/dist/scenes/diagram-providers.js +0 -181
- package/dist/scenes/diagram-validation.d.ts +0 -22
- package/dist/scenes/diagram-validation.js +0 -74
- package/dist/scenes/environment.d.ts +0 -2
- package/dist/scenes/environment.js +0 -65
- package/dist/scenes/evidence-bundle.d.ts +0 -5
- package/dist/scenes/evidence-bundle.js +0 -42
- package/dist/scenes/evidence-report.d.ts +0 -10
- package/dist/scenes/evidence-report.js +0 -73
- package/dist/scenes/evidence.d.ts +0 -16
- package/dist/scenes/evidence.js +0 -277
- package/dist/scenes/index.d.ts +0 -48
- package/dist/scenes/index.js +0 -312
- package/dist/scenes/inspect.d.ts +0 -9
- package/dist/scenes/inspect.js +0 -64
- package/dist/scenes/loader.d.ts +0 -3
- package/dist/scenes/loader.js +0 -38
- package/dist/scenes/logs.d.ts +0 -2
- package/dist/scenes/logs.js +0 -47
- package/dist/scenes/operations.d.ts +0 -3
- package/dist/scenes/operations.js +0 -128
- package/dist/scenes/phase0.d.ts +0 -9
- package/dist/scenes/phase0.js +0 -637
- package/dist/scenes/planner.d.ts +0 -12
- package/dist/scenes/planner.js +0 -166
- package/dist/scenes/playwright-adapter.d.ts +0 -2
- package/dist/scenes/playwright-adapter.js +0 -63
- package/dist/scenes/plugins.d.ts +0 -5
- package/dist/scenes/plugins.js +0 -115
- package/dist/scenes/progress.d.ts +0 -17
- package/dist/scenes/progress.js +0 -24
- package/dist/scenes/publish-destinations.d.ts +0 -12
- package/dist/scenes/publish-destinations.js +0 -99
- package/dist/scenes/publish-plan-report.d.ts +0 -10
- package/dist/scenes/publish-plan-report.js +0 -70
- package/dist/scenes/publish-plan.d.ts +0 -15
- package/dist/scenes/publish-plan.js +0 -248
- package/dist/scenes/publish-redaction.d.ts +0 -16
- package/dist/scenes/publish-redaction.js +0 -148
- package/dist/scenes/publish-release-record.d.ts +0 -13
- package/dist/scenes/publish-release-record.js +0 -34
- package/dist/scenes/publish-report.d.ts +0 -10
- package/dist/scenes/publish-report.js +0 -67
- package/dist/scenes/publish.d.ts +0 -16
- package/dist/scenes/publish.js +0 -298
- package/dist/scenes/registry.d.ts +0 -12
- package/dist/scenes/registry.js +0 -65
- package/dist/scenes/remotion-adapter.d.ts +0 -3
- package/dist/scenes/remotion-adapter.js +0 -94
- package/dist/scenes/remotion-composition-registry.d.ts +0 -2
- package/dist/scenes/remotion-composition-registry.js +0 -12
- package/dist/scenes/remotion-compositions.d.ts +0 -15
- package/dist/scenes/remotion-compositions.js +0 -464
- package/dist/scenes/remotion-diagrams.d.ts +0 -9
- package/dist/scenes/remotion-diagrams.js +0 -188
- package/dist/scenes/remotion-input.d.ts +0 -13
- package/dist/scenes/remotion-input.js +0 -291
- package/dist/scenes/remotion-visuals.d.ts +0 -29
- package/dist/scenes/remotion-visuals.js +0 -198
- package/dist/scenes/render-media-assets.d.ts +0 -9
- package/dist/scenes/render-media-assets.js +0 -117
- package/dist/scenes/render-report.d.ts +0 -10
- package/dist/scenes/render-report.js +0 -27
- package/dist/scenes/render.d.ts +0 -2
- package/dist/scenes/render.js +0 -218
- package/dist/scenes/reporter.d.ts +0 -3
- package/dist/scenes/reporter.js +0 -101
- package/dist/scenes/resume.d.ts +0 -2
- package/dist/scenes/resume.js +0 -79
- package/dist/scenes/runner.d.ts +0 -2
- package/dist/scenes/runner.js +0 -687
- package/dist/scenes/schema.d.ts +0 -6
- package/dist/scenes/schema.js +0 -1023
- package/dist/scenes/seed.d.ts +0 -2
- package/dist/scenes/seed.js +0 -83
- package/dist/scenes/segments.d.ts +0 -15
- package/dist/scenes/segments.js +0 -79
- package/dist/scenes/selectors.d.ts +0 -3
- package/dist/scenes/selectors.js +0 -21
- package/dist/scenes/timeline.d.ts +0 -11
- package/dist/scenes/timeline.js +0 -26
- package/dist/scenes/timeouts.d.ts +0 -6
- package/dist/scenes/timeouts.js +0 -17
- package/dist/scenes/training-captions.d.ts +0 -14
- package/dist/scenes/training-captions.js +0 -98
- package/dist/scenes/training-report.d.ts +0 -10
- package/dist/scenes/training-report.js +0 -78
- package/dist/scenes/training-transcript.d.ts +0 -24
- package/dist/scenes/training-transcript.js +0 -204
- package/dist/scenes/training.d.ts +0 -10
- package/dist/scenes/training.js +0 -134
- package/dist/scenes/types.d.ts +0 -1900
- package/dist/scenes/types.js +0 -8
- package/dist/scenes/visual-audit-contact-sheets.d.ts +0 -6
- package/dist/scenes/visual-audit-contact-sheets.js +0 -119
- package/dist/scenes/visual-audit-fixtures.d.ts +0 -25
- package/dist/scenes/visual-audit-fixtures.js +0 -247
- package/dist/scenes/visual-audit-report.d.ts +0 -11
- package/dist/scenes/visual-audit-report.js +0 -85
- package/dist/scenes/visual-audit-review.d.ts +0 -23
- package/dist/scenes/visual-audit-review.js +0 -829
- package/dist/scenes/visual-audit-routes.d.ts +0 -13
- package/dist/scenes/visual-audit-routes.js +0 -308
- package/dist/scenes/visual-audit.d.ts +0 -3
- package/dist/scenes/visual-audit.js +0 -596
- package/dist/scripts/aggregate-book.js +0 -16
- package/dist/scripts/assert-release-tag-version.js +0 -23
- package/dist/scripts/build-dist.js +0 -294
- package/dist/scripts/build-tenant-worker.js +0 -36
- package/dist/scripts/check-build-warnings.js +0 -51
- package/dist/scripts/cleanup-markdown.js +0 -377
- package/dist/scripts/cli-test-fixtures.js +0 -44
- package/dist/scripts/config-treeseed.js +0 -131
- package/dist/scripts/deployment-readiness.js +0 -20
- package/dist/scripts/generate-treedx-openapi-types.js +0 -186
- package/dist/scripts/install-managed-dependencies.js +0 -12
- package/dist/scripts/key-agent.js +0 -165
- package/dist/scripts/local-dev.js +0 -129
- package/dist/scripts/migrate-journey-csv-to-guarantees.js +0 -140
- package/dist/scripts/operations-runner-smoke.js +0 -16
- package/dist/scripts/package-tools.js +0 -85
- package/dist/scripts/patch-starlight-content-path.js +0 -173
- package/dist/scripts/prepare.js +0 -10
- package/dist/scripts/publish-package.js +0 -29
- package/dist/scripts/release-verify.js +0 -105
- package/dist/scripts/run-fixture-astro-command.js +0 -18
- package/dist/scripts/scaffold-site.js +0 -66
- package/dist/scripts/sync-template.js +0 -20
- package/dist/scripts/template-catalog.test.js +0 -86
- package/dist/scripts/template-command.js +0 -31
- package/dist/scripts/tenant-astro-command.js +0 -3
- package/dist/scripts/tenant-build.js +0 -48
- package/dist/scripts/tenant-check.js +0 -10
- package/dist/scripts/tenant-d1-migrate-local.js +0 -15
- package/dist/scripts/tenant-deploy.js +0 -220
- package/dist/scripts/tenant-destroy.js +0 -112
- package/dist/scripts/tenant-dev.js +0 -160
- package/dist/scripts/tenant-lint.js +0 -4
- package/dist/scripts/tenant-test.js +0 -4
- package/dist/scripts/tenant-workflow-action.js +0 -101
- package/dist/scripts/test-cloudflare-local.js +0 -209
- package/dist/scripts/test-scaffold.js +0 -300
- package/dist/scripts/test-smoke.js +0 -137
- package/dist/scripts/treeseed-assert-release-tag-version.js +0 -21
- package/dist/scripts/treeseed-build-dist.js +0 -131
- package/dist/scripts/treeseed-publish-package.js +0 -19
- package/dist/scripts/treeseed-release-verify.js +0 -131
- package/dist/scripts/validate-templates.js +0 -6
- package/dist/scripts/verify-driver.js +0 -45
- package/dist/scripts/workflow-commands.test.js +0 -31
- package/dist/scripts/workspace-command-e2e.js +0 -718
- package/dist/scripts/workspace-lint.js +0 -15
- package/dist/scripts/workspace-preflight.js +0 -22
- package/dist/scripts/workspace-publish-changed-packages.js +0 -16
- package/dist/scripts/workspace-release-test.js +0 -104
- package/dist/scripts/workspace-release-verify.js +0 -81
- package/dist/scripts/workspace-save.js +0 -64
- package/dist/scripts/workspace-start-warning.js +0 -3
- package/dist/scripts/workspace-test-unit.js +0 -4
- package/dist/scripts/workspace-test.js +0 -17
- package/dist/sdk-dispatch.d.ts +0 -12
- package/dist/sdk-dispatch.js +0 -126
- package/dist/sdk-fields.d.ts +0 -11
- package/dist/sdk-filters.d.ts +0 -4
- package/dist/sdk-filters.js +0 -74
- package/dist/sdk-types.d.ts +0 -3868
- package/dist/sdk-types.js +0 -542
- package/dist/sdk-version.d.ts +0 -2
- package/dist/sdk-version.js +0 -42
- package/dist/sdk.d.ts +0 -266
- package/dist/sdk.js +0 -825
- package/dist/secrets-capability.d.ts +0 -421
- package/dist/secrets-capability.js +0 -530
- package/dist/seeds/errors.d.ts +0 -5
- package/dist/seeds/errors.js +0 -22
- package/dist/seeds/index.d.ts +0 -20
- package/dist/seeds/index.js +0 -53
- package/dist/seeds/loader.d.ts +0 -8
- package/dist/seeds/loader.js +0 -32
- package/dist/seeds/normalize.d.ts +0 -13
- package/dist/seeds/normalize.js +0 -282
- package/dist/seeds/planner.d.ts +0 -10
- package/dist/seeds/planner.js +0 -149
- package/dist/seeds/schema.d.ts +0 -2
- package/dist/seeds/schema.js +0 -893
- package/dist/seeds/types.d.ts +0 -312
- package/dist/seeds/types.js +0 -12
- package/dist/service-credentials.d.ts +0 -54
- package/dist/service-credentials.js +0 -112
- package/dist/stores/cursor-store.d.ts +0 -10
- package/dist/stores/cursor-store.js +0 -164
- package/dist/stores/envelopes.d.ts +0 -116
- package/dist/stores/envelopes.js +0 -219
- package/dist/stores/helpers.d.ts +0 -12
- package/dist/stores/helpers.js +0 -42
- package/dist/stores/lease-store.d.ts +0 -18
- package/dist/stores/lease-store.js +0 -189
- package/dist/stores/message-store.d.ts +0 -12
- package/dist/stores/message-store.js +0 -273
- package/dist/stores/operational-store.d.ts +0 -55
- package/dist/stores/operational-store.js +0 -861
- package/dist/stores/project-workflow-store.d.ts +0 -56
- package/dist/stores/project-workflow-store.js +0 -482
- package/dist/stores/run-store.d.ts +0 -9
- package/dist/stores/run-store.js +0 -75
- package/dist/stores/subscription-store.d.ts +0 -8
- package/dist/stores/subscription-store.js +0 -73
- package/dist/template-catalog.d.ts +0 -13
- package/dist/template-catalog.js +0 -151
- package/dist/template-launch-requirements.d.ts +0 -118
- package/dist/template-launch-requirements.js +0 -768
- package/dist/template-launch-ui.d.ts +0 -85
- package/dist/template-launch-ui.js +0 -189
- package/dist/timing.d.ts +0 -20
- package/dist/timing.js +0 -73
- package/dist/treedx/adapters.d.ts +0 -6
- package/dist/treedx/adapters.js +0 -36
- package/dist/treedx/client.d.ts +0 -222
- package/dist/treedx/client.js +0 -871
- package/dist/treedx/errors.d.ts +0 -13
- package/dist/treedx/federated-client.d.ts +0 -27
- package/dist/treedx/federated-client.js +0 -158
- package/dist/treedx/graph-adapter.d.ts +0 -33
- package/dist/treedx/graph-adapter.js +0 -156
- package/dist/treedx/index.d.ts +0 -14
- package/dist/treedx/index.js +0 -48
- package/dist/treedx/market-integration.d.ts +0 -27
- package/dist/treedx/market-integration.js +0 -131
- package/dist/treedx/ports.d.ts +0 -166
- package/dist/treedx/ports.js +0 -231
- package/dist/treedx/query-adapter.d.ts +0 -19
- package/dist/treedx/query-adapter.js +0 -62
- package/dist/treedx/registry-client.d.ts +0 -11
- package/dist/treedx/registry-client.js +0 -19
- package/dist/treedx/repository-adapter.d.ts +0 -45
- package/dist/treedx/repository-adapter.js +0 -319
- package/dist/treedx/sdk-integration.d.ts +0 -27
- package/dist/treedx/sdk-integration.js +0 -63
- package/dist/treedx/workspace-adapter.d.ts +0 -27
- package/dist/treedx/workspace-adapter.js +0 -65
- package/dist/treedx-backends.d.ts +0 -220
- package/dist/treedx-backends.js +0 -634
- package/dist/treedx-client.d.ts +0 -89
- package/dist/treedx-client.js +0 -184
- package/dist/treeseed/services/compose.yml +0 -6
- package/dist/treeseed/template-catalog/catalog.fixture.json +0 -707
- package/dist/types/cloudflare.d.ts +0 -54
- package/dist/verification.d.ts +0 -31
- package/dist/verification.js +0 -430
- package/dist/workflow/operations.d.ts +0 -1516
- package/dist/workflow/operations.js +0 -7001
- package/dist/workflow/policy.d.ts +0 -12
- package/dist/workflow/policy.js +0 -70
- package/dist/workflow/runs.d.ts +0 -143
- package/dist/workflow/runs.js +0 -720
- package/dist/workflow/session.d.ts +0 -34
- package/dist/workflow/session.js +0 -160
- package/dist/workflow/worktrees.d.ts +0 -43
- package/dist/workflow/worktrees.js +0 -307
- package/dist/workflow-state.d.ts +0 -250
- package/dist/workflow-state.js +0 -844
- package/dist/workflow-support.d.ts +0 -28
- package/dist/workflow-support.js +0 -390
- package/dist/workflow.d.ts +0 -327
- package/dist/workflow.js +0 -140
- package/drizzle/d1/0000_treeseed_d1.sql +0 -19
- package/drizzle/market/0000_market_control_plane.sql +0 -3223
- package/drizzle/market/0001_capacity_budget_mode_default.sql +0 -4
- package/drizzle/market/0002_user_email_addresses.sql +0 -26
- package/drizzle/market/0003_project_team_slug_unique.sql +0 -4
- package/drizzle/market/0004_treedx_market_integration.sql +0 -99
- package/drizzle/market/0005_agent_capacity_coordination.sql +0 -145
- package/drizzle/market/0006_provider_assignment_lifecycle.sql +0 -9
- package/drizzle/market/0007_agent_architecture_gap_closure.sql +0 -157
- package/drizzle/market/0008_agent_capacity_operational_closure.sql +0 -27
- package/drizzle/market/0009_agent_capacity_runtime_hardening.sql +0 -29
- package/drizzle/market/0010_secrets_capability_persistence.sql +0 -130
- package/drizzle/market/0011_github_app_adapter.sql +0 -47
- package/drizzle/market/0012_workday_test_runs.sql +0 -44
- package/drizzle/market/0013_runtime_task_lifecycle.sql +0 -39
- package/drizzle/market/0014_governance_pipeline.sql +0 -189
- package/drizzle/market/0015_agent_decision_assignment_graphs.sql +0 -60
- package/scripts/verify-driver.ts +0 -51
- package/templates/github/deploy-web.workflow.yml +0 -168
- package/templates/github/hosted-project.workflow.yml +0 -76
- /package/dist/{api/types.js → agent-capacity/contracts/capacity/workdays/workday-records.js} +0 -0
- /package/dist/{hosting/contracts.js → agent-capacity/contracts/operations/workflow-promotion.js} +0 -0
- /package/dist/{platform/contracts.js → agent-capacity/contracts/projects/agents/project-agent-class.js} +0 -0
- /package/dist/{reconcile/contracts.js → agent-capacity/contracts/runtime/runtime-observability.js} +0 -0
- /package/dist/{types/cloudflare.js → agent-capacity/contracts/support/decision-work.js} +0 -0
- /package/dist/{field-aliases.js → entrypoints/models/field-aliases.js} +0 -0
- /package/dist/{sdk-fields.js → entrypoints/models/sdk-fields.js} +0 -0
- /package/dist/{runtime.d.ts → runtime/runtime.d.ts} +0 -0
- /package/dist/{runtime.js → runtime/runtime.js} +0 -0
- /package/dist/treedx/{errors.js → support/errors.js} +0 -0
|
@@ -1,3826 +0,0 @@
|
|
|
1
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { dirname, relative, resolve } from "node:path";
|
|
4
|
-
import { spawnSync } from "node:child_process";
|
|
5
|
-
import { createInterface } from "node:readline/promises";
|
|
6
|
-
import { resolveTreeseedWebCachePolicy } from "../../platform/deploy-config.js";
|
|
7
|
-
import {
|
|
8
|
-
deleteRailwayCustomDomain,
|
|
9
|
-
deleteRailwayEnvironment,
|
|
10
|
-
deleteRailwayVolume,
|
|
11
|
-
getRailwayServiceInstance,
|
|
12
|
-
listRailwayCustomDomains,
|
|
13
|
-
listRailwayProjects,
|
|
14
|
-
listRailwayVariables,
|
|
15
|
-
listRailwayVolumes,
|
|
16
|
-
normalizeRailwayEnvironmentName,
|
|
17
|
-
resolveRailwayApiToken,
|
|
18
|
-
resolveRailwayWorkspace,
|
|
19
|
-
resolveRailwayWorkspaceContext
|
|
20
|
-
} from "./railway-api.js";
|
|
21
|
-
import { loadCliDeployConfig, resolveWranglerBin } from "./runtime-tools.js";
|
|
22
|
-
import { sdkD1MigrationsRoot } from "./runtime-paths.js";
|
|
23
|
-
const DEFAULT_COMPATIBILITY_DATE = "2026-04-05";
|
|
24
|
-
const DEFAULT_COMPATIBILITY_FLAGS = ["nodejs_compat"];
|
|
25
|
-
const DEFAULT_TREESEED_MARKET_BASE_URL = "https://api.treeseed.dev";
|
|
26
|
-
const GENERATED_ROOT = ".treeseed/generated";
|
|
27
|
-
const STATE_ROOT = ".treeseed/state";
|
|
28
|
-
const WORKTREE_METADATA_RELATIVE_PATH = ".treeseed/worktree.json";
|
|
29
|
-
const PERSISTENT_SCOPES = /* @__PURE__ */ new Set(["local", "staging", "prod"]);
|
|
30
|
-
const MANAGED_SERVICE_KEYS = ["api"];
|
|
31
|
-
const TRESEED_ENVELOPE_SCHEMA_GENERATION = "runtime-envelopes-v1";
|
|
32
|
-
const TRESEED_MIGRATION_WAVE_ID = "0005_runtime_envelopes";
|
|
33
|
-
const TRESEED_SUPPORTED_PAYLOAD_RANGE = { min: 1, max: 1 };
|
|
34
|
-
function sleepSync(milliseconds) {
|
|
35
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
|
|
36
|
-
}
|
|
37
|
-
function ensureParent(filePath) {
|
|
38
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
39
|
-
}
|
|
40
|
-
function stableHash(value) {
|
|
41
|
-
return createHash("sha256").update(value).digest("hex");
|
|
42
|
-
}
|
|
43
|
-
function compactDeploymentKey(input) {
|
|
44
|
-
const rawKey = sanitizeResourceKey(input.rawKey ?? "");
|
|
45
|
-
if (rawKey && rawKey.length <= 40) return rawKey;
|
|
46
|
-
const base = sanitizeSegment(input.slug ?? input.projectSegment ?? "project").slice(0, 27) || "project";
|
|
47
|
-
const hash = stableHash(`${input.teamId ?? ""}:${input.projectId ?? ""}:${input.slug ?? ""}`).slice(0, 8);
|
|
48
|
-
return `${base}-${hash}`;
|
|
49
|
-
}
|
|
50
|
-
function readJson(filePath, fallback) {
|
|
51
|
-
if (!existsSync(filePath)) {
|
|
52
|
-
return fallback;
|
|
53
|
-
}
|
|
54
|
-
try {
|
|
55
|
-
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
56
|
-
} catch {
|
|
57
|
-
return fallback;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
function writeJson(filePath, value) {
|
|
61
|
-
ensureParent(filePath);
|
|
62
|
-
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}
|
|
63
|
-
`, "utf8");
|
|
64
|
-
}
|
|
65
|
-
function renderTomlString(value) {
|
|
66
|
-
return JSON.stringify(String(value));
|
|
67
|
-
}
|
|
68
|
-
function envOrNull(key) {
|
|
69
|
-
const value = process.env[key];
|
|
70
|
-
return typeof value === "string" && value.length ? value : null;
|
|
71
|
-
}
|
|
72
|
-
function loadTenantDeployConfig(tenantRoot) {
|
|
73
|
-
return loadCliDeployConfig(tenantRoot);
|
|
74
|
-
}
|
|
75
|
-
function sanitizeSegment(value) {
|
|
76
|
-
return String(value).trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-").slice(0, 36) || "default";
|
|
77
|
-
}
|
|
78
|
-
function sanitizeResourceKey(value) {
|
|
79
|
-
return String(value).trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "");
|
|
80
|
-
}
|
|
81
|
-
function requireConfiguredIdentityValue(value, label) {
|
|
82
|
-
const normalized = typeof value === "string" && value.trim() ? value.trim() : "";
|
|
83
|
-
if (!normalized) {
|
|
84
|
-
throw new Error(`Configure ${label} before reconciling multi-tenant resources.`);
|
|
85
|
-
}
|
|
86
|
-
return normalized;
|
|
87
|
-
}
|
|
88
|
-
function resolveTreeseedResourceIdentity(deployConfig, target) {
|
|
89
|
-
const teamId = requireConfiguredIdentityValue(
|
|
90
|
-
envOrNull("TREESEED_HOSTING_TEAM_ID") ?? deployConfig.runtime?.teamId ?? deployConfig.hosting?.teamId,
|
|
91
|
-
"hosting.teamId or runtime.teamId in treeseed.site.yaml"
|
|
92
|
-
);
|
|
93
|
-
const projectId = requireConfiguredIdentityValue(
|
|
94
|
-
envOrNull("TREESEED_PROJECT_ID") ?? deployConfig.runtime?.projectId ?? deployConfig.hosting?.projectId,
|
|
95
|
-
"hosting.projectId or runtime.projectId in treeseed.site.yaml"
|
|
96
|
-
);
|
|
97
|
-
const teamSegment = sanitizeSegment(teamId);
|
|
98
|
-
const projectSegment = sanitizeSegment(projectId);
|
|
99
|
-
const deploymentKey = compactDeploymentKey({
|
|
100
|
-
rawKey: `${teamSegment}-${projectSegment}`,
|
|
101
|
-
teamId,
|
|
102
|
-
projectId,
|
|
103
|
-
projectSegment,
|
|
104
|
-
slug: deployConfig.slug
|
|
105
|
-
});
|
|
106
|
-
const environment = target.kind === "persistent" ? target.scope : target.branchName;
|
|
107
|
-
const environmentSegment = target.kind === "persistent" ? target.scope : sanitizeSegment(target.branchName);
|
|
108
|
-
return {
|
|
109
|
-
teamId,
|
|
110
|
-
projectId,
|
|
111
|
-
slug: deployConfig.slug,
|
|
112
|
-
environment,
|
|
113
|
-
deploymentKey,
|
|
114
|
-
environmentKey: `${deploymentKey}-${environmentSegment}`
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
function primaryHost(value) {
|
|
118
|
-
return safeUrl(value)?.hostname ?? null;
|
|
119
|
-
}
|
|
120
|
-
function domainZoneFromConfiguredWebDomain(domain) {
|
|
121
|
-
if (typeof domain !== "string" || !domain.trim()) {
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
return domain.trim().replace(/^api\./u, "");
|
|
125
|
-
}
|
|
126
|
-
function resolveSurfaceDomainZone(deployConfig) {
|
|
127
|
-
return domainZoneFromConfiguredWebDomain(
|
|
128
|
-
deployConfig.surfaces?.web?.environments?.prod?.domain ?? primaryHost(deployConfig.surfaces?.web?.publicBaseUrl ?? deployConfig.siteUrl)
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
function deriveStagingDomainHash(identity, surface) {
|
|
132
|
-
return stableHash(`${identity.teamId}:${identity.projectId}:${identity.slug}:${surface}:staging`).slice(0, 8);
|
|
133
|
-
}
|
|
134
|
-
function deriveTreeseedStagingSurfaceDomain(deployConfig, identity, surface) {
|
|
135
|
-
const zone = resolveSurfaceDomainZone(deployConfig);
|
|
136
|
-
if (!zone) {
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
const hash = deriveStagingDomainHash(identity, surface);
|
|
140
|
-
return surface === "web" ? `${identity.deploymentKey}-staging-${hash}.${zone}` : `api-${identity.deploymentKey}-staging-${hash}.${zone}`;
|
|
141
|
-
}
|
|
142
|
-
function deriveApiDomainFromWebDomain(domain) {
|
|
143
|
-
if (!domain) {
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
return domain.startsWith("api.") ? domain : `api.${domain}`;
|
|
147
|
-
}
|
|
148
|
-
function configuredApiConnectionDomain(deployConfig, scope) {
|
|
149
|
-
const apiConnection = deployConfig.connections?.api?.environments?.[scope];
|
|
150
|
-
const domain = apiConnection?.domain?.trim();
|
|
151
|
-
if (domain) {
|
|
152
|
-
return domain;
|
|
153
|
-
}
|
|
154
|
-
const connectionBaseUrl = primaryHost(apiConnection?.baseUrl);
|
|
155
|
-
if (connectionBaseUrl) {
|
|
156
|
-
return connectionBaseUrl;
|
|
157
|
-
}
|
|
158
|
-
const serviceDomain = deployConfig.services?.api?.environments?.[scope]?.domain?.trim();
|
|
159
|
-
if (serviceDomain) {
|
|
160
|
-
return serviceDomain;
|
|
161
|
-
}
|
|
162
|
-
return primaryHost(
|
|
163
|
-
deployConfig.services?.api?.environments?.[scope]?.baseUrl ?? deployConfig.services?.api?.publicBaseUrl
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
function resolveConfiguredSurfaceDomain(deployConfig, target, surface) {
|
|
167
|
-
if (target.kind !== "persistent") {
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
const scope = target.scope;
|
|
171
|
-
const configured = deployConfig.surfaces?.[surface]?.environments?.[scope]?.domain?.trim();
|
|
172
|
-
if (configured) {
|
|
173
|
-
return configured;
|
|
174
|
-
}
|
|
175
|
-
if (surface === "api") {
|
|
176
|
-
const apiConnectionDomain = configuredApiConnectionDomain(deployConfig, scope);
|
|
177
|
-
if (apiConnectionDomain) {
|
|
178
|
-
return apiConnectionDomain;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
if (scope === "staging") {
|
|
182
|
-
return deriveTreeseedStagingSurfaceDomain(
|
|
183
|
-
deployConfig,
|
|
184
|
-
resolveTreeseedResourceIdentity(deployConfig, target),
|
|
185
|
-
surface
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
if (scope !== "prod") {
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
191
|
-
if (surface === "web") {
|
|
192
|
-
return primaryHost(deployConfig.surfaces?.web?.publicBaseUrl ?? deployConfig.siteUrl);
|
|
193
|
-
}
|
|
194
|
-
return deriveApiDomainFromWebDomain(resolveConfiguredSurfaceDomain(deployConfig, target, "web"));
|
|
195
|
-
}
|
|
196
|
-
function resolveConfiguredSurfaceBaseUrl(deployConfig, target, surface) {
|
|
197
|
-
if (surface === "api") {
|
|
198
|
-
const scope = scopeFromTarget(target);
|
|
199
|
-
const apiConnection = deployConfig.connections?.api?.environments?.[scope];
|
|
200
|
-
const connectionBaseUrl = apiConnection?.baseUrl ?? (apiConnection?.domain ? `https://${apiConnection.domain}` : null);
|
|
201
|
-
if (connectionBaseUrl) {
|
|
202
|
-
return connectionBaseUrl;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
const configuredDomain = resolveConfiguredSurfaceDomain(deployConfig, target, surface);
|
|
206
|
-
if (configuredDomain) {
|
|
207
|
-
return `https://${configuredDomain}`;
|
|
208
|
-
}
|
|
209
|
-
if (surface === "web") {
|
|
210
|
-
return deployConfig.surfaces?.web?.publicBaseUrl ?? deployConfig.siteUrl ?? null;
|
|
211
|
-
}
|
|
212
|
-
if (surface === "api") {
|
|
213
|
-
const scope = scopeFromTarget(target);
|
|
214
|
-
return deployConfig.services?.api?.environments?.[scope]?.baseUrl ?? deployConfig.services?.api?.publicBaseUrl ?? null;
|
|
215
|
-
}
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
function configuredSurfaceHosts(deployConfig, target, surface) {
|
|
219
|
-
const hosts = [
|
|
220
|
-
resolveConfiguredSurfaceDomain(deployConfig, target, surface),
|
|
221
|
-
surface === "web" && target.kind === "persistent" && target.scope === "prod" ? primaryHost(deployConfig.surfaces?.web?.publicBaseUrl ?? deployConfig.siteUrl) : null
|
|
222
|
-
].filter(Boolean);
|
|
223
|
-
return [...new Set(hosts)];
|
|
224
|
-
}
|
|
225
|
-
function sharedDeploymentName(identity, role = "") {
|
|
226
|
-
const roleSegment = role === "workdayManager" ? "workday-manager" : role === "workerRunner" ? "worker-runner-01" : role;
|
|
227
|
-
return role ? `${identity.deploymentKey}-${sanitizeSegment(roleSegment)}` : identity.deploymentKey;
|
|
228
|
-
}
|
|
229
|
-
function environmentScopedIdentityName(identity, role, target) {
|
|
230
|
-
const environmentSegment = target.kind === "persistent" ? target.scope : sanitizeSegment(target.branchName);
|
|
231
|
-
return `${identity.deploymentKey}-${sanitizeSegment(role)}-${environmentSegment}`;
|
|
232
|
-
}
|
|
233
|
-
function normalizePersistentScope(scope = "prod") {
|
|
234
|
-
if (!PERSISTENT_SCOPES.has(scope)) {
|
|
235
|
-
throw new Error(`Unsupported Treeseed environment "${scope}". Expected one of local, staging, prod.`);
|
|
236
|
-
}
|
|
237
|
-
return scope;
|
|
238
|
-
}
|
|
239
|
-
function createPersistentDeployTarget(scope = "prod") {
|
|
240
|
-
return {
|
|
241
|
-
kind: "persistent",
|
|
242
|
-
scope: normalizePersistentScope(scope)
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
function createBranchPreviewDeployTarget(branchName) {
|
|
246
|
-
const normalized = String(branchName ?? "").trim();
|
|
247
|
-
if (!normalized) {
|
|
248
|
-
throw new Error("Branch preview target requires a branch name.");
|
|
249
|
-
}
|
|
250
|
-
return {
|
|
251
|
-
kind: "branch",
|
|
252
|
-
branchName: normalized
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
function normalizeTarget(scopeOrTarget = "prod") {
|
|
256
|
-
if (!scopeOrTarget || typeof scopeOrTarget === "string") {
|
|
257
|
-
return createPersistentDeployTarget(scopeOrTarget ?? "prod");
|
|
258
|
-
}
|
|
259
|
-
if (scopeOrTarget.kind === "persistent") {
|
|
260
|
-
return createPersistentDeployTarget(scopeOrTarget.scope);
|
|
261
|
-
}
|
|
262
|
-
if (scopeOrTarget.kind === "branch") {
|
|
263
|
-
return createBranchPreviewDeployTarget(scopeOrTarget.branchName);
|
|
264
|
-
}
|
|
265
|
-
throw new Error("Unsupported Treeseed deployment target.");
|
|
266
|
-
}
|
|
267
|
-
function scopeFromTarget(target) {
|
|
268
|
-
return target.kind === "persistent" ? target.scope : "staging";
|
|
269
|
-
}
|
|
270
|
-
function targetDirectoryParts(target) {
|
|
271
|
-
if (target.kind === "persistent") {
|
|
272
|
-
return ["environments", target.scope];
|
|
273
|
-
}
|
|
274
|
-
return ["branches", sanitizeSegment(target.branchName)];
|
|
275
|
-
}
|
|
276
|
-
function targetKey(target) {
|
|
277
|
-
return target.kind === "persistent" ? target.scope : `branch:${target.branchName}`;
|
|
278
|
-
}
|
|
279
|
-
function resolveManagedWorktreeStateRoot(tenantRoot) {
|
|
280
|
-
const metadataPath = resolve(tenantRoot, WORKTREE_METADATA_RELATIVE_PATH);
|
|
281
|
-
if (!existsSync(metadataPath)) return tenantRoot;
|
|
282
|
-
try {
|
|
283
|
-
const metadata = JSON.parse(readFileSync(metadataPath, "utf8")) ?? {};
|
|
284
|
-
const primaryRoot = metadata.kind === "treeseed.workflow.worktree" && typeof metadata.primaryRoot === "string" ? metadata.primaryRoot : null;
|
|
285
|
-
return primaryRoot && existsSync(resolve(primaryRoot, STATE_ROOT)) ? primaryRoot : tenantRoot;
|
|
286
|
-
} catch {
|
|
287
|
-
return tenantRoot;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
function resolveTargetPaths(tenantRoot, scopeOrTarget = "prod") {
|
|
291
|
-
const target = normalizeTarget(scopeOrTarget);
|
|
292
|
-
const pathParts = targetDirectoryParts(target);
|
|
293
|
-
const stateRoot = resolveManagedWorktreeStateRoot(tenantRoot);
|
|
294
|
-
const generatedRoot = resolve(tenantRoot, GENERATED_ROOT, ...pathParts);
|
|
295
|
-
const statePath = resolve(stateRoot, STATE_ROOT, ...pathParts, "deploy.json");
|
|
296
|
-
return {
|
|
297
|
-
target,
|
|
298
|
-
generatedRoot,
|
|
299
|
-
wranglerPath: resolve(generatedRoot, "wrangler.toml"),
|
|
300
|
-
workerEntryPath: resolve(tenantRoot, "dist/_worker.js/index.js"),
|
|
301
|
-
statePath
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
function deployTargetLabel(scopeOrTarget = "prod") {
|
|
305
|
-
const target = normalizeTarget(scopeOrTarget);
|
|
306
|
-
return target.kind === "persistent" ? target.scope : `branch:${target.branchName}`;
|
|
307
|
-
}
|
|
308
|
-
function targetWorkerName(deployConfig, target) {
|
|
309
|
-
const identity = resolveTreeseedResourceIdentity(deployConfig, target);
|
|
310
|
-
const configuredBaseName = deployConfig.cloudflare.workerName?.trim();
|
|
311
|
-
const baseName = configuredBaseName && configuredBaseName === deployConfig.slug ? identity.deploymentKey : configuredBaseName || `${identity.deploymentKey}-edge`;
|
|
312
|
-
if (target.kind === "persistent") {
|
|
313
|
-
return `${sanitizeSegment(baseName)}-${target.scope}`;
|
|
314
|
-
}
|
|
315
|
-
return `${sanitizeSegment(baseName)}-${sanitizeSegment(target.branchName)}`;
|
|
316
|
-
}
|
|
317
|
-
function targetWorkersDevUrl(workerName) {
|
|
318
|
-
return `https://${workerName}.workers.dev`;
|
|
319
|
-
}
|
|
320
|
-
function relativeFromGeneratedRoot(targetPath, generatedRoot) {
|
|
321
|
-
return relative(generatedRoot, targetPath).replaceAll("\\", "/");
|
|
322
|
-
}
|
|
323
|
-
function resolveContentServingMode(deployConfig, options = {}) {
|
|
324
|
-
const override = envOrNull("TREESEED_CONTENT_SERVING_MODE");
|
|
325
|
-
if (override) {
|
|
326
|
-
return override;
|
|
327
|
-
}
|
|
328
|
-
const target = options.target ? normalizeTarget(options.target) : null;
|
|
329
|
-
if (target?.kind === "persistent" && target.scope !== "local") {
|
|
330
|
-
return "published_runtime";
|
|
331
|
-
}
|
|
332
|
-
return deployConfig.providers?.content?.serving ?? "local_collections";
|
|
333
|
-
}
|
|
334
|
-
function buildPublicVars(deployConfig, options = {}) {
|
|
335
|
-
const target = options.target ? normalizeTarget(options.target) : createPersistentDeployTarget("prod");
|
|
336
|
-
const identity = resolveTreeseedResourceIdentity(deployConfig, createPersistentDeployTarget("prod"));
|
|
337
|
-
const contentRuntimeProvider = deployConfig.providers?.content?.runtime ?? "team_scoped_r2_overlay";
|
|
338
|
-
const contentPublishProvider = deployConfig.providers?.content?.publish ?? contentRuntimeProvider;
|
|
339
|
-
const contentServingMode = resolveContentServingMode(deployConfig, options);
|
|
340
|
-
const contentDefaultTeamId = identity.teamId;
|
|
341
|
-
const contentManifestKeyTemplate = deployConfig.cloudflare.r2?.manifestKeyTemplate ?? "teams/{teamId}/published/common.json";
|
|
342
|
-
const contentPreviewRootTemplate = deployConfig.cloudflare.r2?.previewRootTemplate ?? "teams/{teamId}/previews";
|
|
343
|
-
const contentManifestKey = contentManifestKeyTemplate.replaceAll("{teamId}", contentDefaultTeamId);
|
|
344
|
-
const managedRuntime = deployConfig.runtime?.mode === "treeseed_managed";
|
|
345
|
-
const workerRailway = deployConfig.services?.worker?.railway ?? {};
|
|
346
|
-
const webCachePolicy = resolveTreeseedWebCachePolicy(deployConfig);
|
|
347
|
-
const projectDomain = target.kind === "persistent" ? resolveConfiguredSurfaceDomain(deployConfig, target, "web") : primaryHost(deployConfig.surfaces?.web?.publicBaseUrl ?? deployConfig.siteUrl);
|
|
348
|
-
return {
|
|
349
|
-
TREESEED_HOSTING_KIND: deployConfig.hosting?.kind ?? "self_hosted_project",
|
|
350
|
-
TREESEED_HOSTING_REGISTRATION: deployConfig.hosting?.registration ?? "none",
|
|
351
|
-
TREESEED_HUB_MODE: deployConfig.hub?.mode ?? "treeseed_hosted",
|
|
352
|
-
TREESEED_RUNTIME_MODE: deployConfig.runtime?.mode ?? "none",
|
|
353
|
-
TREESEED_RUNTIME_REGISTRATION: deployConfig.runtime?.registration ?? "none",
|
|
354
|
-
TREESEED_CENTRAL_MARKET_API_BASE_URL: resolveConfiguredCentralMarketBaseUrl(deployConfig, target),
|
|
355
|
-
TREESEED_MARKET_API_BASE_URL: resolveConfiguredMarketBaseUrl(deployConfig, target),
|
|
356
|
-
TREESEED_API_BASE_URL: resolveConfiguredMarketBaseUrl(deployConfig, target),
|
|
357
|
-
TREESEED_CATALOG_MARKET_API_BASE_URLS: resolveConfiguredMarketBaseUrl(deployConfig, target) ?? envOrNull("TREESEED_CATALOG_MARKET_API_BASE_URLS"),
|
|
358
|
-
TREESEED_HOSTING_TEAM_ID: contentDefaultTeamId,
|
|
359
|
-
TREESEED_PROJECT_DOMAINS: projectDomain ?? "",
|
|
360
|
-
TREESEED_PROJECT_ID: identity.projectId,
|
|
361
|
-
TREESEED_AGENT_EXECUTION_PROVIDER: deployConfig.providers?.agents?.execution ?? "codex",
|
|
362
|
-
TREESEED_AGENT_REPOSITORY_PROVIDER: deployConfig.providers?.agents?.repository ?? "git",
|
|
363
|
-
TREESEED_AGENT_VERIFICATION_PROVIDER: deployConfig.providers?.agents?.verification ?? "local",
|
|
364
|
-
TREESEED_CONTENT_RUNTIME_PROVIDER: contentRuntimeProvider,
|
|
365
|
-
TREESEED_CONTENT_PUBLISH_PROVIDER: contentPublishProvider,
|
|
366
|
-
TREESEED_CONTENT_SERVING_MODE: contentServingMode,
|
|
367
|
-
TREESEED_CONTENT_DEFAULT_TEAM_ID: contentDefaultTeamId,
|
|
368
|
-
TREESEED_CONTENT_MANIFEST_KEY: contentManifestKey,
|
|
369
|
-
TREESEED_CONTENT_MANIFEST_KEY_TEMPLATE: contentManifestKeyTemplate,
|
|
370
|
-
TREESEED_CONTENT_PREVIEW_ROOT_TEMPLATE: contentPreviewRootTemplate,
|
|
371
|
-
TREESEED_EDITORIAL_PREVIEW_ROOT: contentPreviewRootTemplate.replaceAll("{teamId}", contentDefaultTeamId),
|
|
372
|
-
TREESEED_EDITORIAL_PREVIEW_TTL_HOURS: String(deployConfig.cloudflare.r2?.previewTtlHours ?? 168),
|
|
373
|
-
TREESEED_CONTENT_BUCKET_NAME: resolveConfiguredContentBucketName(deployConfig),
|
|
374
|
-
TREESEED_CONTENT_PUBLIC_BASE_URL: resolveConfiguredContentPublicBaseUrl(deployConfig),
|
|
375
|
-
TREESEED_WORKER_POOL_SCALER: envOrNull("TREESEED_WORKER_POOL_SCALER") ?? (managedRuntime ? "railway" : ""),
|
|
376
|
-
TREESEED_WORKDAY_TIMEZONE: envOrNull("TREESEED_WORKDAY_TIMEZONE") ?? "",
|
|
377
|
-
TREESEED_WORKDAY_WINDOWS_JSON: envOrNull("TREESEED_WORKDAY_WINDOWS_JSON") ?? "",
|
|
378
|
-
TREESEED_WORKDAY_TASK_CREDIT_BUDGET: envOrNull("TREESEED_WORKDAY_TASK_CREDIT_BUDGET") ?? "",
|
|
379
|
-
TREESEED_MANAGER_MAX_QUEUED_TASKS: envOrNull("TREESEED_MANAGER_MAX_QUEUED_TASKS") ?? "",
|
|
380
|
-
TREESEED_MANAGER_MAX_QUEUED_CREDITS: envOrNull("TREESEED_MANAGER_MAX_QUEUED_CREDITS") ?? "",
|
|
381
|
-
TREESEED_MANAGER_PRIORITY_MODELS: envOrNull("TREESEED_MANAGER_PRIORITY_MODELS") ?? "",
|
|
382
|
-
TREESEED_TASK_CREDIT_WEIGHTS_JSON: envOrNull("TREESEED_TASK_CREDIT_WEIGHTS_JSON") ?? "",
|
|
383
|
-
TREESEED_AGENT_POOL_MIN_WORKERS: envOrNull("TREESEED_AGENT_POOL_MIN_WORKERS") ?? "",
|
|
384
|
-
TREESEED_AGENT_POOL_MAX_WORKERS: envOrNull("TREESEED_AGENT_POOL_MAX_WORKERS") ?? "",
|
|
385
|
-
TREESEED_AGENT_POOL_TARGET_QUEUE_DEPTH: envOrNull("TREESEED_AGENT_POOL_TARGET_QUEUE_DEPTH") ?? "",
|
|
386
|
-
TREESEED_AGENT_POOL_COOLDOWN_SECONDS: envOrNull("TREESEED_AGENT_POOL_COOLDOWN_SECONDS") ?? "",
|
|
387
|
-
TREESEED_RAILWAY_PROJECT_ID: envOrNull("TREESEED_RAILWAY_PROJECT_ID") ?? workerRailway.projectId ?? "",
|
|
388
|
-
TREESEED_RAILWAY_ENVIRONMENT_ID: envOrNull("TREESEED_RAILWAY_ENVIRONMENT_ID") ?? "",
|
|
389
|
-
TREESEED_RAILWAY_WORKER_SERVICE_ID: envOrNull("TREESEED_RAILWAY_WORKER_SERVICE_ID") ?? workerRailway.serviceId ?? "",
|
|
390
|
-
TREESEED_PUBLIC_TURNSTILE_SITE_KEY: envOrNull("TREESEED_PUBLIC_TURNSTILE_SITE_KEY") ?? "",
|
|
391
|
-
TREESEED_AUTH_EMAIL_FROM: envOrNull("TREESEED_AUTH_EMAIL_FROM") ?? "",
|
|
392
|
-
TREESEED_AUTH_EMAIL_REPLY_TO: envOrNull("TREESEED_AUTH_EMAIL_REPLY_TO") ?? "",
|
|
393
|
-
TREESEED_SMTP_HOST: envOrNull("TREESEED_SMTP_HOST") ?? "",
|
|
394
|
-
TREESEED_SMTP_PORT: envOrNull("TREESEED_SMTP_PORT") ?? "",
|
|
395
|
-
TREESEED_SMTP_USERNAME: envOrNull("TREESEED_SMTP_USERNAME") ?? "",
|
|
396
|
-
TREESEED_SMTP_FROM: envOrNull("TREESEED_SMTP_FROM") ?? "",
|
|
397
|
-
TREESEED_SMTP_REPLY_TO: envOrNull("TREESEED_SMTP_REPLY_TO") ?? "",
|
|
398
|
-
TREESEED_SMTP_SECURE: envOrNull("TREESEED_SMTP_SECURE") ?? "",
|
|
399
|
-
TREESEED_WEB_CACHE_SOURCE_BROWSER_TTL_SECONDS: String(webCachePolicy.sourcePages.browserTtlSeconds),
|
|
400
|
-
TREESEED_WEB_CACHE_SOURCE_EDGE_TTL_SECONDS: String(webCachePolicy.sourcePages.edgeTtlSeconds),
|
|
401
|
-
TREESEED_WEB_CACHE_SOURCE_STALE_WHILE_REVALIDATE_SECONDS: String(webCachePolicy.sourcePages.staleWhileRevalidateSeconds),
|
|
402
|
-
TREESEED_WEB_CACHE_SOURCE_STALE_IF_ERROR_SECONDS: String(webCachePolicy.sourcePages.staleIfErrorSeconds),
|
|
403
|
-
TREESEED_WEB_CACHE_CONTENT_BROWSER_TTL_SECONDS: String(webCachePolicy.contentPages.browserTtlSeconds),
|
|
404
|
-
TREESEED_WEB_CACHE_CONTENT_EDGE_TTL_SECONDS: String(webCachePolicy.contentPages.edgeTtlSeconds),
|
|
405
|
-
TREESEED_WEB_CACHE_CONTENT_STALE_WHILE_REVALIDATE_SECONDS: String(webCachePolicy.contentPages.staleWhileRevalidateSeconds),
|
|
406
|
-
TREESEED_WEB_CACHE_CONTENT_STALE_IF_ERROR_SECONDS: String(webCachePolicy.contentPages.staleIfErrorSeconds),
|
|
407
|
-
TREESEED_WEB_CACHE_R2_BROWSER_TTL_SECONDS: String(webCachePolicy.r2PublishedObjects.browserTtlSeconds),
|
|
408
|
-
TREESEED_WEB_CACHE_R2_EDGE_TTL_SECONDS: String(webCachePolicy.r2PublishedObjects.edgeTtlSeconds),
|
|
409
|
-
TREESEED_WEB_CACHE_R2_STALE_WHILE_REVALIDATE_SECONDS: String(webCachePolicy.r2PublishedObjects.staleWhileRevalidateSeconds),
|
|
410
|
-
TREESEED_WEB_CACHE_R2_STALE_IF_ERROR_SECONDS: String(webCachePolicy.r2PublishedObjects.staleIfErrorSeconds)
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
function envValue(env, key) {
|
|
414
|
-
const value = env?.[key] ?? process.env[key];
|
|
415
|
-
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
416
|
-
}
|
|
417
|
-
const LOCAL_RUNTIME_AUTH_ENV_KEYS = [
|
|
418
|
-
"TREESEED_BETTER_AUTH_URL",
|
|
419
|
-
"TREESEED_SITE_URL",
|
|
420
|
-
"TREESEED_AUTH_MODE",
|
|
421
|
-
"TREESEED_AUTH_INTERNAL_SIGNUP",
|
|
422
|
-
"TREESEED_AUTH_EMAIL_LINKING",
|
|
423
|
-
"TREESEED_AUTH_ALLOW_MEMORY_DB",
|
|
424
|
-
"TREESEED_AUTH_EMAIL_FROM",
|
|
425
|
-
"TREESEED_WEB_SESSION_TTL",
|
|
426
|
-
"TREESEED_API_BOOTSTRAP_ADMIN_ALLOWLIST",
|
|
427
|
-
"TREESEED_API_AUTH_SECRET",
|
|
428
|
-
"TREESEED_API_ISSUER",
|
|
429
|
-
"TREESEED_API_ACCESS_TOKEN_TTL",
|
|
430
|
-
"TREESEED_API_REFRESH_TOKEN_TTL",
|
|
431
|
-
"TREESEED_API_DEVICE_CODE_TTL",
|
|
432
|
-
"TREESEED_API_DEVICE_ACCESS_TOKEN_TTL",
|
|
433
|
-
"TREESEED_API_DEVICE_POLL_INTERVAL",
|
|
434
|
-
"TREESEED_API_WEB_SERVICE_ID",
|
|
435
|
-
"TREESEED_API_WEB_SERVICE_SECRET",
|
|
436
|
-
"TREESEED_API_WEB_EXCHANGE_TTL",
|
|
437
|
-
"TREESEED_PLATFORM_RUNNER_SECRET",
|
|
438
|
-
"TREESEED_AUTH_GITHUB_CLIENT_ID",
|
|
439
|
-
"TREESEED_AUTH_GITHUB_CLIENT_SECRET",
|
|
440
|
-
"TREESEED_AUTH_GOOGLE_CLIENT_ID",
|
|
441
|
-
"TREESEED_AUTH_GOOGLE_CLIENT_SECRET",
|
|
442
|
-
"TREESEED_AUTH_MICROSOFT_CLIENT_ID",
|
|
443
|
-
"TREESEED_AUTH_MICROSOFT_CLIENT_SECRET",
|
|
444
|
-
"TREESEED_AUTH_APPLE_CLIENT_ID",
|
|
445
|
-
"TREESEED_AUTH_APPLE_CLIENT_SECRET"
|
|
446
|
-
];
|
|
447
|
-
function localAuthRuntimeVars(env) {
|
|
448
|
-
return Object.fromEntries(
|
|
449
|
-
LOCAL_RUNTIME_AUTH_ENV_KEYS.map((key) => [key, envValue(env, key)]).filter(([, value]) => value != null)
|
|
450
|
-
);
|
|
451
|
-
}
|
|
452
|
-
function buildLocalRuntimeVars(deployConfig, state, target, env) {
|
|
453
|
-
if (target.kind !== "persistent" || target.scope !== "local") {
|
|
454
|
-
return {};
|
|
455
|
-
}
|
|
456
|
-
const localDevAuthTtlSeconds = String(365 * 24 * 60 * 60);
|
|
457
|
-
return {
|
|
458
|
-
...localAuthRuntimeVars(env),
|
|
459
|
-
TREESEED_LOCAL_DEV_MODE: envValue(env, "TREESEED_LOCAL_DEV_MODE") ?? "cloudflare",
|
|
460
|
-
TREESEED_API_ACCESS_TOKEN_TTL: envValue(env, "TREESEED_API_ACCESS_TOKEN_TTL") ?? localDevAuthTtlSeconds,
|
|
461
|
-
TREESEED_API_REFRESH_TOKEN_TTL: envValue(env, "TREESEED_API_REFRESH_TOKEN_TTL") ?? localDevAuthTtlSeconds,
|
|
462
|
-
TREESEED_FORM_TOKEN_SECRET: envValue(env, "TREESEED_FORM_TOKEN_SECRET") ?? state.generatedSecrets?.TREESEED_FORM_TOKEN_SECRET ?? "treeseed-local-form-token-secret",
|
|
463
|
-
TREESEED_BETTER_AUTH_SECRET: envValue(env, "TREESEED_BETTER_AUTH_SECRET") ?? state.generatedSecrets?.TREESEED_BETTER_AUTH_SECRET ?? state.generatedSecrets?.TREESEED_FORM_TOKEN_SECRET ?? "treeseed-local-better-auth-secret-minimum-32-characters",
|
|
464
|
-
TREESEED_EDITORIAL_PREVIEW_SECRET: envValue(env, "TREESEED_EDITORIAL_PREVIEW_SECRET") ?? state.generatedSecrets?.TREESEED_EDITORIAL_PREVIEW_SECRET ?? "treeseed-local-editorial-preview-secret",
|
|
465
|
-
TREESEED_FORMS_LOCAL_BYPASS_CLOUDFLARE_GUARDS: envValue(env, "TREESEED_FORMS_LOCAL_BYPASS_CLOUDFLARE_GUARDS") ?? "",
|
|
466
|
-
TREESEED_MAILPIT_SMTP_HOST: "127.0.0.1",
|
|
467
|
-
TREESEED_MAILPIT_SMTP_PORT: "1025",
|
|
468
|
-
TREESEED_SMTP_HOST: "127.0.0.1",
|
|
469
|
-
TREESEED_SMTP_PORT: "1025",
|
|
470
|
-
TREESEED_SMTP_USERNAME: "",
|
|
471
|
-
TREESEED_SMTP_PASSWORD: ""
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
function buildSecretMap(deployConfig, state) {
|
|
475
|
-
const generatedSecret = state.generatedSecrets?.TREESEED_FORM_TOKEN_SECRET ?? randomBytes(24).toString("hex");
|
|
476
|
-
const previewSecret = state.generatedSecrets?.TREESEED_EDITORIAL_PREVIEW_SECRET ?? randomBytes(24).toString("hex");
|
|
477
|
-
return {
|
|
478
|
-
TREESEED_FORM_TOKEN_SECRET: envOrNull("TREESEED_FORM_TOKEN_SECRET") ?? generatedSecret,
|
|
479
|
-
TREESEED_EDITORIAL_PREVIEW_SECRET: envOrNull("TREESEED_EDITORIAL_PREVIEW_SECRET") ?? previewSecret,
|
|
480
|
-
TREESEED_TURNSTILE_SECRET_KEY: state.turnstileWidgets?.formGuard?.secret ?? envOrNull("TREESEED_TURNSTILE_SECRET_KEY"),
|
|
481
|
-
TREESEED_SMTP_PASSWORD: envOrNull("TREESEED_SMTP_PASSWORD")
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
function defaultStateFromConfig(deployConfig, target) {
|
|
485
|
-
const identity = resolveTreeseedResourceIdentity(deployConfig, target);
|
|
486
|
-
const workerName = targetWorkerName(deployConfig, target);
|
|
487
|
-
const suffix = target.kind === "persistent" ? target.scope : sanitizeSegment(target.branchName);
|
|
488
|
-
const contentManifestKeyTemplate = deployConfig.cloudflare.r2?.manifestKeyTemplate ?? "teams/{teamId}/published/common.json";
|
|
489
|
-
const contentPreviewRootTemplate = deployConfig.cloudflare.r2?.previewRootTemplate ?? "teams/{teamId}/previews";
|
|
490
|
-
const contentDefaultTeamId = identity.teamId;
|
|
491
|
-
const contentManifestKey = contentManifestKeyTemplate.replaceAll("{teamId}", contentDefaultTeamId);
|
|
492
|
-
const turnstileName = environmentScopedIdentityName(identity, "turnstile", target);
|
|
493
|
-
const turnstileDomains = configuredSurfaceHosts(deployConfig, target, "web");
|
|
494
|
-
return {
|
|
495
|
-
version: 2,
|
|
496
|
-
target,
|
|
497
|
-
identity,
|
|
498
|
-
previewEnabled: target.kind === "branch",
|
|
499
|
-
workerName,
|
|
500
|
-
kvNamespaces: {
|
|
501
|
-
FORM_GUARD_KV: {
|
|
502
|
-
name: environmentScopedIdentityName(identity, "form-guard", target),
|
|
503
|
-
binding: "FORM_GUARD_KV",
|
|
504
|
-
id: `plan-${suffix}-form-guard`,
|
|
505
|
-
previewId: `plan-${suffix}-form-guard-preview`
|
|
506
|
-
}
|
|
507
|
-
},
|
|
508
|
-
d1Databases: {
|
|
509
|
-
SITE_DATA_DB: {
|
|
510
|
-
databaseName: environmentScopedIdentityName(identity, "site-data", target),
|
|
511
|
-
binding: "SITE_DATA_DB",
|
|
512
|
-
databaseId: `plan-${suffix}-site-data`,
|
|
513
|
-
previewDatabaseId: `plan-${suffix}-site-data-preview`
|
|
514
|
-
}
|
|
515
|
-
},
|
|
516
|
-
queues: {},
|
|
517
|
-
pages: {
|
|
518
|
-
projectName: resolveConfiguredPagesProjectName(deployConfig),
|
|
519
|
-
productionBranch: deployConfig.cloudflare.pages?.productionBranch ?? "main",
|
|
520
|
-
stagingBranch: deployConfig.cloudflare.pages?.stagingBranch ?? "staging",
|
|
521
|
-
buildOutputDir: deployConfig.cloudflare.pages?.buildOutputDir ?? "dist",
|
|
522
|
-
url: resolveConfiguredSurfaceBaseUrl(deployConfig, target, "web")
|
|
523
|
-
},
|
|
524
|
-
turnstileWidgets: {
|
|
525
|
-
formGuard: {
|
|
526
|
-
name: turnstileName,
|
|
527
|
-
sitekey: null,
|
|
528
|
-
secret: null,
|
|
529
|
-
mode: "managed",
|
|
530
|
-
domains: turnstileDomains,
|
|
531
|
-
managed: true,
|
|
532
|
-
lastSyncedAt: null
|
|
533
|
-
}
|
|
534
|
-
},
|
|
535
|
-
content: {
|
|
536
|
-
runtimeProvider: deployConfig.providers?.content?.runtime ?? "team_scoped_r2_overlay",
|
|
537
|
-
publishProvider: deployConfig.providers?.content?.publish ?? deployConfig.providers?.content?.runtime ?? "team_scoped_r2_overlay",
|
|
538
|
-
defaultTeamId: contentDefaultTeamId,
|
|
539
|
-
r2Binding: resolveConfiguredContentBucketBinding(deployConfig),
|
|
540
|
-
bucketName: resolveConfiguredContentBucketName(deployConfig),
|
|
541
|
-
publicBaseUrl: resolveConfiguredContentPublicBaseUrl(deployConfig) || null,
|
|
542
|
-
manifestKeyTemplate: contentManifestKeyTemplate,
|
|
543
|
-
previewRootTemplate: contentPreviewRootTemplate,
|
|
544
|
-
previewTtlHours: deployConfig.cloudflare.r2?.previewTtlHours ?? 168,
|
|
545
|
-
manifestKey: contentManifestKey,
|
|
546
|
-
lastPublishedManifestRevision: null,
|
|
547
|
-
lastPublishedManifestSha256: null
|
|
548
|
-
},
|
|
549
|
-
hosting: {
|
|
550
|
-
kind: deployConfig.hosting?.kind ?? "self_hosted_project",
|
|
551
|
-
registration: deployConfig.hosting?.registration ?? "none",
|
|
552
|
-
marketBaseUrl: resolveConfiguredMarketBaseUrl(deployConfig) || null,
|
|
553
|
-
teamId: identity.teamId,
|
|
554
|
-
projectId: identity.projectId
|
|
555
|
-
},
|
|
556
|
-
hub: {
|
|
557
|
-
mode: deployConfig.hub?.mode ?? "treeseed_hosted"
|
|
558
|
-
},
|
|
559
|
-
runtime: {
|
|
560
|
-
mode: deployConfig.runtime?.mode ?? "none",
|
|
561
|
-
registration: deployConfig.runtime?.registration ?? "none",
|
|
562
|
-
marketBaseUrl: resolveConfiguredMarketBaseUrl(deployConfig) || null,
|
|
563
|
-
teamId: identity.teamId,
|
|
564
|
-
projectId: identity.projectId
|
|
565
|
-
},
|
|
566
|
-
webCache: {
|
|
567
|
-
webHost: null,
|
|
568
|
-
contentHost: null,
|
|
569
|
-
webZoneId: null,
|
|
570
|
-
contentZoneId: null,
|
|
571
|
-
webRulesetId: null,
|
|
572
|
-
contentRulesetId: null,
|
|
573
|
-
rulesManaged: false,
|
|
574
|
-
lastSyncedAt: null,
|
|
575
|
-
lastVerifiedAt: null,
|
|
576
|
-
lastError: null,
|
|
577
|
-
deployPurge: {
|
|
578
|
-
lastPurgedAt: null,
|
|
579
|
-
purgeCount: 0,
|
|
580
|
-
lastError: null
|
|
581
|
-
},
|
|
582
|
-
contentPurge: {
|
|
583
|
-
lastPurgedAt: null,
|
|
584
|
-
purgeCount: 0,
|
|
585
|
-
lastError: null
|
|
586
|
-
}
|
|
587
|
-
},
|
|
588
|
-
generatedSecrets: {},
|
|
589
|
-
readiness: {
|
|
590
|
-
phase: "pending",
|
|
591
|
-
configured: false,
|
|
592
|
-
provisioned: false,
|
|
593
|
-
deployable: false,
|
|
594
|
-
initialized: false,
|
|
595
|
-
initializedAt: null,
|
|
596
|
-
lastValidatedAt: null,
|
|
597
|
-
lastConfigFingerprint: null,
|
|
598
|
-
blockers: [],
|
|
599
|
-
warnings: [],
|
|
600
|
-
lastValidationSummary: null
|
|
601
|
-
},
|
|
602
|
-
lastDeployedUrl: target.kind === "branch" ? targetWorkersDevUrl(workerName) : resolveConfiguredSurfaceBaseUrl(deployConfig, target, "web"),
|
|
603
|
-
lastManifestFingerprint: null,
|
|
604
|
-
lastDeploymentTimestamp: null,
|
|
605
|
-
lastDeployedCommit: null,
|
|
606
|
-
services: Object.fromEntries(
|
|
607
|
-
MANAGED_SERVICE_KEYS.map((serviceKey) => {
|
|
608
|
-
const serviceConfig = deployConfig.services?.[serviceKey];
|
|
609
|
-
const scope = scopeFromTarget(target);
|
|
610
|
-
const baseUrl = serviceKey === "api" ? resolveConfiguredSurfaceBaseUrl(deployConfig, target, "api") ?? serviceConfig?.environments?.[scope]?.baseUrl ?? serviceConfig?.publicBaseUrl ?? null : serviceConfig?.environments?.[scope]?.baseUrl ?? serviceConfig?.publicBaseUrl ?? null;
|
|
611
|
-
return [
|
|
612
|
-
serviceKey,
|
|
613
|
-
{
|
|
614
|
-
enabled: serviceConfig?.enabled !== false && Boolean(serviceConfig),
|
|
615
|
-
provider: serviceConfig?.provider ?? (serviceConfig ? "railway" : "none"),
|
|
616
|
-
projectId: serviceConfig?.railway?.projectId ?? null,
|
|
617
|
-
projectName: serviceConfig?.railway?.projectName ?? sharedDeploymentName(identity),
|
|
618
|
-
serviceId: serviceConfig?.railway?.serviceId ?? null,
|
|
619
|
-
serviceName: serviceConfig?.environments?.[scope]?.serviceName ?? serviceConfig?.railway?.serviceName ?? sharedDeploymentName(identity, serviceKey),
|
|
620
|
-
workerName: serviceConfig?.cloudflare?.workerName ?? null,
|
|
621
|
-
rootDir: serviceConfig?.railway?.rootDir ?? serviceConfig?.rootDir ?? null,
|
|
622
|
-
environment: normalizeRailwayEnvironmentName(serviceConfig?.environments?.[scope]?.railwayEnvironment ?? scope),
|
|
623
|
-
schedule: serviceConfig?.railway?.schedule ?? null,
|
|
624
|
-
publicBaseUrl: baseUrl,
|
|
625
|
-
initialized: false,
|
|
626
|
-
lastDeploymentTimestamp: null,
|
|
627
|
-
lastDeployedUrl: baseUrl,
|
|
628
|
-
lastDeploymentCommand: null,
|
|
629
|
-
lastScheduleSyncAt: null
|
|
630
|
-
}
|
|
631
|
-
];
|
|
632
|
-
})
|
|
633
|
-
),
|
|
634
|
-
railwaySchedules: {},
|
|
635
|
-
runtimeCompatibility: {
|
|
636
|
-
envelopeSchemaGeneration: TRESEED_ENVELOPE_SCHEMA_GENERATION,
|
|
637
|
-
migrationWaveId: TRESEED_MIGRATION_WAVE_ID,
|
|
638
|
-
supportedPayloadVersionRange: TRESEED_SUPPORTED_PAYLOAD_RANGE
|
|
639
|
-
},
|
|
640
|
-
deploymentHistory: []
|
|
641
|
-
};
|
|
642
|
-
}
|
|
643
|
-
function loadDeployState(tenantRoot, deployConfig, options = {}) {
|
|
644
|
-
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
645
|
-
const defaults = defaultStateFromConfig(deployConfig, target);
|
|
646
|
-
const { statePath } = resolveTargetPaths(tenantRoot, target);
|
|
647
|
-
const persisted = readJson(statePath, {});
|
|
648
|
-
const persistedSiteDataDb = persisted.d1Databases?.SITE_DATA_DB ?? persisted.d1Databases?.SUBSCRIBERS_DB ?? {};
|
|
649
|
-
const merged = {
|
|
650
|
-
...defaults,
|
|
651
|
-
...persisted,
|
|
652
|
-
target,
|
|
653
|
-
identity: {
|
|
654
|
-
...defaults.identity ?? {},
|
|
655
|
-
...persisted.identity ?? {},
|
|
656
|
-
teamId: defaults.identity?.teamId,
|
|
657
|
-
projectId: defaults.identity?.projectId,
|
|
658
|
-
slug: defaults.identity?.slug,
|
|
659
|
-
environment: defaults.identity?.environment,
|
|
660
|
-
deploymentKey: defaults.identity?.deploymentKey,
|
|
661
|
-
environmentKey: defaults.identity?.environmentKey
|
|
662
|
-
},
|
|
663
|
-
previewEnabled: persisted.previewEnabled ?? defaults.previewEnabled,
|
|
664
|
-
workerName: defaults.workerName,
|
|
665
|
-
kvNamespaces: {
|
|
666
|
-
...defaults.kvNamespaces,
|
|
667
|
-
FORM_GUARD_KV: {
|
|
668
|
-
...defaults.kvNamespaces.FORM_GUARD_KV,
|
|
669
|
-
...persisted.kvNamespaces?.FORM_GUARD_KV ?? {},
|
|
670
|
-
name: defaults.kvNamespaces.FORM_GUARD_KV.name,
|
|
671
|
-
binding: defaults.kvNamespaces.FORM_GUARD_KV.binding
|
|
672
|
-
},
|
|
673
|
-
...persisted.kvNamespaces?.SESSION ? { SESSION: persisted.kvNamespaces.SESSION } : {}
|
|
674
|
-
},
|
|
675
|
-
d1Databases: {
|
|
676
|
-
...defaults.d1Databases,
|
|
677
|
-
...persisted.d1Databases ?? {},
|
|
678
|
-
SITE_DATA_DB: {
|
|
679
|
-
...defaults.d1Databases.SITE_DATA_DB,
|
|
680
|
-
...persistedSiteDataDb,
|
|
681
|
-
databaseName: defaults.d1Databases.SITE_DATA_DB.databaseName,
|
|
682
|
-
binding: defaults.d1Databases.SITE_DATA_DB.binding
|
|
683
|
-
}
|
|
684
|
-
},
|
|
685
|
-
queues: {},
|
|
686
|
-
generatedSecrets: {
|
|
687
|
-
...defaults.generatedSecrets ?? {},
|
|
688
|
-
...persisted.generatedSecrets ?? {}
|
|
689
|
-
},
|
|
690
|
-
turnstileWidgets: {
|
|
691
|
-
...defaults.turnstileWidgets ?? {},
|
|
692
|
-
...persisted.turnstileWidgets ?? {},
|
|
693
|
-
formGuard: {
|
|
694
|
-
...defaults.turnstileWidgets?.formGuard ?? {},
|
|
695
|
-
...persisted.turnstileWidgets?.formGuard ?? {},
|
|
696
|
-
name: defaults.turnstileWidgets?.formGuard?.name ?? persisted.turnstileWidgets?.formGuard?.name ?? null,
|
|
697
|
-
mode: "managed",
|
|
698
|
-
managed: true,
|
|
699
|
-
domains: [
|
|
700
|
-
...new Set([
|
|
701
|
-
...Array.isArray(defaults.turnstileWidgets?.formGuard?.domains) ? defaults.turnstileWidgets.formGuard.domains : [],
|
|
702
|
-
...Array.isArray(persisted.turnstileWidgets?.formGuard?.domains) ? persisted.turnstileWidgets.formGuard.domains : []
|
|
703
|
-
].filter(Boolean))
|
|
704
|
-
]
|
|
705
|
-
}
|
|
706
|
-
},
|
|
707
|
-
content: {
|
|
708
|
-
...defaults.content ?? {},
|
|
709
|
-
...persisted.content ?? {},
|
|
710
|
-
runtimeProvider: defaults.content?.runtimeProvider ?? persisted.content?.runtimeProvider ?? "team_scoped_r2_overlay",
|
|
711
|
-
publishProvider: defaults.content?.publishProvider ?? persisted.content?.publishProvider ?? "team_scoped_r2_overlay",
|
|
712
|
-
defaultTeamId: defaults.content?.defaultTeamId ?? persisted.content?.defaultTeamId ?? defaults.identity?.teamId,
|
|
713
|
-
r2Binding: defaults.content?.r2Binding ?? persisted.content?.r2Binding ?? null,
|
|
714
|
-
bucketName: defaults.content?.bucketName ?? persisted.content?.bucketName ?? null,
|
|
715
|
-
publicBaseUrl: defaults.content?.publicBaseUrl ?? persisted.content?.publicBaseUrl ?? null,
|
|
716
|
-
manifestKeyTemplate: defaults.content?.manifestKeyTemplate ?? persisted.content?.manifestKeyTemplate ?? "teams/{teamId}/published/common.json",
|
|
717
|
-
previewRootTemplate: defaults.content?.previewRootTemplate ?? persisted.content?.previewRootTemplate ?? "teams/{teamId}/previews",
|
|
718
|
-
previewTtlHours: defaults.content?.previewTtlHours ?? persisted.content?.previewTtlHours ?? 168,
|
|
719
|
-
manifestKey: defaults.content?.manifestKey ?? persisted.content?.manifestKey ?? `teams/${defaults.identity?.teamId}/published/common.json`,
|
|
720
|
-
lastPublishedManifestRevision: persisted.content?.lastPublishedManifestRevision ?? defaults.content?.lastPublishedManifestRevision ?? null,
|
|
721
|
-
lastPublishedManifestSha256: persisted.content?.lastPublishedManifestSha256 ?? defaults.content?.lastPublishedManifestSha256 ?? null
|
|
722
|
-
},
|
|
723
|
-
hosting: {
|
|
724
|
-
...defaults.hosting ?? {},
|
|
725
|
-
...persisted.hosting ?? {},
|
|
726
|
-
kind: defaults.hosting?.kind ?? persisted.hosting?.kind ?? "self_hosted_project",
|
|
727
|
-
registration: defaults.hosting?.registration ?? persisted.hosting?.registration ?? "none",
|
|
728
|
-
marketBaseUrl: defaults.hosting?.marketBaseUrl ?? persisted.hosting?.marketBaseUrl ?? null,
|
|
729
|
-
teamId: defaults.hosting?.teamId ?? persisted.hosting?.teamId ?? defaults.identity?.teamId,
|
|
730
|
-
projectId: defaults.hosting?.projectId ?? persisted.hosting?.projectId ?? defaults.identity?.projectId
|
|
731
|
-
},
|
|
732
|
-
hub: {
|
|
733
|
-
...defaults.hub ?? {},
|
|
734
|
-
...persisted.hub ?? {},
|
|
735
|
-
mode: defaults.hub?.mode ?? persisted.hub?.mode ?? "treeseed_hosted"
|
|
736
|
-
},
|
|
737
|
-
runtime: {
|
|
738
|
-
...defaults.runtime ?? {},
|
|
739
|
-
...persisted.runtime ?? {},
|
|
740
|
-
mode: defaults.runtime?.mode ?? persisted.runtime?.mode ?? "none",
|
|
741
|
-
registration: defaults.runtime?.registration ?? persisted.runtime?.registration ?? "none",
|
|
742
|
-
marketBaseUrl: defaults.runtime?.marketBaseUrl ?? persisted.runtime?.marketBaseUrl ?? null,
|
|
743
|
-
teamId: defaults.runtime?.teamId ?? persisted.runtime?.teamId ?? defaults.identity?.teamId,
|
|
744
|
-
projectId: defaults.runtime?.projectId ?? persisted.runtime?.projectId ?? defaults.identity?.projectId
|
|
745
|
-
},
|
|
746
|
-
webCache: {
|
|
747
|
-
...defaults.webCache ?? {},
|
|
748
|
-
...persisted.webCache ?? {},
|
|
749
|
-
webHost: persisted.webCache?.webHost ?? persisted.webCache?.publicHost ?? defaults.webCache?.webHost ?? null,
|
|
750
|
-
contentHost: persisted.webCache?.contentHost ?? defaults.webCache?.contentHost ?? null,
|
|
751
|
-
webZoneId: persisted.webCache?.webZoneId ?? persisted.webCache?.zoneId ?? defaults.webCache?.webZoneId ?? null,
|
|
752
|
-
contentZoneId: persisted.webCache?.contentZoneId ?? defaults.webCache?.contentZoneId ?? null,
|
|
753
|
-
webRulesetId: persisted.webCache?.webRulesetId ?? persisted.webCache?.rulesetId ?? defaults.webCache?.webRulesetId ?? null,
|
|
754
|
-
contentRulesetId: persisted.webCache?.contentRulesetId ?? defaults.webCache?.contentRulesetId ?? null,
|
|
755
|
-
rulesManaged: persisted.webCache?.rulesManaged ?? defaults.webCache?.rulesManaged ?? false,
|
|
756
|
-
lastSyncedAt: persisted.webCache?.lastSyncedAt ?? defaults.webCache?.lastSyncedAt ?? null,
|
|
757
|
-
lastVerifiedAt: persisted.webCache?.lastVerifiedAt ?? defaults.webCache?.lastVerifiedAt ?? null,
|
|
758
|
-
lastError: persisted.webCache?.lastError ?? defaults.webCache?.lastError ?? null,
|
|
759
|
-
deployPurge: {
|
|
760
|
-
...defaults.webCache?.deployPurge ?? {},
|
|
761
|
-
...persisted.webCache?.deployPurge ?? {},
|
|
762
|
-
lastPurgedAt: persisted.webCache?.deployPurge?.lastPurgedAt ?? defaults.webCache?.deployPurge?.lastPurgedAt ?? null,
|
|
763
|
-
purgeCount: persisted.webCache?.deployPurge?.purgeCount ?? defaults.webCache?.deployPurge?.purgeCount ?? 0,
|
|
764
|
-
lastError: persisted.webCache?.deployPurge?.lastError ?? defaults.webCache?.deployPurge?.lastError ?? null
|
|
765
|
-
},
|
|
766
|
-
contentPurge: {
|
|
767
|
-
...defaults.webCache?.contentPurge ?? {},
|
|
768
|
-
...persisted.webCache?.contentPurge ?? {},
|
|
769
|
-
lastPurgedAt: persisted.webCache?.contentPurge?.lastPurgedAt ?? defaults.webCache?.contentPurge?.lastPurgedAt ?? null,
|
|
770
|
-
purgeCount: persisted.webCache?.contentPurge?.purgeCount ?? defaults.webCache?.contentPurge?.purgeCount ?? 0,
|
|
771
|
-
lastError: persisted.webCache?.contentPurge?.lastError ?? defaults.webCache?.contentPurge?.lastError ?? null
|
|
772
|
-
}
|
|
773
|
-
},
|
|
774
|
-
pages: {
|
|
775
|
-
...defaults.pages ?? {},
|
|
776
|
-
...persisted.pages ?? {},
|
|
777
|
-
projectName: defaults.pages?.projectName ?? persisted.pages?.projectName ?? null,
|
|
778
|
-
productionBranch: defaults.pages?.productionBranch ?? persisted.pages?.productionBranch ?? "main",
|
|
779
|
-
stagingBranch: defaults.pages?.stagingBranch ?? persisted.pages?.stagingBranch ?? "staging",
|
|
780
|
-
buildOutputDir: defaults.pages?.buildOutputDir ?? persisted.pages?.buildOutputDir ?? "dist",
|
|
781
|
-
url: defaults.pages?.url ?? persisted.pages?.url ?? null
|
|
782
|
-
},
|
|
783
|
-
readiness: {
|
|
784
|
-
...defaults.readiness,
|
|
785
|
-
...persisted.readiness ?? {}
|
|
786
|
-
},
|
|
787
|
-
services: Object.fromEntries(
|
|
788
|
-
MANAGED_SERVICE_KEYS.map((serviceKey) => {
|
|
789
|
-
const defaultService = defaults.services?.[serviceKey] ?? {};
|
|
790
|
-
const persistedService = persisted.services?.[serviceKey] ?? {};
|
|
791
|
-
const effectiveDeploymentTimestamp = persistedService.lastDeploymentTimestamp ?? defaultService.lastDeploymentTimestamp ?? null;
|
|
792
|
-
return [
|
|
793
|
-
serviceKey,
|
|
794
|
-
{
|
|
795
|
-
...defaultService,
|
|
796
|
-
...persistedService,
|
|
797
|
-
enabled: defaultService.enabled,
|
|
798
|
-
initialized: persistedService.initialized === true && Boolean(effectiveDeploymentTimestamp),
|
|
799
|
-
provider: defaultService.provider,
|
|
800
|
-
projectId: defaultService.projectId ?? persistedService.projectId ?? null,
|
|
801
|
-
projectName: defaultService.projectName ?? persistedService.projectName ?? null,
|
|
802
|
-
serviceId: defaultService.serviceId ?? persistedService.serviceId ?? null,
|
|
803
|
-
serviceName: defaultService.serviceName ?? persistedService.serviceName ?? null,
|
|
804
|
-
workerName: defaultService.workerName ?? persistedService.workerName ?? null,
|
|
805
|
-
rootDir: defaultService.rootDir ?? persistedService.rootDir ?? null,
|
|
806
|
-
environment: defaultService.environment ?? persistedService.environment ?? null,
|
|
807
|
-
schedule: defaultService.schedule ?? persistedService.schedule ?? null,
|
|
808
|
-
publicBaseUrl: defaultService.publicBaseUrl ?? persistedService.publicBaseUrl ?? null,
|
|
809
|
-
lastDeploymentTimestamp: effectiveDeploymentTimestamp,
|
|
810
|
-
lastDeployedUrl: defaultService.publicBaseUrl ?? persistedService.lastDeployedUrl ?? null,
|
|
811
|
-
lastScheduleSyncAt: persistedService.lastScheduleSyncAt ?? defaultService.lastScheduleSyncAt ?? null
|
|
812
|
-
}
|
|
813
|
-
];
|
|
814
|
-
})
|
|
815
|
-
),
|
|
816
|
-
railwaySchedules: {
|
|
817
|
-
...persisted.railwaySchedules ?? {}
|
|
818
|
-
}
|
|
819
|
-
};
|
|
820
|
-
if (target.kind === "persistent") {
|
|
821
|
-
merged.lastDeployedUrl = resolveConfiguredSurfaceBaseUrl(deployConfig, target, "web") ?? merged.lastDeployedUrl ?? null;
|
|
822
|
-
}
|
|
823
|
-
if (target.kind === "branch" && !merged.lastDeployedUrl) {
|
|
824
|
-
merged.lastDeployedUrl = targetWorkersDevUrl(merged.workerName);
|
|
825
|
-
}
|
|
826
|
-
return merged;
|
|
827
|
-
}
|
|
828
|
-
function writeDeployState(tenantRoot, state, options = {}) {
|
|
829
|
-
const target = normalizeTarget(options.scope ?? options.target ?? state.target ?? "prod");
|
|
830
|
-
const { statePath } = resolveTargetPaths(tenantRoot, target);
|
|
831
|
-
writeJson(statePath, {
|
|
832
|
-
...state,
|
|
833
|
-
target
|
|
834
|
-
});
|
|
835
|
-
}
|
|
836
|
-
function resolveGeneratedWranglerPath(tenantRoot, options = {}) {
|
|
837
|
-
return resolveTargetPaths(tenantRoot, options.scope ?? options.target ?? "prod").wranglerPath;
|
|
838
|
-
}
|
|
839
|
-
function buildWranglerConfigContents(tenantRoot, deployConfig, state, options = {}) {
|
|
840
|
-
const target = normalizeTarget(options.scope ?? options.target ?? state.target ?? "prod");
|
|
841
|
-
const { generatedRoot } = resolveTargetPaths(tenantRoot, target);
|
|
842
|
-
const workerName = state.workerName ?? targetWorkerName(deployConfig, target);
|
|
843
|
-
const mainPath = relativeFromGeneratedRoot(resolve(tenantRoot, "dist/_worker.js/index.js"), generatedRoot);
|
|
844
|
-
const assetsDirectory = relativeFromGeneratedRoot(resolve(tenantRoot, "dist"), generatedRoot);
|
|
845
|
-
const migrationsDir = relativeFromGeneratedRoot(sdkD1MigrationsRoot, generatedRoot);
|
|
846
|
-
const vars = {
|
|
847
|
-
...buildPublicVars(deployConfig, { target }),
|
|
848
|
-
...buildLocalRuntimeVars(deployConfig, state, target, options.env)
|
|
849
|
-
};
|
|
850
|
-
const r2Config = deployConfig.cloudflare.r2;
|
|
851
|
-
const r2Binding = resolveConfiguredContentBucketBinding(deployConfig);
|
|
852
|
-
const r2BucketName = resolveConfiguredContentBucketName(deployConfig);
|
|
853
|
-
return [
|
|
854
|
-
`name = ${renderTomlString(workerName)}`,
|
|
855
|
-
`compatibility_date = ${renderTomlString(DEFAULT_COMPATIBILITY_DATE)}`,
|
|
856
|
-
`compatibility_flags = [${DEFAULT_COMPATIBILITY_FLAGS.map((flag) => renderTomlString(flag)).join(", ")}]`,
|
|
857
|
-
`main = ${renderTomlString(mainPath)}`,
|
|
858
|
-
"workers_dev = true",
|
|
859
|
-
"preview_urls = true",
|
|
860
|
-
"",
|
|
861
|
-
"[assets]",
|
|
862
|
-
`directory = ${renderTomlString(assetsDirectory)}`,
|
|
863
|
-
"",
|
|
864
|
-
"[vars]",
|
|
865
|
-
...Object.entries(vars).map(([key, value]) => `${key} = ${renderTomlString(value)}`),
|
|
866
|
-
"",
|
|
867
|
-
"[[kv_namespaces]]",
|
|
868
|
-
'binding = "FORM_GUARD_KV"',
|
|
869
|
-
`id = ${renderTomlString(state.kvNamespaces.FORM_GUARD_KV.id)}`,
|
|
870
|
-
`preview_id = ${renderTomlString(state.kvNamespaces.FORM_GUARD_KV.previewId ?? state.kvNamespaces.FORM_GUARD_KV.id)}`,
|
|
871
|
-
"",
|
|
872
|
-
"[[d1_databases]]",
|
|
873
|
-
'binding = "SITE_DATA_DB"',
|
|
874
|
-
`database_name = ${renderTomlString(state.d1Databases.SITE_DATA_DB.databaseName)}`,
|
|
875
|
-
`database_id = ${renderTomlString(state.d1Databases.SITE_DATA_DB.databaseId)}`,
|
|
876
|
-
`preview_database_id = ${renderTomlString(state.d1Databases.SITE_DATA_DB.previewDatabaseId ?? state.d1Databases.SITE_DATA_DB.databaseId)}`,
|
|
877
|
-
`migrations_dir = ${renderTomlString(migrationsDir)}`,
|
|
878
|
-
"",
|
|
879
|
-
...r2Config ? [
|
|
880
|
-
"[[r2_buckets]]",
|
|
881
|
-
`binding = ${renderTomlString(r2Binding)}`,
|
|
882
|
-
`bucket_name = ${renderTomlString(r2BucketName)}`,
|
|
883
|
-
""
|
|
884
|
-
] : []
|
|
885
|
-
].join("\n");
|
|
886
|
-
}
|
|
887
|
-
function ensureGeneratedWranglerConfig(tenantRoot, options = {}) {
|
|
888
|
-
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
889
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
890
|
-
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
891
|
-
const { wranglerPath } = resolveTargetPaths(tenantRoot, target);
|
|
892
|
-
const manifestFingerprint = stableHash(JSON.stringify({ deployConfig, targetKey: targetKey(target) }));
|
|
893
|
-
const contents = buildWranglerConfigContents(tenantRoot, deployConfig, state, { target, env: options.env });
|
|
894
|
-
ensureParent(wranglerPath);
|
|
895
|
-
writeFileSync(wranglerPath, contents, "utf8");
|
|
896
|
-
state.lastManifestFingerprint = manifestFingerprint;
|
|
897
|
-
state.readiness.lastConfigFingerprint = manifestFingerprint;
|
|
898
|
-
if (!state.generatedSecrets) {
|
|
899
|
-
state.generatedSecrets = {};
|
|
900
|
-
}
|
|
901
|
-
const secretMap = buildSecretMap(deployConfig, state);
|
|
902
|
-
state.generatedSecrets.TREESEED_FORM_TOKEN_SECRET = secretMap.TREESEED_FORM_TOKEN_SECRET;
|
|
903
|
-
state.generatedSecrets.TREESEED_EDITORIAL_PREVIEW_SECRET = secretMap.TREESEED_EDITORIAL_PREVIEW_SECRET;
|
|
904
|
-
writeDeployState(tenantRoot, state, { target });
|
|
905
|
-
return { wranglerPath, deployConfig, state, manifestFingerprint, target };
|
|
906
|
-
}
|
|
907
|
-
function runWrangler(args, { cwd, allowFailure = false, json = false, capture = false, env = {}, input } = {}) {
|
|
908
|
-
const result = spawnSync(process.execPath, [resolveWranglerBin(), ...args], {
|
|
909
|
-
stdio: json || capture || input !== void 0 ? ["pipe", "pipe", "pipe"] : "inherit",
|
|
910
|
-
cwd,
|
|
911
|
-
env: { ...process.env, ...env },
|
|
912
|
-
encoding: "utf8",
|
|
913
|
-
input
|
|
914
|
-
});
|
|
915
|
-
if (result.status !== 0 && !allowFailure) {
|
|
916
|
-
const stderr = result.stderr?.trim();
|
|
917
|
-
const stdout = result.stdout?.trim();
|
|
918
|
-
const output = [stderr, stdout].filter(Boolean).join("\n");
|
|
919
|
-
if (/Authentication error/i.test(output) || /\[code:\s*10000\]/i.test(output)) {
|
|
920
|
-
throw new Error([
|
|
921
|
-
output || `Wrangler command failed: ${args.join(" ")}`,
|
|
922
|
-
"",
|
|
923
|
-
"Treeseed Cloudflare authentication failed. Check that CLOUDFLARE_API_TOKEN is an account-level token scoped to the target account and domain.",
|
|
924
|
-
"Required Cloudflare permissions: account Pages Write, Workers Scripts Write, Workers KV Storage Write, Workers R2 Storage Write, D1 Write, Queues Write, Turnstile Sites Write, Account Rulesets Write, and Account Rule Lists Write; target zone Zone Read, DNS Write, Cache Settings Write, and SSL and Certificates Write."
|
|
925
|
-
].join("\n"));
|
|
926
|
-
}
|
|
927
|
-
throw new Error(output || `Wrangler command failed: ${args.join(" ")}`);
|
|
928
|
-
}
|
|
929
|
-
return result;
|
|
930
|
-
}
|
|
931
|
-
function parseWranglerJsonOutput(result, label) {
|
|
932
|
-
const source = `${result.stdout ?? ""}`.trim();
|
|
933
|
-
if (!source) {
|
|
934
|
-
throw new Error(`Expected JSON output from ${label}.`);
|
|
935
|
-
}
|
|
936
|
-
return JSON.parse(source);
|
|
937
|
-
}
|
|
938
|
-
function isWranglerAlreadyExistsError(error, matchers) {
|
|
939
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
940
|
-
return matchers.some((matcher) => matcher.test(message));
|
|
941
|
-
}
|
|
942
|
-
function listKvNamespaces(tenantRoot, env) {
|
|
943
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
944
|
-
if (!accountId) {
|
|
945
|
-
return [];
|
|
946
|
-
}
|
|
947
|
-
const payload = cloudflareApiRequest(`/accounts/${encodeURIComponent(accountId)}/storage/kv/namespaces?per_page=1000&order=title&direction=asc`, {
|
|
948
|
-
env,
|
|
949
|
-
allowFailure: true
|
|
950
|
-
});
|
|
951
|
-
return Array.isArray(payload?.result) ? payload.result : [];
|
|
952
|
-
}
|
|
953
|
-
function listD1Databases(tenantRoot, env) {
|
|
954
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
955
|
-
if (!accountId) {
|
|
956
|
-
return [];
|
|
957
|
-
}
|
|
958
|
-
const payload = cloudflareApiRequest(`/accounts/${encodeURIComponent(accountId)}/d1/database`, {
|
|
959
|
-
env,
|
|
960
|
-
allowFailure: true
|
|
961
|
-
});
|
|
962
|
-
return Array.isArray(payload?.result) ? payload.result : [];
|
|
963
|
-
}
|
|
964
|
-
function listQueues(tenantRoot, env) {
|
|
965
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
966
|
-
if (!accountId) {
|
|
967
|
-
return [];
|
|
968
|
-
}
|
|
969
|
-
const payload = cloudflareApiRequest(`/accounts/${encodeURIComponent(accountId)}/queues`, {
|
|
970
|
-
env,
|
|
971
|
-
allowFailure: true
|
|
972
|
-
});
|
|
973
|
-
return Array.isArray(payload?.result) ? payload.result : [];
|
|
974
|
-
}
|
|
975
|
-
function listR2Buckets(tenantRoot, env) {
|
|
976
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
977
|
-
if (!accountId) {
|
|
978
|
-
return [];
|
|
979
|
-
}
|
|
980
|
-
const payload = cloudflareApiRequest(`/accounts/${encodeURIComponent(accountId)}/r2/buckets`, {
|
|
981
|
-
env,
|
|
982
|
-
allowFailure: true
|
|
983
|
-
});
|
|
984
|
-
return Array.isArray(payload?.result?.buckets) ? payload.result.buckets : [];
|
|
985
|
-
}
|
|
986
|
-
function listPagesProjects(tenantRoot, env) {
|
|
987
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
988
|
-
if (!accountId) {
|
|
989
|
-
return [];
|
|
990
|
-
}
|
|
991
|
-
const payload = cloudflareApiRequest(`/accounts/${encodeURIComponent(accountId)}/pages/projects`, {
|
|
992
|
-
env,
|
|
993
|
-
allowFailure: true
|
|
994
|
-
});
|
|
995
|
-
return Array.isArray(payload?.result) ? payload.result : [];
|
|
996
|
-
}
|
|
997
|
-
function listTurnstileWidgets(tenantRoot, env) {
|
|
998
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
999
|
-
if (!accountId) {
|
|
1000
|
-
return [];
|
|
1001
|
-
}
|
|
1002
|
-
const payload = cloudflareApiRequest(`/accounts/${encodeURIComponent(accountId)}/challenges/widgets?per_page=100`, {
|
|
1003
|
-
env,
|
|
1004
|
-
allowFailure: true
|
|
1005
|
-
});
|
|
1006
|
-
return Array.isArray(payload?.result) ? payload.result : [];
|
|
1007
|
-
}
|
|
1008
|
-
function listWorkers(tenantRoot, env) {
|
|
1009
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1010
|
-
if (!accountId) {
|
|
1011
|
-
return [];
|
|
1012
|
-
}
|
|
1013
|
-
const payload = cloudflareApiRequest(`/accounts/${encodeURIComponent(accountId)}/workers/services?per_page=100`, {
|
|
1014
|
-
env,
|
|
1015
|
-
allowFailure: true
|
|
1016
|
-
});
|
|
1017
|
-
return Array.isArray(payload?.result) ? payload.result : [];
|
|
1018
|
-
}
|
|
1019
|
-
function listDnsZones(env) {
|
|
1020
|
-
const payload = cloudflareApiRequest("/zones?per_page=100", {
|
|
1021
|
-
env,
|
|
1022
|
-
allowFailure: true
|
|
1023
|
-
});
|
|
1024
|
-
return Array.isArray(payload?.result) ? payload.result : [];
|
|
1025
|
-
}
|
|
1026
|
-
function listDnsRecords(zoneId, env) {
|
|
1027
|
-
if (!zoneId) {
|
|
1028
|
-
return [];
|
|
1029
|
-
}
|
|
1030
|
-
const records = [];
|
|
1031
|
-
let page = 1;
|
|
1032
|
-
let totalPages = 1;
|
|
1033
|
-
while (page <= totalPages && page <= 50) {
|
|
1034
|
-
const payload = cloudflareApiRequest(
|
|
1035
|
-
`/zones/${encodeURIComponent(zoneId)}/dns_records?per_page=100&page=${page}`,
|
|
1036
|
-
{ env, allowFailure: true }
|
|
1037
|
-
);
|
|
1038
|
-
if (payload?.success === false) {
|
|
1039
|
-
break;
|
|
1040
|
-
}
|
|
1041
|
-
if (Array.isArray(payload?.result)) {
|
|
1042
|
-
records.push(...payload.result);
|
|
1043
|
-
}
|
|
1044
|
-
const reportedTotal = Number(payload?.result_info?.total_pages);
|
|
1045
|
-
totalPages = Number.isFinite(reportedTotal) && reportedTotal > 0 ? reportedTotal : page;
|
|
1046
|
-
page += 1;
|
|
1047
|
-
}
|
|
1048
|
-
return records;
|
|
1049
|
-
}
|
|
1050
|
-
function getTurnstileWidget(env, sitekey) {
|
|
1051
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1052
|
-
if (!accountId || !sitekey) {
|
|
1053
|
-
return null;
|
|
1054
|
-
}
|
|
1055
|
-
const payload = cloudflareApiRequest(
|
|
1056
|
-
`/accounts/${encodeURIComponent(accountId)}/challenges/widgets/${encodeURIComponent(sitekey)}`,
|
|
1057
|
-
{ env, allowFailure: true }
|
|
1058
|
-
);
|
|
1059
|
-
return payload?.result ?? null;
|
|
1060
|
-
}
|
|
1061
|
-
function createTurnstileWidget(env, input) {
|
|
1062
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1063
|
-
if (!accountId) {
|
|
1064
|
-
throw new Error("Configure CLOUDFLARE_ACCOUNT_ID before creating Turnstile widgets.");
|
|
1065
|
-
}
|
|
1066
|
-
try {
|
|
1067
|
-
return cloudflareApiRequest(`/accounts/${encodeURIComponent(accountId)}/challenges/widgets`, {
|
|
1068
|
-
method: "POST",
|
|
1069
|
-
env,
|
|
1070
|
-
body: {
|
|
1071
|
-
name: input.name,
|
|
1072
|
-
domains: input.domains ?? [],
|
|
1073
|
-
mode: input.mode ?? "managed"
|
|
1074
|
-
}
|
|
1075
|
-
})?.result ?? null;
|
|
1076
|
-
} catch (error) {
|
|
1077
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
1078
|
-
throw new Error(`Cloudflare Turnstile widget creation failed. Ensure the API token has Turnstile Sites Write permission: ${detail}`);
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
function updateTurnstileWidget(env, sitekey, input) {
|
|
1082
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1083
|
-
if (!accountId || !sitekey) {
|
|
1084
|
-
throw new Error("Configure CLOUDFLARE_ACCOUNT_ID and sitekey before updating Turnstile widgets.");
|
|
1085
|
-
}
|
|
1086
|
-
try {
|
|
1087
|
-
return cloudflareApiRequest(`/accounts/${encodeURIComponent(accountId)}/challenges/widgets/${encodeURIComponent(sitekey)}`, {
|
|
1088
|
-
method: "PUT",
|
|
1089
|
-
env,
|
|
1090
|
-
body: {
|
|
1091
|
-
name: input.name,
|
|
1092
|
-
domains: input.domains ?? [],
|
|
1093
|
-
mode: input.mode ?? "managed"
|
|
1094
|
-
}
|
|
1095
|
-
})?.result ?? null;
|
|
1096
|
-
} catch (error) {
|
|
1097
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
1098
|
-
throw new Error(`Cloudflare Turnstile widget update failed. Ensure the API token has Turnstile Sites Write permission: ${detail}`);
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
function buildCloudflarePagesFunctionBindings(state) {
|
|
1102
|
-
const kvNamespaces = Object.fromEntries(
|
|
1103
|
-
Object.entries(state.kvNamespaces ?? {}).map(([key, namespace]) => {
|
|
1104
|
-
const binding = namespace?.binding ?? key;
|
|
1105
|
-
const namespaceId = namespace?.id;
|
|
1106
|
-
return binding && namespaceId && !isPlaceholderResourceId(namespaceId) ? [binding, { namespace_id: namespaceId }] : null;
|
|
1107
|
-
}).filter(Boolean)
|
|
1108
|
-
);
|
|
1109
|
-
const database = state.d1Databases?.SITE_DATA_DB;
|
|
1110
|
-
const d1Databases = database?.binding && database?.databaseId && !isPlaceholderResourceId(database.databaseId) ? { [database.binding]: { id: database.databaseId } } : {};
|
|
1111
|
-
const contentBinding = state.content?.r2Binding;
|
|
1112
|
-
const contentBucketName = state.content?.bucketName;
|
|
1113
|
-
const r2Buckets = contentBinding && contentBucketName ? { [contentBinding]: { name: contentBucketName } } : {};
|
|
1114
|
-
return {
|
|
1115
|
-
...Object.keys(kvNamespaces).length ? { kv_namespaces: kvNamespaces } : {},
|
|
1116
|
-
...Object.keys(d1Databases).length ? { d1_databases: d1Databases } : {},
|
|
1117
|
-
...Object.keys(r2Buckets).length ? { r2_buckets: r2Buckets } : {}
|
|
1118
|
-
};
|
|
1119
|
-
}
|
|
1120
|
-
function mergeCloudflarePagesDeploymentConfig(config = {}, bindings = {}) {
|
|
1121
|
-
return Object.entries(bindings).reduce((merged, [key, value]) => ({
|
|
1122
|
-
...merged,
|
|
1123
|
-
[key]: {
|
|
1124
|
-
...merged[key] ?? {},
|
|
1125
|
-
...value
|
|
1126
|
-
}
|
|
1127
|
-
}), { ...config });
|
|
1128
|
-
}
|
|
1129
|
-
function ensurePagesProjectCompatibility(accountId, projectName, env, currentProject = null, options = {}) {
|
|
1130
|
-
if (!accountId || !projectName) {
|
|
1131
|
-
return;
|
|
1132
|
-
}
|
|
1133
|
-
const projectPath = `/accounts/${encodeURIComponent(accountId)}/pages/projects/${encodeURIComponent(projectName)}`;
|
|
1134
|
-
const latestProject = cloudflareApiRequest(projectPath, { env, allowFailure: true })?.result ?? currentProject;
|
|
1135
|
-
const currentConfigs = latestProject?.deployment_configs ?? {};
|
|
1136
|
-
const target = options.target;
|
|
1137
|
-
const targetConfigKey = target?.kind === "persistent" && target.scope === "prod" ? "production" : "preview";
|
|
1138
|
-
const bindings = options.state ? buildCloudflarePagesFunctionBindings(options.state) : {};
|
|
1139
|
-
const mergeCompatibility = (config = {}) => ({
|
|
1140
|
-
...config,
|
|
1141
|
-
compatibility_date: config.compatibility_date ?? DEFAULT_COMPATIBILITY_DATE,
|
|
1142
|
-
compatibility_flags: [.../* @__PURE__ */ new Set([...config.compatibility_flags ?? [], ...DEFAULT_COMPATIBILITY_FLAGS])]
|
|
1143
|
-
});
|
|
1144
|
-
const mergeTarget = (key, config = {}) => {
|
|
1145
|
-
const compatible = mergeCompatibility(config);
|
|
1146
|
-
return key === targetConfigKey && Object.keys(bindings).length ? mergeCloudflarePagesDeploymentConfig(compatible, bindings) : compatible;
|
|
1147
|
-
};
|
|
1148
|
-
cloudflareApiRequest(
|
|
1149
|
-
projectPath,
|
|
1150
|
-
{
|
|
1151
|
-
method: "PATCH",
|
|
1152
|
-
env,
|
|
1153
|
-
body: {
|
|
1154
|
-
deployment_configs: {
|
|
1155
|
-
...currentConfigs,
|
|
1156
|
-
preview: mergeTarget("preview", currentConfigs.preview),
|
|
1157
|
-
production: mergeTarget("production", currentConfigs.production)
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
);
|
|
1162
|
-
}
|
|
1163
|
-
function isPlaceholderResourceId(value) {
|
|
1164
|
-
if (!value || typeof value !== "string") {
|
|
1165
|
-
return true;
|
|
1166
|
-
}
|
|
1167
|
-
return value.startsWith("local-") || value.startsWith("plan-") || value.endsWith("-id") || value.endsWith("-preview-id");
|
|
1168
|
-
}
|
|
1169
|
-
function buildProvisioningSummary(deployConfig, state, target) {
|
|
1170
|
-
const webCachePolicy = resolveTreeseedWebCachePolicy(deployConfig);
|
|
1171
|
-
const identity = state.identity ?? resolveTreeseedResourceIdentity(deployConfig, target);
|
|
1172
|
-
const configuredWebDomain = resolveConfiguredSurfaceDomain(deployConfig, target, "web");
|
|
1173
|
-
const configuredApiDomain = resolveConfiguredSurfaceDomain(deployConfig, target, "api");
|
|
1174
|
-
return {
|
|
1175
|
-
target: deployTargetLabel(target),
|
|
1176
|
-
identity,
|
|
1177
|
-
workerName: state.workerName ?? targetWorkerName(deployConfig, target),
|
|
1178
|
-
siteUrl: target.kind === "branch" ? targetWorkersDevUrl(state.workerName) : deployConfig.siteUrl,
|
|
1179
|
-
accountId: resolveConfiguredCloudflareAccountId(deployConfig),
|
|
1180
|
-
pages: state.pages ?? null,
|
|
1181
|
-
turnstileWidget: state.turnstileWidgets?.formGuard ?? null,
|
|
1182
|
-
formGuardKv: state.kvNamespaces.FORM_GUARD_KV,
|
|
1183
|
-
sessionKv: state.kvNamespaces.SESSION ?? null,
|
|
1184
|
-
siteDataDb: state.d1Databases.SITE_DATA_DB,
|
|
1185
|
-
content: state.content ?? null,
|
|
1186
|
-
resources: {
|
|
1187
|
-
pagesProject: state.pages?.projectName ?? null,
|
|
1188
|
-
contentBucket: state.content?.bucketName ?? null,
|
|
1189
|
-
database: state.d1Databases?.SITE_DATA_DB?.databaseName ?? null,
|
|
1190
|
-
turnstileWidget: state.turnstileWidgets?.formGuard?.name ?? null,
|
|
1191
|
-
formGuardKv: state.kvNamespaces?.FORM_GUARD_KV?.name ?? null,
|
|
1192
|
-
railwayProject: state.services?.worker?.projectName ?? state.services?.api?.projectName ?? null,
|
|
1193
|
-
webDomain: configuredWebDomain,
|
|
1194
|
-
apiDomain: configuredApiDomain,
|
|
1195
|
-
railwayServices: Object.fromEntries(
|
|
1196
|
-
Object.entries(state.services ?? {}).filter(([, service]) => service?.enabled === true).map(([serviceKey, service]) => [serviceKey, service?.serviceName ?? null])
|
|
1197
|
-
)
|
|
1198
|
-
},
|
|
1199
|
-
webCache: {
|
|
1200
|
-
webHost: state.webCache?.webHost ?? null,
|
|
1201
|
-
contentHost: state.webCache?.contentHost ?? null,
|
|
1202
|
-
rulesManaged: state.webCache?.rulesManaged === true,
|
|
1203
|
-
lastSyncedAt: state.webCache?.lastSyncedAt ?? null,
|
|
1204
|
-
lastError: state.webCache?.lastError ?? null,
|
|
1205
|
-
policy: webCachePolicy,
|
|
1206
|
-
deployPurge: state.webCache?.deployPurge ?? null,
|
|
1207
|
-
contentPurge: state.webCache?.contentPurge ?? null
|
|
1208
|
-
}
|
|
1209
|
-
};
|
|
1210
|
-
}
|
|
1211
|
-
function safeUrl(value) {
|
|
1212
|
-
try {
|
|
1213
|
-
return new URL(value);
|
|
1214
|
-
} catch {
|
|
1215
|
-
return null;
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
function normalizePathPrefix(pathname) {
|
|
1219
|
-
const normalized = String(pathname ?? "").replace(/\/+$/u, "");
|
|
1220
|
-
if (!normalized || normalized === "/") {
|
|
1221
|
-
return "";
|
|
1222
|
-
}
|
|
1223
|
-
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
1224
|
-
}
|
|
1225
|
-
function resolvePublicWebCacheTarget(deployConfig) {
|
|
1226
|
-
const parsed = safeUrl(deployConfig.surfaces?.web?.publicBaseUrl ?? deployConfig.siteUrl);
|
|
1227
|
-
if (!parsed) {
|
|
1228
|
-
return null;
|
|
1229
|
-
}
|
|
1230
|
-
return {
|
|
1231
|
-
host: parsed.hostname,
|
|
1232
|
-
pathPrefix: normalizePathPrefix(parsed.pathname)
|
|
1233
|
-
};
|
|
1234
|
-
}
|
|
1235
|
-
function resolvePublicContentCacheTarget(deployConfig) {
|
|
1236
|
-
const parsed = safeUrl(resolveConfiguredContentPublicBaseUrl(deployConfig));
|
|
1237
|
-
if (!parsed) {
|
|
1238
|
-
return null;
|
|
1239
|
-
}
|
|
1240
|
-
return {
|
|
1241
|
-
host: parsed.hostname,
|
|
1242
|
-
pathPrefix: normalizePathPrefix(parsed.pathname)
|
|
1243
|
-
};
|
|
1244
|
-
}
|
|
1245
|
-
function shouldManageCloudflareWebCacheRules(deployConfig, target) {
|
|
1246
|
-
if (target.kind !== "persistent" || target.scope !== "prod") {
|
|
1247
|
-
return false;
|
|
1248
|
-
}
|
|
1249
|
-
if ((deployConfig.surfaces?.web?.provider ?? deployConfig.providers?.deploy) !== "cloudflare") {
|
|
1250
|
-
return false;
|
|
1251
|
-
}
|
|
1252
|
-
const webTarget = resolvePublicWebCacheTarget(deployConfig);
|
|
1253
|
-
return Boolean(webTarget?.host && !webTarget.host.endsWith(".workers.dev") && !webTarget.host.endsWith(".pages.dev"));
|
|
1254
|
-
}
|
|
1255
|
-
function cloudflareApiRequest(path, { method = "GET", body, env, allowFailure = false } = {}) {
|
|
1256
|
-
const token = env?.TREESEED_CLOUDFLARE_API_TOKEN ?? env?.CLOUDFLARE_API_TOKEN ?? process.env.TREESEED_CLOUDFLARE_API_TOKEN ?? "";
|
|
1257
|
-
if (!token) {
|
|
1258
|
-
if (allowFailure) {
|
|
1259
|
-
return null;
|
|
1260
|
-
}
|
|
1261
|
-
throw new Error(`Cloudflare API token is required: ${method} ${path}`);
|
|
1262
|
-
}
|
|
1263
|
-
const requestScript = `import { readFileSync } from 'node:fs';
|
|
1264
|
-
import { request } from 'node:https';
|
|
1265
|
-
const input = JSON.parse(readFileSync(0, 'utf8') || '{}');
|
|
1266
|
-
function errorMessage(error) {
|
|
1267
|
-
const parts = [];
|
|
1268
|
-
if (error && typeof error.message === 'string') parts.push(error.message);
|
|
1269
|
-
const cause = error?.cause;
|
|
1270
|
-
if (cause && typeof cause.message === 'string') parts.push(cause.message);
|
|
1271
|
-
if (cause && typeof cause.code === 'string') parts.push(cause.code);
|
|
1272
|
-
if (Array.isArray(cause?.errors)) {
|
|
1273
|
-
for (const entry of cause.errors) {
|
|
1274
|
-
if (entry && typeof entry.message === 'string') parts.push(entry.message);
|
|
1275
|
-
if (entry && typeof entry.code === 'string') parts.push(entry.code);
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
return [...new Set(parts.filter(Boolean))].join('; ') || String(error);
|
|
1279
|
-
}
|
|
1280
|
-
try {
|
|
1281
|
-
const body = input.body ? JSON.stringify(input.body) : undefined;
|
|
1282
|
-
const response = await new Promise((resolve, reject) => {
|
|
1283
|
-
const req = request(input.url, {
|
|
1284
|
-
method: input.method,
|
|
1285
|
-
headers: {
|
|
1286
|
-
authorization: 'Bearer ' + input.token,
|
|
1287
|
-
'content-type': 'application/json',
|
|
1288
|
-
},
|
|
1289
|
-
timeout: input.timeoutMs ?? 12000,
|
|
1290
|
-
}, (res) => {
|
|
1291
|
-
const chunks = [];
|
|
1292
|
-
res.setEncoding('utf8');
|
|
1293
|
-
res.on('data', (chunk) => chunks.push(chunk));
|
|
1294
|
-
res.on('end', () => resolve({
|
|
1295
|
-
ok: typeof res.statusCode === 'number' && res.statusCode >= 200 && res.statusCode < 300,
|
|
1296
|
-
text: chunks.join(''),
|
|
1297
|
-
}));
|
|
1298
|
-
});
|
|
1299
|
-
req.on('timeout', () => {
|
|
1300
|
-
req.destroy(new Error('Cloudflare API request timed out'));
|
|
1301
|
-
});
|
|
1302
|
-
req.on('error', reject);
|
|
1303
|
-
if (body) req.write(body);
|
|
1304
|
-
req.end();
|
|
1305
|
-
});
|
|
1306
|
-
const rawBody = response.text;
|
|
1307
|
-
let payload;
|
|
1308
|
-
try {
|
|
1309
|
-
payload = rawBody ? JSON.parse(rawBody) : {};
|
|
1310
|
-
} catch {
|
|
1311
|
-
payload = { success: false, errors: [{ message: rawBody || 'empty response' }] };
|
|
1312
|
-
}
|
|
1313
|
-
process.stdout.write(JSON.stringify({ ok: response.ok, payload }));
|
|
1314
|
-
} catch (error) {
|
|
1315
|
-
process.stdout.write(JSON.stringify({
|
|
1316
|
-
ok: false,
|
|
1317
|
-
transient: true,
|
|
1318
|
-
payload: { success: false, errors: [{ message: errorMessage(error) }] },
|
|
1319
|
-
}));
|
|
1320
|
-
}`;
|
|
1321
|
-
const requestInput = JSON.stringify({
|
|
1322
|
-
url: `https://api.cloudflare.com/client/v4${path}`,
|
|
1323
|
-
method,
|
|
1324
|
-
body,
|
|
1325
|
-
timeoutMs: 12e3,
|
|
1326
|
-
token
|
|
1327
|
-
});
|
|
1328
|
-
const isTransient = (text) => /fetch failed|timed out|etimedout|econnreset|enetunreach|temporarily unavailable|aborted|rate limit|too many requests|throttl|please wait/iu.test(text || "");
|
|
1329
|
-
const retryDelay = (text, currentAttempt) => {
|
|
1330
|
-
const base = /rate limit|too many requests|throttl|please wait/iu.test(text || "") ? 2500 : 500;
|
|
1331
|
-
return base * (currentAttempt + 1);
|
|
1332
|
-
};
|
|
1333
|
-
const formatPayloadErrors = (payload) => Array.isArray(payload?.errors) ? payload.errors.map((entry) => entry?.message ?? JSON.stringify(entry)).join("; ") : "";
|
|
1334
|
-
const summarizeChildError = (text) => {
|
|
1335
|
-
const lines = String(text || "").split("\n").map((line) => line.trim()).filter(Boolean);
|
|
1336
|
-
return lines.find((line) => /fetch failed|timed out|etimedout|econnreset|enetunreach|temporarily unavailable|aborted|typeerror|error/iu.test(line)) ?? lines[0] ?? "";
|
|
1337
|
-
};
|
|
1338
|
-
let attempt = 0;
|
|
1339
|
-
for (; ; ) {
|
|
1340
|
-
const response = spawnSync(
|
|
1341
|
-
process.execPath,
|
|
1342
|
-
[
|
|
1343
|
-
"--input-type=module",
|
|
1344
|
-
"-e",
|
|
1345
|
-
requestScript
|
|
1346
|
-
],
|
|
1347
|
-
{
|
|
1348
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
1349
|
-
encoding: "utf8",
|
|
1350
|
-
env: { ...process.env, ...env ?? {} },
|
|
1351
|
-
input: requestInput,
|
|
1352
|
-
timeout: 15e3
|
|
1353
|
-
}
|
|
1354
|
-
);
|
|
1355
|
-
if (response.error?.code === "ETIMEDOUT") {
|
|
1356
|
-
if (attempt < 7) {
|
|
1357
|
-
attempt += 1;
|
|
1358
|
-
sleepSync(retryDelay("timed out", attempt));
|
|
1359
|
-
continue;
|
|
1360
|
-
}
|
|
1361
|
-
if (!allowFailure) {
|
|
1362
|
-
throw new Error(`Cloudflare API request timed out after ${attempt + 1} attempts: ${method} ${path}`);
|
|
1363
|
-
}
|
|
1364
|
-
return null;
|
|
1365
|
-
}
|
|
1366
|
-
const stderr = response.stderr?.trim() || "";
|
|
1367
|
-
if (response.status !== 0) {
|
|
1368
|
-
if (attempt < 7 && isTransient(stderr)) {
|
|
1369
|
-
attempt += 1;
|
|
1370
|
-
sleepSync(retryDelay(stderr, attempt));
|
|
1371
|
-
continue;
|
|
1372
|
-
}
|
|
1373
|
-
if (!allowFailure) {
|
|
1374
|
-
const detail = summarizeChildError(stderr);
|
|
1375
|
-
throw new Error(detail ? `Cloudflare API request failed after ${attempt + 1} attempts: ${method} ${path}: ${detail}` : `Cloudflare API request failed after ${attempt + 1} attempts: ${method} ${path}`);
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
let parsed;
|
|
1379
|
-
try {
|
|
1380
|
-
parsed = JSON.parse(response.stdout?.trim() || '{"ok":false,"payload":{"success":false,"errors":[{"message":"empty response"}]}}');
|
|
1381
|
-
} catch {
|
|
1382
|
-
parsed = {
|
|
1383
|
-
ok: false,
|
|
1384
|
-
payload: {
|
|
1385
|
-
success: false,
|
|
1386
|
-
errors: [{ message: response.stdout?.trim() || stderr || "empty response" }]
|
|
1387
|
-
}
|
|
1388
|
-
};
|
|
1389
|
-
}
|
|
1390
|
-
const details = formatPayloadErrors(parsed.payload);
|
|
1391
|
-
if (!parsed.ok && isTransient(details) && attempt < 7) {
|
|
1392
|
-
attempt += 1;
|
|
1393
|
-
sleepSync(retryDelay(details, attempt));
|
|
1394
|
-
continue;
|
|
1395
|
-
}
|
|
1396
|
-
if (!parsed.ok && !allowFailure) {
|
|
1397
|
-
throw new Error(details ? `Cloudflare API request failed after ${attempt + 1} attempts: ${method} ${path}: ${details}` : `Cloudflare API request failed after ${attempt + 1} attempts: ${method} ${path}`);
|
|
1398
|
-
}
|
|
1399
|
-
return parsed.payload;
|
|
1400
|
-
}
|
|
1401
|
-
}
|
|
1402
|
-
function resolveCloudflareZoneIdForHost(deployConfig, host, env) {
|
|
1403
|
-
if (deployConfig.cloudflare.zoneId) {
|
|
1404
|
-
return deployConfig.cloudflare.zoneId;
|
|
1405
|
-
}
|
|
1406
|
-
const result = cloudflareApiRequest(`/zones?name=${encodeURIComponent(host)}`, { env, allowFailure: true });
|
|
1407
|
-
const exact = Array.isArray(result?.result) ? result.result.find((zone) => zone?.name === host) : null;
|
|
1408
|
-
if (exact?.id) {
|
|
1409
|
-
return exact.id;
|
|
1410
|
-
}
|
|
1411
|
-
const fallback = cloudflareApiRequest("/zones", { env, allowFailure: true });
|
|
1412
|
-
const zones = Array.isArray(fallback?.result) ? fallback.result : [];
|
|
1413
|
-
const matched = zones.filter((zone) => typeof zone?.name === "string" && (host === zone.name || host.endsWith(`.${zone.name}`))).sort((left, right) => String(right.name).length - String(left.name).length)[0];
|
|
1414
|
-
return matched?.id ?? null;
|
|
1415
|
-
}
|
|
1416
|
-
function listCloudflareZoneRulesets(zoneId, env) {
|
|
1417
|
-
const result = cloudflareApiRequest(`/zones/${zoneId}/rulesets`, { env, allowFailure: true });
|
|
1418
|
-
return Array.isArray(result?.result) ? result.result : [];
|
|
1419
|
-
}
|
|
1420
|
-
function joinCloudflareAndExpression(clauses) {
|
|
1421
|
-
const parts = clauses.map((clause) => typeof clause === "string" ? clause.trim() : "").filter((clause) => clause.length > 0).map((clause) => clause.startsWith("(") && clause.endsWith(")") ? clause : `(${clause})`);
|
|
1422
|
-
if (parts.length === 0) {
|
|
1423
|
-
throw new Error("Cannot build a Cloudflare expression without predicates.");
|
|
1424
|
-
}
|
|
1425
|
-
return `(${parts.join(" and ")})`;
|
|
1426
|
-
}
|
|
1427
|
-
function buildTreeseedManagedCloudflareCacheRules(deployConfig, cacheTarget, kind) {
|
|
1428
|
-
if (!cacheTarget?.host) {
|
|
1429
|
-
return [];
|
|
1430
|
-
}
|
|
1431
|
-
const policy = resolveTreeseedWebCachePolicy(deployConfig);
|
|
1432
|
-
const cachePolicy = kind === "web" ? policy.contentPages : policy.r2PublishedObjects;
|
|
1433
|
-
const hostExpression = `(http.host eq "${cacheTarget.host}")`;
|
|
1434
|
-
const pathExpression = cacheTarget.pathPrefix ? `(starts_with(http.request.uri.path, "${cacheTarget.pathPrefix}/") or (http.request.uri.path eq "${cacheTarget.pathPrefix}"))` : null;
|
|
1435
|
-
if (kind === "content") {
|
|
1436
|
-
return [
|
|
1437
|
-
{
|
|
1438
|
-
description: "treeseed-managed: cache public r2 objects",
|
|
1439
|
-
expression: joinCloudflareAndExpression([
|
|
1440
|
-
hostExpression,
|
|
1441
|
-
pathExpression,
|
|
1442
|
-
'(http.request.method in {"GET" "HEAD"})'
|
|
1443
|
-
]),
|
|
1444
|
-
action: "set_cache_settings",
|
|
1445
|
-
action_parameters: {
|
|
1446
|
-
cache: true,
|
|
1447
|
-
edge_ttl: {
|
|
1448
|
-
mode: "override_origin",
|
|
1449
|
-
default: cachePolicy.edgeTtlSeconds
|
|
1450
|
-
},
|
|
1451
|
-
browser_ttl: {
|
|
1452
|
-
mode: "override_origin",
|
|
1453
|
-
default: cachePolicy.browserTtlSeconds
|
|
1454
|
-
}
|
|
1455
|
-
},
|
|
1456
|
-
enabled: true
|
|
1457
|
-
}
|
|
1458
|
-
];
|
|
1459
|
-
}
|
|
1460
|
-
const sourcePaths = policy.sourcePages.paths.map((path) => path === "/" ? "/" : path.replace(/\/+$/u, ""));
|
|
1461
|
-
const sourcePathExpression = sourcePaths.length > 0 ? `(${sourcePaths.map((path) => `(http.request.uri.path eq "${path}")`).join(" or ")})` : null;
|
|
1462
|
-
const notSourcePathExpression = sourcePaths.length > 0 ? `not ${sourcePathExpression}` : null;
|
|
1463
|
-
const rules = [
|
|
1464
|
-
{
|
|
1465
|
-
description: "treeseed-managed: bypass preview and dynamic routes",
|
|
1466
|
-
expression: joinCloudflareAndExpression([
|
|
1467
|
-
hostExpression,
|
|
1468
|
-
'((starts_with(http.request.uri.path, "/api/")) or (http.request.uri.path eq "/api") or (starts_with(http.request.uri.path, "/auth")) or (starts_with(http.request.uri.path, "/admin")) or (starts_with(http.request.uri.path, "/app")) or (starts_with(http.request.uri.path, "/internal")) or (http.request.uri.query contains "preview=") or (http.cookie contains "treeseed-content-preview="))'
|
|
1469
|
-
]),
|
|
1470
|
-
action: "set_cache_settings",
|
|
1471
|
-
action_parameters: {
|
|
1472
|
-
cache: false
|
|
1473
|
-
},
|
|
1474
|
-
enabled: true
|
|
1475
|
-
}
|
|
1476
|
-
];
|
|
1477
|
-
if (sourcePathExpression) {
|
|
1478
|
-
rules.push({
|
|
1479
|
-
description: "treeseed-managed: bypass source html routes",
|
|
1480
|
-
expression: joinCloudflareAndExpression([
|
|
1481
|
-
hostExpression,
|
|
1482
|
-
pathExpression,
|
|
1483
|
-
sourcePathExpression,
|
|
1484
|
-
'(http.request.method in {"GET" "HEAD"})'
|
|
1485
|
-
]),
|
|
1486
|
-
action: "set_cache_settings",
|
|
1487
|
-
action_parameters: {
|
|
1488
|
-
cache: false
|
|
1489
|
-
},
|
|
1490
|
-
enabled: true
|
|
1491
|
-
});
|
|
1492
|
-
}
|
|
1493
|
-
rules.push(
|
|
1494
|
-
{
|
|
1495
|
-
description: "treeseed-managed: cache content html routes",
|
|
1496
|
-
expression: joinCloudflareAndExpression([
|
|
1497
|
-
hostExpression,
|
|
1498
|
-
pathExpression,
|
|
1499
|
-
notSourcePathExpression,
|
|
1500
|
-
'(http.request.method in {"GET" "HEAD"})',
|
|
1501
|
-
'(http.request.uri.path.extension eq "")',
|
|
1502
|
-
'not (starts_with(http.request.uri.path, "/api/"))',
|
|
1503
|
-
'not (http.request.uri.path eq "/api")',
|
|
1504
|
-
'not (starts_with(http.request.uri.path, "/auth"))',
|
|
1505
|
-
'not (starts_with(http.request.uri.path, "/admin"))',
|
|
1506
|
-
'not (starts_with(http.request.uri.path, "/app"))',
|
|
1507
|
-
'not (starts_with(http.request.uri.path, "/internal"))',
|
|
1508
|
-
'not (http.request.uri.query contains "preview=")',
|
|
1509
|
-
'not (http.cookie contains "treeseed-content-preview=")'
|
|
1510
|
-
]),
|
|
1511
|
-
action: "set_cache_settings",
|
|
1512
|
-
action_parameters: {
|
|
1513
|
-
cache: true,
|
|
1514
|
-
edge_ttl: {
|
|
1515
|
-
mode: "override_origin",
|
|
1516
|
-
default: cachePolicy.edgeTtlSeconds
|
|
1517
|
-
},
|
|
1518
|
-
browser_ttl: {
|
|
1519
|
-
mode: "override_origin",
|
|
1520
|
-
default: cachePolicy.browserTtlSeconds
|
|
1521
|
-
}
|
|
1522
|
-
},
|
|
1523
|
-
enabled: true
|
|
1524
|
-
}
|
|
1525
|
-
);
|
|
1526
|
-
return rules;
|
|
1527
|
-
}
|
|
1528
|
-
function reconcileCloudflareCacheRulesForTarget(role, deployConfig, state, cacheTarget, env, { planOnly = false } = {}) {
|
|
1529
|
-
const roleKey = role === "web" ? "Web" : "Content";
|
|
1530
|
-
if (!cacheTarget?.host) {
|
|
1531
|
-
return { managed: false, skipped: true, reason: "missing_host" };
|
|
1532
|
-
}
|
|
1533
|
-
const zoneId = resolveCloudflareZoneIdForHost(deployConfig, cacheTarget.host, env);
|
|
1534
|
-
if (!zoneId) {
|
|
1535
|
-
return { managed: false, skipped: true, reason: "zone_unresolved" };
|
|
1536
|
-
}
|
|
1537
|
-
const desiredRules = buildTreeseedManagedCloudflareCacheRules(deployConfig, cacheTarget, role);
|
|
1538
|
-
state.webCache[role === "web" ? "webHost" : "contentHost"] = cacheTarget.host;
|
|
1539
|
-
state.webCache[role === "web" ? "webZoneId" : "contentZoneId"] = zoneId;
|
|
1540
|
-
if (planOnly) {
|
|
1541
|
-
return { managed: true, planOnly: true, zoneId, host: cacheTarget.host, rules: desiredRules };
|
|
1542
|
-
}
|
|
1543
|
-
const rulesets = listCloudflareZoneRulesets(zoneId, env);
|
|
1544
|
-
const existing = rulesets.find((ruleset) => ruleset?.phase === "http_request_cache_settings") ?? null;
|
|
1545
|
-
const prefix = `treeseed-managed:${roleKey.toLowerCase()}:`;
|
|
1546
|
-
const unmanagedRules = Array.isArray(existing?.rules) ? existing.rules.filter((rule) => typeof rule?.description !== "string" || !rule.description.startsWith(prefix)) : [];
|
|
1547
|
-
const rules = [
|
|
1548
|
-
...unmanagedRules,
|
|
1549
|
-
...desiredRules.map((rule) => ({ ...rule, description: `${prefix} ${rule.description}` }))
|
|
1550
|
-
];
|
|
1551
|
-
const payload = existing ? cloudflareApiRequest(`/zones/${zoneId}/rulesets/${existing.id}`, {
|
|
1552
|
-
method: "PUT",
|
|
1553
|
-
body: { rules },
|
|
1554
|
-
env
|
|
1555
|
-
}) : cloudflareApiRequest(`/zones/${zoneId}/rulesets`, {
|
|
1556
|
-
method: "POST",
|
|
1557
|
-
body: {
|
|
1558
|
-
name: `Treeseed Managed ${roleKey} Cache Rules`,
|
|
1559
|
-
kind: "zone",
|
|
1560
|
-
phase: "http_request_cache_settings",
|
|
1561
|
-
rules
|
|
1562
|
-
},
|
|
1563
|
-
env
|
|
1564
|
-
});
|
|
1565
|
-
const rulesetId = payload?.result?.id ?? existing?.id ?? null;
|
|
1566
|
-
state.webCache[role === "web" ? "webRulesetId" : "contentRulesetId"] = rulesetId;
|
|
1567
|
-
return { managed: true, zoneId, host: cacheTarget.host, rulesetId };
|
|
1568
|
-
}
|
|
1569
|
-
function reconcileCloudflareWebCacheRules(tenantRoot, deployConfig, state, target, { planOnly = false, env: providedEnv } = {}) {
|
|
1570
|
-
if (!shouldManageCloudflareWebCacheRules(deployConfig, target)) {
|
|
1571
|
-
const webTarget2 = resolvePublicWebCacheTarget(deployConfig);
|
|
1572
|
-
const contentTarget2 = resolvePublicContentCacheTarget(deployConfig);
|
|
1573
|
-
state.webCache.webHost = webTarget2?.host ?? null;
|
|
1574
|
-
state.webCache.contentHost = contentTarget2?.host ?? null;
|
|
1575
|
-
state.webCache.rulesManaged = false;
|
|
1576
|
-
state.webCache.lastError = null;
|
|
1577
|
-
return { managed: false, skipped: true, reason: "unsupported_target_or_host" };
|
|
1578
|
-
}
|
|
1579
|
-
const env = {
|
|
1580
|
-
CLOUDFLARE_API_TOKEN: providedEnv?.CLOUDFLARE_API_TOKEN ?? process.env.TREESEED_CLOUDFLARE_API_TOKEN ?? ""
|
|
1581
|
-
};
|
|
1582
|
-
if (!env.CLOUDFLARE_API_TOKEN) {
|
|
1583
|
-
state.webCache.webHost = resolvePublicWebCacheTarget(deployConfig)?.host ?? null;
|
|
1584
|
-
state.webCache.contentHost = resolvePublicContentCacheTarget(deployConfig)?.host ?? null;
|
|
1585
|
-
state.webCache.rulesManaged = false;
|
|
1586
|
-
state.webCache.lastError = "CLOUDFLARE_API_TOKEN is required to manage Cloudflare Cache Rules.";
|
|
1587
|
-
return { managed: false, skipped: true, reason: "missing_api_token" };
|
|
1588
|
-
}
|
|
1589
|
-
const webTarget = resolvePublicWebCacheTarget(deployConfig);
|
|
1590
|
-
const contentTarget = resolvePublicContentCacheTarget(deployConfig);
|
|
1591
|
-
try {
|
|
1592
|
-
const results = [];
|
|
1593
|
-
if (webTarget?.host) {
|
|
1594
|
-
results.push(reconcileCloudflareCacheRulesForTarget("web", deployConfig, state, webTarget, env, { planOnly }));
|
|
1595
|
-
}
|
|
1596
|
-
if (contentTarget?.host) {
|
|
1597
|
-
results.push(reconcileCloudflareCacheRulesForTarget("content", deployConfig, state, contentTarget, env, { planOnly }));
|
|
1598
|
-
}
|
|
1599
|
-
state.webCache.rulesManaged = true;
|
|
1600
|
-
state.webCache.lastSyncedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1601
|
-
state.webCache.lastError = null;
|
|
1602
|
-
return { managed: true, results };
|
|
1603
|
-
} catch (error) {
|
|
1604
|
-
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
1605
|
-
if (/Authentication error|permission/i.test(message)) {
|
|
1606
|
-
state.webCache.rulesManaged = false;
|
|
1607
|
-
state.webCache.lastError = message;
|
|
1608
|
-
return { managed: false, skipped: true, reason: "auth_error", error: message };
|
|
1609
|
-
}
|
|
1610
|
-
throw error;
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
function purgeCloudflareCacheByUrls(urls, deployConfig, { env } = {}) {
|
|
1614
|
-
const uniqueUrls = [...new Set((urls ?? []).filter(Boolean))];
|
|
1615
|
-
if (uniqueUrls.length === 0) {
|
|
1616
|
-
return [];
|
|
1617
|
-
}
|
|
1618
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
1619
|
-
for (const urlValue of uniqueUrls) {
|
|
1620
|
-
const parsed = safeUrl(urlValue);
|
|
1621
|
-
if (!parsed) {
|
|
1622
|
-
continue;
|
|
1623
|
-
}
|
|
1624
|
-
const zoneId = resolveCloudflareZoneIdForHost(deployConfig, parsed.hostname, env);
|
|
1625
|
-
if (!zoneId) {
|
|
1626
|
-
continue;
|
|
1627
|
-
}
|
|
1628
|
-
const current = grouped.get(zoneId) ?? [];
|
|
1629
|
-
current.push(parsed.toString());
|
|
1630
|
-
grouped.set(zoneId, current);
|
|
1631
|
-
}
|
|
1632
|
-
return [...grouped.entries()].map(([zoneId, files]) => {
|
|
1633
|
-
const payload = cloudflareApiRequest(`/zones/${zoneId}/purge_cache`, {
|
|
1634
|
-
method: "POST",
|
|
1635
|
-
body: { files: [...new Set(files)] },
|
|
1636
|
-
env
|
|
1637
|
-
});
|
|
1638
|
-
const errors = Array.isArray(payload?.errors) ? payload.errors.map((entry) => entry?.message ?? JSON.stringify(entry)).filter(Boolean) : [];
|
|
1639
|
-
return {
|
|
1640
|
-
zoneId,
|
|
1641
|
-
count: [...new Set(files)].length,
|
|
1642
|
-
success: payload?.success === true,
|
|
1643
|
-
errors
|
|
1644
|
-
};
|
|
1645
|
-
});
|
|
1646
|
-
}
|
|
1647
|
-
function purgeCloudflareCacheEverythingByHosts(hosts, deployConfig, { env } = {}) {
|
|
1648
|
-
const zoneIds = [...new Set((hosts ?? []).map((host) => typeof host === "string" ? host.trim() : "").filter(Boolean).map((host) => resolveCloudflareZoneIdForHost(deployConfig, host, env)).filter(Boolean))];
|
|
1649
|
-
return zoneIds.map((zoneId) => {
|
|
1650
|
-
const payload = cloudflareApiRequest(`/zones/${zoneId}/purge_cache`, {
|
|
1651
|
-
method: "POST",
|
|
1652
|
-
body: { purge_everything: true },
|
|
1653
|
-
env
|
|
1654
|
-
});
|
|
1655
|
-
const errors = Array.isArray(payload?.errors) ? payload.errors.map((entry) => entry?.message ?? JSON.stringify(entry)).filter(Boolean) : [];
|
|
1656
|
-
return {
|
|
1657
|
-
zoneId,
|
|
1658
|
-
count: "everything",
|
|
1659
|
-
success: payload?.success === true,
|
|
1660
|
-
errors
|
|
1661
|
-
};
|
|
1662
|
-
});
|
|
1663
|
-
}
|
|
1664
|
-
function assertCloudflareCachePurgeSucceeded(results) {
|
|
1665
|
-
const failures = (results ?? []).filter((result) => result?.success !== true);
|
|
1666
|
-
if (failures.length === 0) return;
|
|
1667
|
-
const detail = failures.map((result) => {
|
|
1668
|
-
const errors = Array.isArray(result?.errors) && result.errors.length > 0 ? `: ${result.errors.join("; ")}` : "";
|
|
1669
|
-
return `${result?.zoneId ?? "unknown-zone"} (${result?.count ?? 0} urls)${errors}`;
|
|
1670
|
-
}).join(", ");
|
|
1671
|
-
throw new Error(`Cloudflare cache purge did not succeed for ${detail}.`);
|
|
1672
|
-
}
|
|
1673
|
-
function queueName(entry) {
|
|
1674
|
-
return entry?.queue_name ?? entry?.queueName ?? entry?.name ?? null;
|
|
1675
|
-
}
|
|
1676
|
-
function queueId(entry) {
|
|
1677
|
-
return entry?.queue_id ?? entry?.queueId ?? entry?.id ?? entry?.uuid ?? null;
|
|
1678
|
-
}
|
|
1679
|
-
function hasProvisionedCloudflareResources(state) {
|
|
1680
|
-
return Boolean(
|
|
1681
|
-
state?.pages?.projectName && state?.pages?.url && state?.d1Databases?.SITE_DATA_DB?.databaseId && state?.kvNamespaces?.FORM_GUARD_KV?.id && state?.content?.bucketName
|
|
1682
|
-
);
|
|
1683
|
-
}
|
|
1684
|
-
function absoluteUrlForPath(baseUrl, path) {
|
|
1685
|
-
const parsed = safeUrl(baseUrl);
|
|
1686
|
-
if (!parsed) {
|
|
1687
|
-
return null;
|
|
1688
|
-
}
|
|
1689
|
-
const normalizedPath = String(path ?? "").startsWith("/") ? String(path) : `/${String(path ?? "")}`;
|
|
1690
|
-
return new URL(normalizedPath, parsed).toString();
|
|
1691
|
-
}
|
|
1692
|
-
function resolveSourcePagePurgeUrls(deployConfig) {
|
|
1693
|
-
const webBaseUrl = deployConfig.surfaces?.web?.publicBaseUrl ?? deployConfig.siteUrl;
|
|
1694
|
-
const paths = resolveTreeseedWebCachePolicy(deployConfig).sourcePages.paths;
|
|
1695
|
-
return paths.map((path) => absoluteUrlForPath(webBaseUrl, path)).filter(Boolean);
|
|
1696
|
-
}
|
|
1697
|
-
function recordCachePurgeResult(targetState, results, error = null) {
|
|
1698
|
-
if (error) {
|
|
1699
|
-
targetState.lastError = error instanceof Error ? error.message : String(error);
|
|
1700
|
-
return;
|
|
1701
|
-
}
|
|
1702
|
-
targetState.lastPurgedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1703
|
-
targetState.purgeCount = Array.isArray(results) ? results.reduce((sum, result) => sum + (typeof result?.count === "number" ? result.count : 0), 0) : 0;
|
|
1704
|
-
targetState.lastError = null;
|
|
1705
|
-
}
|
|
1706
|
-
function resolveCloudflareCachePurgeEnv(options = {}) {
|
|
1707
|
-
const env = options.env ?? {};
|
|
1708
|
-
const token = env.TREESEED_CLOUDFLARE_API_TOKEN ?? env.CLOUDFLARE_API_TOKEN ?? process.env.TREESEED_CLOUDFLARE_API_TOKEN ?? process.env.CLOUDFLARE_API_TOKEN;
|
|
1709
|
-
return token ? { ...env, TREESEED_CLOUDFLARE_API_TOKEN: token, CLOUDFLARE_API_TOKEN: token } : null;
|
|
1710
|
-
}
|
|
1711
|
-
function purgeSourcePageCaches(tenantRoot, options = {}) {
|
|
1712
|
-
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
1713
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
1714
|
-
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
1715
|
-
const urls = resolveSourcePagePurgeUrls(deployConfig);
|
|
1716
|
-
const env = resolveCloudflareCachePurgeEnv(options);
|
|
1717
|
-
if ((options.planOnly ?? false) || urls.length === 0 || !env) {
|
|
1718
|
-
recordCachePurgeResult(state.webCache.deployPurge, urls.map((url) => ({ count: url ? 1 : 0 })));
|
|
1719
|
-
writeDeployState(tenantRoot, state, { target });
|
|
1720
|
-
return {
|
|
1721
|
-
skipped: true,
|
|
1722
|
-
reason: options.planOnly ? "plan" : urls.length === 0 ? "no_urls" : "missing_cloudflare_token",
|
|
1723
|
-
urls,
|
|
1724
|
-
results: []
|
|
1725
|
-
};
|
|
1726
|
-
}
|
|
1727
|
-
try {
|
|
1728
|
-
const results = purgeCloudflareCacheByUrls(urls, deployConfig, {
|
|
1729
|
-
env
|
|
1730
|
-
});
|
|
1731
|
-
const hosts = [...new Set(urls.map((url) => safeUrl(url)?.hostname).filter(Boolean))];
|
|
1732
|
-
const allResults = target.scope === "prod" ? purgeCloudflareCacheEverythingByHosts(hosts, deployConfig, { env }) : [];
|
|
1733
|
-
assertCloudflareCachePurgeSucceeded([...results, ...allResults]);
|
|
1734
|
-
recordCachePurgeResult(state.webCache.deployPurge, [...results, ...allResults]);
|
|
1735
|
-
writeDeployState(tenantRoot, state, { target });
|
|
1736
|
-
return { urls, results: [...results, ...allResults] };
|
|
1737
|
-
} catch (error) {
|
|
1738
|
-
recordCachePurgeResult(state.webCache.deployPurge, [], error);
|
|
1739
|
-
writeDeployState(tenantRoot, state, { target });
|
|
1740
|
-
throw error;
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
function purgePublishedContentCaches(tenantRoot, urls, options = {}) {
|
|
1744
|
-
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
1745
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
1746
|
-
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
1747
|
-
const env = resolveCloudflareCachePurgeEnv(options);
|
|
1748
|
-
if ((options.planOnly ?? false) || !urls?.length || !env) {
|
|
1749
|
-
recordCachePurgeResult(state.webCache.contentPurge, (urls ?? []).map((url) => ({ count: url ? 1 : 0 })));
|
|
1750
|
-
writeDeployState(tenantRoot, state, { target });
|
|
1751
|
-
return {
|
|
1752
|
-
skipped: true,
|
|
1753
|
-
reason: options.planOnly ? "plan" : !urls?.length ? "no_urls" : "missing_cloudflare_token",
|
|
1754
|
-
urls: urls ?? [],
|
|
1755
|
-
results: []
|
|
1756
|
-
};
|
|
1757
|
-
}
|
|
1758
|
-
try {
|
|
1759
|
-
const results = purgeCloudflareCacheByUrls(urls, deployConfig, {
|
|
1760
|
-
env
|
|
1761
|
-
});
|
|
1762
|
-
assertCloudflareCachePurgeSucceeded(results);
|
|
1763
|
-
recordCachePurgeResult(state.webCache.contentPurge, results);
|
|
1764
|
-
writeDeployState(tenantRoot, state, { target });
|
|
1765
|
-
return { urls, results };
|
|
1766
|
-
} catch (error) {
|
|
1767
|
-
recordCachePurgeResult(state.webCache.contentPurge, [], error);
|
|
1768
|
-
writeDeployState(tenantRoot, state, { target });
|
|
1769
|
-
throw error;
|
|
1770
|
-
}
|
|
1771
|
-
}
|
|
1772
|
-
function buildDestroySummary(deployConfig, state, target) {
|
|
1773
|
-
return buildProvisioningSummary(deployConfig, state, target);
|
|
1774
|
-
}
|
|
1775
|
-
function isPlaceholderAccountId(value) {
|
|
1776
|
-
return !value || value === "replace-with-cloudflare-account-id";
|
|
1777
|
-
}
|
|
1778
|
-
function resolveConfiguredCloudflareAccountId(deployConfig) {
|
|
1779
|
-
return envOrNull("CLOUDFLARE_ACCOUNT_ID") ?? deployConfig.cloudflare.accountId;
|
|
1780
|
-
}
|
|
1781
|
-
function normalizeConfiguredBaseUrl(value) {
|
|
1782
|
-
const raw = typeof value === "string" ? value.trim() : "";
|
|
1783
|
-
if (!raw) {
|
|
1784
|
-
return null;
|
|
1785
|
-
}
|
|
1786
|
-
return raw.replace(/\/+$/u, "");
|
|
1787
|
-
}
|
|
1788
|
-
function domainBaseUrl(domain) {
|
|
1789
|
-
const raw = typeof domain === "string" ? domain.trim() : "";
|
|
1790
|
-
if (!raw) {
|
|
1791
|
-
return null;
|
|
1792
|
-
}
|
|
1793
|
-
if (/^https?:\/\//iu.test(raw)) {
|
|
1794
|
-
return normalizeConfiguredBaseUrl(raw);
|
|
1795
|
-
}
|
|
1796
|
-
return `https://${raw.replace(/^\/+|\/+$/gu, "")}`;
|
|
1797
|
-
}
|
|
1798
|
-
function targetEnvironmentKey(target) {
|
|
1799
|
-
if (target?.kind === "persistent") {
|
|
1800
|
-
return target.scope;
|
|
1801
|
-
}
|
|
1802
|
-
return "staging";
|
|
1803
|
-
}
|
|
1804
|
-
function resolveConfiguredApiConnectionBaseUrl(deployConfig, target) {
|
|
1805
|
-
const scope = targetEnvironmentKey(target);
|
|
1806
|
-
if (scope === "local") {
|
|
1807
|
-
return normalizeConfiguredBaseUrl(deployConfig.connections?.api?.localBaseUrl) ?? normalizeConfiguredBaseUrl(deployConfig.connections?.api?.environments?.local?.baseUrl) ?? domainBaseUrl(deployConfig.connections?.api?.environments?.local?.domain);
|
|
1808
|
-
}
|
|
1809
|
-
return normalizeConfiguredBaseUrl(deployConfig.connections?.api?.environments?.[scope]?.baseUrl) ?? domainBaseUrl(deployConfig.connections?.api?.environments?.[scope]?.domain);
|
|
1810
|
-
}
|
|
1811
|
-
function resolveConfiguredMarketBaseUrl(deployConfig, target) {
|
|
1812
|
-
return resolveConfiguredApiConnectionBaseUrl(deployConfig, target) ?? envOrNull("TREESEED_API_BASE_URL") ?? deployConfig.runtime?.marketBaseUrl ?? deployConfig.hosting?.marketBaseUrl ?? envOrNull("TREESEED_CENTRAL_MARKET_API_BASE_URL") ?? DEFAULT_TREESEED_MARKET_BASE_URL;
|
|
1813
|
-
}
|
|
1814
|
-
function resolveConfiguredCentralMarketBaseUrl(deployConfig, target) {
|
|
1815
|
-
return resolveConfiguredApiConnectionBaseUrl(deployConfig, target) ?? envOrNull("TREESEED_CENTRAL_MARKET_API_BASE_URL") ?? DEFAULT_TREESEED_MARKET_BASE_URL;
|
|
1816
|
-
}
|
|
1817
|
-
function resolveConfiguredPagesProjectName(deployConfig) {
|
|
1818
|
-
return sharedDeploymentName(resolveTreeseedResourceIdentity(deployConfig, createPersistentDeployTarget("prod")));
|
|
1819
|
-
}
|
|
1820
|
-
function resolveConfiguredContentBucketBinding(deployConfig) {
|
|
1821
|
-
return envOrNull("TREESEED_CONTENT_BUCKET_BINDING") ?? deployConfig.cloudflare.r2?.binding ?? "TREESEED_CONTENT_BUCKET";
|
|
1822
|
-
}
|
|
1823
|
-
function resolveConfiguredContentBucketName(deployConfig) {
|
|
1824
|
-
return sharedDeploymentName(resolveTreeseedResourceIdentity(deployConfig, createPersistentDeployTarget("prod")), "content");
|
|
1825
|
-
}
|
|
1826
|
-
function resolveConfiguredContentPublicBaseUrl(deployConfig) {
|
|
1827
|
-
return envOrNull("TREESEED_CONTENT_PUBLIC_BASE_URL") ?? deployConfig.cloudflare.r2?.publicBaseUrl ?? "";
|
|
1828
|
-
}
|
|
1829
|
-
function missingTurnstileRequirements() {
|
|
1830
|
-
return [];
|
|
1831
|
-
}
|
|
1832
|
-
function missingContentRuntimeRequirements(deployConfig) {
|
|
1833
|
-
const issues = [];
|
|
1834
|
-
if (deployConfig.providers?.content?.runtime === "team_scoped_r2_overlay") {
|
|
1835
|
-
if (!resolveConfiguredContentBucketName(deployConfig)) {
|
|
1836
|
-
issues.push("Set TREESEED_CONTENT_BUCKET_NAME before deploying team-scoped hosted content.");
|
|
1837
|
-
}
|
|
1838
|
-
if (!envOrNull("TREESEED_EDITORIAL_PREVIEW_SECRET")) {
|
|
1839
|
-
issues.push("Set TREESEED_EDITORIAL_PREVIEW_SECRET before deploying team-scoped hosted content.");
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1842
|
-
return issues;
|
|
1843
|
-
}
|
|
1844
|
-
function collectMissingDeployInputs(tenantRoot) {
|
|
1845
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
1846
|
-
const missing = [];
|
|
1847
|
-
if (isPlaceholderAccountId(deployConfig.cloudflare.accountId)) {
|
|
1848
|
-
missing.push({
|
|
1849
|
-
key: "CLOUDFLARE_ACCOUNT_ID",
|
|
1850
|
-
label: "Cloudflare account ID",
|
|
1851
|
-
message: "Cloudflare account ID is missing. Set CLOUDFLARE_ACCOUNT_ID with treeseed config or provide it now."
|
|
1852
|
-
});
|
|
1853
|
-
}
|
|
1854
|
-
if (deployConfig.providers?.content?.runtime === "team_scoped_r2_overlay" && !envOrNull("TREESEED_EDITORIAL_PREVIEW_SECRET")) {
|
|
1855
|
-
missing.push({
|
|
1856
|
-
key: "TREESEED_EDITORIAL_PREVIEW_SECRET",
|
|
1857
|
-
label: "Editorial preview signing secret",
|
|
1858
|
-
message: "Editorial preview signing secret is missing for deploy."
|
|
1859
|
-
});
|
|
1860
|
-
}
|
|
1861
|
-
return missing;
|
|
1862
|
-
}
|
|
1863
|
-
async function promptForMissingDeployInputs(tenantRoot) {
|
|
1864
|
-
const missing = collectMissingDeployInputs(tenantRoot);
|
|
1865
|
-
if (!missing.length || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
1866
|
-
return { prompted: false, provided: [] };
|
|
1867
|
-
}
|
|
1868
|
-
const rl = createInterface({
|
|
1869
|
-
input: process.stdin,
|
|
1870
|
-
output: process.stdout
|
|
1871
|
-
});
|
|
1872
|
-
const provided = [];
|
|
1873
|
-
try {
|
|
1874
|
-
console.log("Treeseed deploy needs a few missing values before it can continue.");
|
|
1875
|
-
console.log("These values will be used for this deploy process only. Persist them in your env files or CI secrets afterward.");
|
|
1876
|
-
for (const item of missing) {
|
|
1877
|
-
console.log(`- ${item.message}`);
|
|
1878
|
-
const answer = (await rl.question(`${item.label}: `)).trim();
|
|
1879
|
-
if (!answer) {
|
|
1880
|
-
continue;
|
|
1881
|
-
}
|
|
1882
|
-
process.env[item.key] = answer;
|
|
1883
|
-
provided.push(item.key);
|
|
1884
|
-
}
|
|
1885
|
-
} finally {
|
|
1886
|
-
rl.close();
|
|
1887
|
-
}
|
|
1888
|
-
return { prompted: true, provided };
|
|
1889
|
-
}
|
|
1890
|
-
function validateDeployPrerequisites(tenantRoot, { requireRemote = true } = {}) {
|
|
1891
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
1892
|
-
const issues = [];
|
|
1893
|
-
if (isPlaceholderAccountId(deployConfig.cloudflare.accountId)) {
|
|
1894
|
-
issues.push(
|
|
1895
|
-
"Set CLOUDFLARE_ACCOUNT_ID with treeseed config or export it before deploying."
|
|
1896
|
-
);
|
|
1897
|
-
}
|
|
1898
|
-
if (requireRemote) {
|
|
1899
|
-
issues.push(...missingTurnstileRequirements());
|
|
1900
|
-
issues.push(...missingContentRuntimeRequirements(deployConfig));
|
|
1901
|
-
const result = runWrangler(["whoami"], {
|
|
1902
|
-
cwd: tenantRoot,
|
|
1903
|
-
allowFailure: true,
|
|
1904
|
-
capture: true
|
|
1905
|
-
});
|
|
1906
|
-
const output = `${result.stdout ?? ""}
|
|
1907
|
-
${result.stderr ?? ""}`;
|
|
1908
|
-
if (/You are not authenticated/i.test(output) || /wrangler login/i.test(output)) {
|
|
1909
|
-
issues.push("Authenticate Wrangler first with `wrangler login`.");
|
|
1910
|
-
}
|
|
1911
|
-
}
|
|
1912
|
-
if (issues.length > 0) {
|
|
1913
|
-
throw new Error(`Treeseed deploy prerequisites are not satisfied:
|
|
1914
|
-
- ${issues.join("\n- ")}`);
|
|
1915
|
-
}
|
|
1916
|
-
return deployConfig;
|
|
1917
|
-
}
|
|
1918
|
-
function validateDestroyPrerequisites(tenantRoot, { requireRemote = true } = {}) {
|
|
1919
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
1920
|
-
const issues = [];
|
|
1921
|
-
if (requireRemote && isPlaceholderAccountId(deployConfig.cloudflare.accountId)) {
|
|
1922
|
-
issues.push(
|
|
1923
|
-
"Set CLOUDFLARE_ACCOUNT_ID with treeseed config or export it before destroying infrastructure."
|
|
1924
|
-
);
|
|
1925
|
-
}
|
|
1926
|
-
if (requireRemote) {
|
|
1927
|
-
const result = runWrangler(["whoami"], {
|
|
1928
|
-
cwd: tenantRoot,
|
|
1929
|
-
allowFailure: true,
|
|
1930
|
-
capture: true
|
|
1931
|
-
});
|
|
1932
|
-
const output = `${result.stdout ?? ""}
|
|
1933
|
-
${result.stderr ?? ""}`;
|
|
1934
|
-
if (/You are not authenticated/i.test(output) || /wrangler login/i.test(output)) {
|
|
1935
|
-
issues.push("Authenticate Wrangler first with `wrangler login`.");
|
|
1936
|
-
}
|
|
1937
|
-
}
|
|
1938
|
-
if (issues.length > 0) {
|
|
1939
|
-
throw new Error(`Treeseed destroy prerequisites are not satisfied:
|
|
1940
|
-
- ${issues.join("\n- ")}`);
|
|
1941
|
-
}
|
|
1942
|
-
return deployConfig;
|
|
1943
|
-
}
|
|
1944
|
-
function resolveExistingKvIdByName(kvNamespaces, expectedName, fallbackId) {
|
|
1945
|
-
if (fallbackId && !isPlaceholderResourceId(fallbackId)) {
|
|
1946
|
-
return fallbackId;
|
|
1947
|
-
}
|
|
1948
|
-
return kvNamespaces.find((entry) => entry?.title === expectedName)?.id ?? null;
|
|
1949
|
-
}
|
|
1950
|
-
function resolveExistingTurnstileWidget(widgets, current) {
|
|
1951
|
-
if (!current?.name && !current?.sitekey) {
|
|
1952
|
-
return current;
|
|
1953
|
-
}
|
|
1954
|
-
const existing = widgets.find(
|
|
1955
|
-
(entry) => current.sitekey && entry?.sitekey === current.sitekey || current.name && entry?.name === current.name
|
|
1956
|
-
);
|
|
1957
|
-
if (!existing?.sitekey) {
|
|
1958
|
-
return current;
|
|
1959
|
-
}
|
|
1960
|
-
return {
|
|
1961
|
-
...current,
|
|
1962
|
-
sitekey: existing.sitekey,
|
|
1963
|
-
secret: existing.secret ?? current.secret ?? null,
|
|
1964
|
-
domains: Array.isArray(existing.domains) ? existing.domains : current.domains ?? [],
|
|
1965
|
-
mode: existing.mode ?? current.mode ?? "managed"
|
|
1966
|
-
};
|
|
1967
|
-
}
|
|
1968
|
-
function resolveExistingD1ByName(d1Databases, expectedName, current) {
|
|
1969
|
-
if (current?.databaseId && !isPlaceholderResourceId(current.databaseId)) {
|
|
1970
|
-
return current;
|
|
1971
|
-
}
|
|
1972
|
-
const existing = d1Databases.find((entry) => entry?.name === expectedName);
|
|
1973
|
-
if (!existing?.uuid) {
|
|
1974
|
-
return current;
|
|
1975
|
-
}
|
|
1976
|
-
return {
|
|
1977
|
-
...current,
|
|
1978
|
-
databaseId: existing.uuid,
|
|
1979
|
-
previewDatabaseId: existing.previewDatabaseUuid ?? existing.uuid
|
|
1980
|
-
};
|
|
1981
|
-
}
|
|
1982
|
-
function looksLikeMissingResource(output) {
|
|
1983
|
-
return /not found|does not exist|could(?: not|n't) find|couldnt find|already deleted|deleted widget|access a deleted/i.test(output);
|
|
1984
|
-
}
|
|
1985
|
-
function deleteKvNamespace(tenantRoot, namespaceId, { env, planOnly, preview = false }) {
|
|
1986
|
-
if (!namespaceId || isPlaceholderResourceId(namespaceId)) {
|
|
1987
|
-
return { status: "missing", id: namespaceId };
|
|
1988
|
-
}
|
|
1989
|
-
if (planOnly) {
|
|
1990
|
-
return { status: "planned", id: namespaceId, preview };
|
|
1991
|
-
}
|
|
1992
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1993
|
-
const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/storage/kv/namespaces/${encodeURIComponent(namespaceId)}` : null;
|
|
1994
|
-
const deleted = deleteCloudflareApiResource(path, { env, planOnly: false, name: namespaceId, type: "kv-namespace" });
|
|
1995
|
-
return { status: deleted.status, id: namespaceId, preview };
|
|
1996
|
-
}
|
|
1997
|
-
function deleteTurnstileWidget(sitekey, { env, planOnly, name = null }) {
|
|
1998
|
-
if (!sitekey || isPlaceholderResourceId(sitekey)) {
|
|
1999
|
-
return { status: "missing", sitekey, name };
|
|
2000
|
-
}
|
|
2001
|
-
if (planOnly) {
|
|
2002
|
-
return { status: "planned", sitekey, name };
|
|
2003
|
-
}
|
|
2004
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2005
|
-
const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/challenges/widgets/${encodeURIComponent(sitekey)}` : null;
|
|
2006
|
-
let deleted;
|
|
2007
|
-
try {
|
|
2008
|
-
deleted = deleteCloudflareApiResource(path, { env, planOnly: false, name: name ?? sitekey, type: "turnstile-widget" });
|
|
2009
|
-
} catch (error) {
|
|
2010
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
2011
|
-
throw new Error(`Cloudflare Turnstile widget deletion failed. Ensure the API token has Turnstile Sites Write permission: ${detail}`);
|
|
2012
|
-
}
|
|
2013
|
-
return { status: deleted.status, sitekey, name };
|
|
2014
|
-
}
|
|
2015
|
-
function deleteD1Database(tenantRoot, databaseName, { env, planOnly }) {
|
|
2016
|
-
if (!databaseName) {
|
|
2017
|
-
return { status: "missing", name: databaseName };
|
|
2018
|
-
}
|
|
2019
|
-
if (planOnly) {
|
|
2020
|
-
return { status: "planned", name: databaseName };
|
|
2021
|
-
}
|
|
2022
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2023
|
-
const database = accountId ? listD1Databases(tenantRoot, env).find((entry) => entry?.name === databaseName) : null;
|
|
2024
|
-
const databaseId = database?.uuid ?? database?.id ?? null;
|
|
2025
|
-
const path = accountId && databaseId ? `/accounts/${encodeURIComponent(accountId)}/d1/database/${encodeURIComponent(databaseId)}` : null;
|
|
2026
|
-
const deleted = deleteCloudflareApiResource(path, { env, planOnly: false, name: databaseName, type: "d1-database" });
|
|
2027
|
-
return { status: deleted.status, name: databaseName, id: databaseId };
|
|
2028
|
-
}
|
|
2029
|
-
function deleteWorker(tenantRoot, workerName, { env, planOnly, force = false }) {
|
|
2030
|
-
if (!workerName) {
|
|
2031
|
-
return { status: "missing", name: workerName };
|
|
2032
|
-
}
|
|
2033
|
-
if (planOnly) {
|
|
2034
|
-
return { status: "planned", name: workerName };
|
|
2035
|
-
}
|
|
2036
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2037
|
-
const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/workers/services/${encodeURIComponent(workerName)}` : null;
|
|
2038
|
-
const deleted = deleteCloudflareApiResource(path, { env, planOnly: false, name: workerName, type: "worker" });
|
|
2039
|
-
return { status: deleted.status, name: workerName };
|
|
2040
|
-
}
|
|
2041
|
-
function resourceOperation(provider, type, name, status, extra = {}) {
|
|
2042
|
-
return {
|
|
2043
|
-
provider,
|
|
2044
|
-
type,
|
|
2045
|
-
name: name ?? null,
|
|
2046
|
-
status,
|
|
2047
|
-
...extra
|
|
2048
|
-
};
|
|
2049
|
-
}
|
|
2050
|
-
function deleteCloudflareApiResource(path, { env, planOnly, name, type }) {
|
|
2051
|
-
if (!path) {
|
|
2052
|
-
return resourceOperation("cloudflare", type, name, "missing");
|
|
2053
|
-
}
|
|
2054
|
-
if (planOnly) {
|
|
2055
|
-
return resourceOperation("cloudflare", type, name, "planned", { path });
|
|
2056
|
-
}
|
|
2057
|
-
const result = cloudflareApiRequest(path, { method: "DELETE", env, allowFailure: true });
|
|
2058
|
-
if (result?.success === false && !looksLikeMissingResource(formatCloudflareErrors(result))) {
|
|
2059
|
-
throw new Error(formatCloudflareErrors(result) || `Failed to delete Cloudflare ${type} ${name}.`);
|
|
2060
|
-
}
|
|
2061
|
-
return resourceOperation("cloudflare", type, name, result?.success === false ? "missing" : "deleted", { path });
|
|
2062
|
-
}
|
|
2063
|
-
function formatCloudflareErrors(payload) {
|
|
2064
|
-
return Array.isArray(payload?.errors) ? payload.errors.map((entry) => entry?.message ?? JSON.stringify(entry)).filter(Boolean).join("; ") : "";
|
|
2065
|
-
}
|
|
2066
|
-
function deleteQueueByName(tenantRoot, queue, { env, planOnly }) {
|
|
2067
|
-
const name = queueName(queue) ?? queue?.name ?? null;
|
|
2068
|
-
let id = queueId(queue);
|
|
2069
|
-
if (!name) {
|
|
2070
|
-
return resourceOperation("cloudflare", "queue", name, "missing");
|
|
2071
|
-
}
|
|
2072
|
-
if (planOnly) {
|
|
2073
|
-
return resourceOperation("cloudflare", "queue", name, "planned", { id });
|
|
2074
|
-
}
|
|
2075
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2076
|
-
if (!id && accountId) {
|
|
2077
|
-
const live = listQueues(tenantRoot, env).find((entry) => queueName(entry) === name);
|
|
2078
|
-
id = queueId(live);
|
|
2079
|
-
}
|
|
2080
|
-
const path = id ? `/accounts/${encodeURIComponent(accountId)}/queues/${encodeURIComponent(id)}` : null;
|
|
2081
|
-
if (path) {
|
|
2082
|
-
const deleted = deleteCloudflareApiResource(path, { env, planOnly: false, name, type: "queue" });
|
|
2083
|
-
if (deleted.status === "deleted" || deleted.status === "missing") {
|
|
2084
|
-
return { ...deleted, id };
|
|
2085
|
-
}
|
|
2086
|
-
}
|
|
2087
|
-
if (accountId) {
|
|
2088
|
-
return resourceOperation("cloudflare", "queue", name, "missing", { id });
|
|
2089
|
-
}
|
|
2090
|
-
throw new Error(`Failed to delete queue ${name}: CLOUDFLARE_ACCOUNT_ID is not configured.`);
|
|
2091
|
-
}
|
|
2092
|
-
function deleteR2Bucket(tenantRoot, bucketName, { env, planOnly, deleteData }) {
|
|
2093
|
-
if (!bucketName) {
|
|
2094
|
-
return resourceOperation("cloudflare", "r2-bucket", bucketName, "missing");
|
|
2095
|
-
}
|
|
2096
|
-
if (!deleteData) {
|
|
2097
|
-
return resourceOperation("cloudflare", "r2-bucket", bucketName, "skipped", { reason: "data_preserved" });
|
|
2098
|
-
}
|
|
2099
|
-
if (planOnly) {
|
|
2100
|
-
return resourceOperation("cloudflare", "r2-bucket", bucketName, "planned");
|
|
2101
|
-
}
|
|
2102
|
-
const drained = drainR2Bucket(bucketName, { env });
|
|
2103
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2104
|
-
const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/r2/buckets/${encodeURIComponent(bucketName)}` : null;
|
|
2105
|
-
const deleted = deleteCloudflareApiResource(path, { env, planOnly: false, name: bucketName, type: "r2-bucket" });
|
|
2106
|
-
return resourceOperation("cloudflare", "r2-bucket", bucketName, deleted.status, drained);
|
|
2107
|
-
}
|
|
2108
|
-
function r2ObjectKey(entry) {
|
|
2109
|
-
return typeof entry?.key === "string" ? entry.key : typeof entry?.name === "string" ? entry.name : "";
|
|
2110
|
-
}
|
|
2111
|
-
function listR2Objects(bucketName, { env }) {
|
|
2112
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2113
|
-
if (!accountId || !bucketName) {
|
|
2114
|
-
return [];
|
|
2115
|
-
}
|
|
2116
|
-
const objects = [];
|
|
2117
|
-
let cursor = "";
|
|
2118
|
-
for (let page = 0; page < 20; page += 1) {
|
|
2119
|
-
const payload = cloudflareApiRequest(
|
|
2120
|
-
`/accounts/${encodeURIComponent(accountId)}/r2/buckets/${encodeURIComponent(bucketName)}/objects?per_page=200${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`,
|
|
2121
|
-
{ env, allowFailure: true }
|
|
2122
|
-
);
|
|
2123
|
-
if (payload?.success === false) {
|
|
2124
|
-
break;
|
|
2125
|
-
}
|
|
2126
|
-
const pageObjects = Array.isArray(payload?.result) ? payload.result : Array.isArray(payload?.result?.objects) ? payload.result.objects : [];
|
|
2127
|
-
objects.push(...pageObjects);
|
|
2128
|
-
const nextCursor = typeof payload?.result_info?.cursor === "string" ? payload.result_info.cursor : typeof payload?.result?.cursor === "string" ? payload.result.cursor : "";
|
|
2129
|
-
if (!nextCursor || nextCursor === cursor || pageObjects.length === 0) {
|
|
2130
|
-
break;
|
|
2131
|
-
}
|
|
2132
|
-
cursor = nextCursor;
|
|
2133
|
-
if (objects.length >= 200) {
|
|
2134
|
-
break;
|
|
2135
|
-
}
|
|
2136
|
-
}
|
|
2137
|
-
return objects;
|
|
2138
|
-
}
|
|
2139
|
-
function drainR2Bucket(bucketName, { env }) {
|
|
2140
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2141
|
-
if (!accountId || !bucketName) {
|
|
2142
|
-
return { objectsDeleted: 0, objectsMissing: 0, objectsDeferred: 0 };
|
|
2143
|
-
}
|
|
2144
|
-
let objectsDeleted = 0;
|
|
2145
|
-
let objectsMissing = 0;
|
|
2146
|
-
let objectsDeferred = 0;
|
|
2147
|
-
for (let batch = 0; batch < 100; batch += 1) {
|
|
2148
|
-
const objects = listR2Objects(bucketName, { env });
|
|
2149
|
-
if (objects.length === 0) {
|
|
2150
|
-
break;
|
|
2151
|
-
}
|
|
2152
|
-
const keys = objects.map((object) => r2ObjectKey(object)).filter(Boolean);
|
|
2153
|
-
const deleted = deleteR2ObjectsBatch(bucketName, keys, { env });
|
|
2154
|
-
objectsDeleted += deleted.objectsDeleted;
|
|
2155
|
-
objectsMissing += deleted.objectsMissing;
|
|
2156
|
-
objectsDeferred += deleted.objectsDeferred;
|
|
2157
|
-
const batchDeleted = deleted.objectsDeleted + deleted.objectsMissing;
|
|
2158
|
-
if (batchDeleted === 0) {
|
|
2159
|
-
if (deleted.objectsDeferred > 0) {
|
|
2160
|
-
sleepSync(3e3);
|
|
2161
|
-
continue;
|
|
2162
|
-
}
|
|
2163
|
-
break;
|
|
2164
|
-
}
|
|
2165
|
-
if (deleted.objectsDeferred > 0) {
|
|
2166
|
-
sleepSync(1500);
|
|
2167
|
-
}
|
|
2168
|
-
}
|
|
2169
|
-
return { objectsDeleted, objectsMissing, objectsDeferred };
|
|
2170
|
-
}
|
|
2171
|
-
function deleteR2ObjectsBatch(bucketName, keys, { env }) {
|
|
2172
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2173
|
-
const token = env?.CLOUDFLARE_API_TOKEN ?? process.env.TREESEED_CLOUDFLARE_API_TOKEN ?? "";
|
|
2174
|
-
const uniqueKeys = [...new Set((keys ?? []).filter(Boolean))];
|
|
2175
|
-
if (!accountId || !bucketName || uniqueKeys.length === 0) {
|
|
2176
|
-
return { objectsDeleted: 0, objectsMissing: 0, objectsDeferred: 0 };
|
|
2177
|
-
}
|
|
2178
|
-
const script = `
|
|
2179
|
-
const input = JSON.parse(await new Promise((resolve) => {
|
|
2180
|
-
let body = '';
|
|
2181
|
-
process.stdin.setEncoding('utf8');
|
|
2182
|
-
process.stdin.on('data', (chunk) => { body += chunk; });
|
|
2183
|
-
process.stdin.on('end', () => resolve(body || '{}'));
|
|
2184
|
-
}));
|
|
2185
|
-
let index = 0;
|
|
2186
|
-
let deleted = 0;
|
|
2187
|
-
let missing = 0;
|
|
2188
|
-
let deferred = 0;
|
|
2189
|
-
const failed = [];
|
|
2190
|
-
async function removeKey(key) {
|
|
2191
|
-
function encodeObjectKey(value) {
|
|
2192
|
-
return String(value).split('/').map((part) => encodeURIComponent(part)).join('/');
|
|
2193
|
-
}
|
|
2194
|
-
const url = 'https://api.cloudflare.com/client/v4/accounts/'
|
|
2195
|
-
+ encodeURIComponent(input.accountId)
|
|
2196
|
-
+ '/r2/buckets/'
|
|
2197
|
-
+ encodeURIComponent(input.bucketName)
|
|
2198
|
-
+ '/objects/'
|
|
2199
|
-
+ encodeObjectKey(key);
|
|
2200
|
-
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
2201
|
-
const controller = new AbortController();
|
|
2202
|
-
const timeout = setTimeout(() => controller.abort(), input.timeoutMs || 15000);
|
|
2203
|
-
try {
|
|
2204
|
-
const response = await fetch(url, {
|
|
2205
|
-
method: 'DELETE',
|
|
2206
|
-
headers: { authorization: 'Bearer ' + input.token },
|
|
2207
|
-
signal: controller.signal,
|
|
2208
|
-
});
|
|
2209
|
-
const text = await response.text();
|
|
2210
|
-
let payload = {};
|
|
2211
|
-
try { payload = text ? JSON.parse(text) : {}; } catch { payload = { errors: [{ message: text }] }; }
|
|
2212
|
-
if (response.ok && payload.success !== false) {
|
|
2213
|
-
deleted += 1;
|
|
2214
|
-
return;
|
|
2215
|
-
}
|
|
2216
|
-
const message = Array.isArray(payload.errors) ? payload.errors.map((entry) => entry?.message || JSON.stringify(entry)).join('; ') : text;
|
|
2217
|
-
if (/not found|does not exist|deleted|missing/i.test(message || '')) {
|
|
2218
|
-
missing += 1;
|
|
2219
|
-
return;
|
|
2220
|
-
}
|
|
2221
|
-
if (response.status === 429 || /rate limit|too many requests/i.test(message || '')) {
|
|
2222
|
-
await new Promise((resolve) => setTimeout(resolve, 750 * (attempt + 1)));
|
|
2223
|
-
continue;
|
|
2224
|
-
}
|
|
2225
|
-
failed.push({ key, message: message || \`delete failed with status \${response.status}\` });
|
|
2226
|
-
return;
|
|
2227
|
-
} catch (error) {
|
|
2228
|
-
if (attempt < 5 && /aborted|timed out|fetch failed|econnreset/i.test(error instanceof Error ? error.message : String(error))) {
|
|
2229
|
-
await new Promise((resolve) => setTimeout(resolve, 750 * (attempt + 1)));
|
|
2230
|
-
continue;
|
|
2231
|
-
}
|
|
2232
|
-
failed.push({ key, message: error instanceof Error ? error.message : String(error) });
|
|
2233
|
-
return;
|
|
2234
|
-
} finally {
|
|
2235
|
-
clearTimeout(timeout);
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
deferred += 1;
|
|
2239
|
-
}
|
|
2240
|
-
async function worker() {
|
|
2241
|
-
for (;;) {
|
|
2242
|
-
const current = index;
|
|
2243
|
-
index += 1;
|
|
2244
|
-
if (current >= input.keys.length) return;
|
|
2245
|
-
await removeKey(input.keys[current]);
|
|
2246
|
-
}
|
|
2247
|
-
}
|
|
2248
|
-
await Promise.all(Array.from({ length: Math.min(input.concurrency || 4, input.keys.length) }, () => worker()));
|
|
2249
|
-
process.stdout.write(JSON.stringify({ deleted, missing, deferred, failed }));
|
|
2250
|
-
`.trim();
|
|
2251
|
-
const result = spawnSync(process.execPath, ["--input-type=module", "-e", script], {
|
|
2252
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
2253
|
-
encoding: "utf8",
|
|
2254
|
-
env: { ...process.env, ...env ?? {} },
|
|
2255
|
-
input: JSON.stringify({
|
|
2256
|
-
accountId,
|
|
2257
|
-
bucketName,
|
|
2258
|
-
keys: uniqueKeys,
|
|
2259
|
-
token,
|
|
2260
|
-
concurrency: 4,
|
|
2261
|
-
timeoutMs: 12e3
|
|
2262
|
-
}),
|
|
2263
|
-
timeout: 12e4
|
|
2264
|
-
});
|
|
2265
|
-
if (result.status !== 0 || result.error) {
|
|
2266
|
-
throw new Error(result.stderr?.trim() || result.error?.message || `Failed to delete R2 object batch for ${bucketName}.`);
|
|
2267
|
-
}
|
|
2268
|
-
let parsed;
|
|
2269
|
-
try {
|
|
2270
|
-
parsed = JSON.parse(result.stdout || "{}");
|
|
2271
|
-
} catch {
|
|
2272
|
-
throw new Error(`R2 object batch delete returned invalid JSON for ${bucketName}.`);
|
|
2273
|
-
}
|
|
2274
|
-
if (Array.isArray(parsed.failed) && parsed.failed.length > 0) {
|
|
2275
|
-
const first = parsed.failed[0];
|
|
2276
|
-
throw new Error(`Failed to delete ${parsed.failed.length} R2 objects from ${bucketName}: ${first?.message ?? first?.key ?? "unknown error"}`);
|
|
2277
|
-
}
|
|
2278
|
-
return {
|
|
2279
|
-
objectsDeleted: Number(parsed.deleted) || 0,
|
|
2280
|
-
objectsMissing: Number(parsed.missing) || 0,
|
|
2281
|
-
objectsDeferred: Number(parsed.deferred) || 0
|
|
2282
|
-
};
|
|
2283
|
-
}
|
|
2284
|
-
function deleteD1DatabaseForDestroy(tenantRoot, databaseName, { env, planOnly, deleteData }) {
|
|
2285
|
-
if (!deleteData) {
|
|
2286
|
-
return resourceOperation("cloudflare", "d1-database", databaseName, "skipped", { reason: "data_preserved" });
|
|
2287
|
-
}
|
|
2288
|
-
const result = deleteD1Database(tenantRoot, databaseName, { env, planOnly });
|
|
2289
|
-
return resourceOperation("cloudflare", "d1-database", databaseName, result.status, result);
|
|
2290
|
-
}
|
|
2291
|
-
function pagesDomainName(domain) {
|
|
2292
|
-
return typeof domain?.name === "string" ? domain.name : typeof domain?.domain === "string" ? domain.domain : typeof domain?.hostname === "string" ? domain.hostname : "";
|
|
2293
|
-
}
|
|
2294
|
-
function listPagesCustomDomains(projectName, { env }) {
|
|
2295
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2296
|
-
if (!projectName || !accountId) {
|
|
2297
|
-
return [];
|
|
2298
|
-
}
|
|
2299
|
-
const domains = [];
|
|
2300
|
-
let page = 1;
|
|
2301
|
-
let totalPages = 1;
|
|
2302
|
-
while (page <= totalPages && page <= 50) {
|
|
2303
|
-
const payload = cloudflareApiRequest(
|
|
2304
|
-
`/accounts/${encodeURIComponent(accountId)}/pages/projects/${encodeURIComponent(projectName)}/domains?per_page=100&page=${page}`,
|
|
2305
|
-
{ env, allowFailure: true }
|
|
2306
|
-
);
|
|
2307
|
-
if (payload?.success === false) {
|
|
2308
|
-
break;
|
|
2309
|
-
}
|
|
2310
|
-
if (Array.isArray(payload?.result)) {
|
|
2311
|
-
domains.push(...payload.result);
|
|
2312
|
-
}
|
|
2313
|
-
const reportedTotal = Number(payload?.result_info?.total_pages);
|
|
2314
|
-
totalPages = Number.isFinite(reportedTotal) && reportedTotal > 0 ? reportedTotal : page;
|
|
2315
|
-
page += 1;
|
|
2316
|
-
}
|
|
2317
|
-
return domains;
|
|
2318
|
-
}
|
|
2319
|
-
function listPagesCustomDomainsWithWrangler(tenantRoot, projectName, { env }) {
|
|
2320
|
-
const result = runWrangler(["pages", "project", "list", "--json"], {
|
|
2321
|
-
cwd: tenantRoot,
|
|
2322
|
-
allowFailure: true,
|
|
2323
|
-
capture: true,
|
|
2324
|
-
env
|
|
2325
|
-
});
|
|
2326
|
-
if (result.status !== 0) {
|
|
2327
|
-
return [];
|
|
2328
|
-
}
|
|
2329
|
-
try {
|
|
2330
|
-
const projects = JSON.parse(result.stdout || "[]");
|
|
2331
|
-
const project = (Array.isArray(projects) ? projects : []).find((entry) => entry?.name === projectName || entry?.projectName === projectName || entry?.["Project Name"] === projectName);
|
|
2332
|
-
const domains = typeof project?.["Project Domains"] === "string" ? project["Project Domains"] : typeof project?.domains === "string" ? project.domains : "";
|
|
2333
|
-
return domains.split(",").map((entry) => entry.trim()).filter((entry) => entry && !entry.endsWith(".pages.dev"));
|
|
2334
|
-
} catch {
|
|
2335
|
-
return [];
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
function deletePagesCustomDomains(tenantRoot, projectName, knownNames, { env, planOnly, knownOnly = false }) {
|
|
2339
|
-
if (!projectName) {
|
|
2340
|
-
return [resourceOperation("cloudflare", "pages-custom-domain", projectName, "missing")];
|
|
2341
|
-
}
|
|
2342
|
-
const desiredNames = [...new Set((knownNames ?? []).filter(Boolean))];
|
|
2343
|
-
if (planOnly) {
|
|
2344
|
-
return desiredNames.length > 0 ? desiredNames.map((name) => resourceOperation("cloudflare", "pages-custom-domain", name, "planned", { projectName, knownOnly })) : [resourceOperation("cloudflare", "pages-custom-domain", projectName, knownOnly ? "skipped" : "planned", { reason: knownOnly ? "no_target_scoped_domain" : "project_delete_prerequisite" })];
|
|
2345
|
-
}
|
|
2346
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2347
|
-
if (!accountId) {
|
|
2348
|
-
return desiredNames.length > 0 ? desiredNames.map((name) => resourceOperation("cloudflare", "pages-custom-domain", name, "blocked", { projectName, reason: "missing_cloudflare_account_id" })) : [resourceOperation("cloudflare", "pages-custom-domain", projectName, "blocked", { reason: "missing_cloudflare_account_id" })];
|
|
2349
|
-
}
|
|
2350
|
-
const listedNames = knownOnly ? [] : listPagesCustomDomains(projectName, { env }).map(pagesDomainName).filter(Boolean);
|
|
2351
|
-
const wranglerNames = knownOnly ? [] : listPagesCustomDomainsWithWrangler(tenantRoot, projectName, { env });
|
|
2352
|
-
const domainNames = [.../* @__PURE__ */ new Set([...desiredNames, ...listedNames, ...wranglerNames])];
|
|
2353
|
-
if (domainNames.length === 0) {
|
|
2354
|
-
return [resourceOperation("cloudflare", "pages-custom-domain", projectName, "missing", { projectName })];
|
|
2355
|
-
}
|
|
2356
|
-
return domainNames.map((name) => deleteCloudflareApiResource(
|
|
2357
|
-
`/accounts/${encodeURIComponent(accountId)}/pages/projects/${encodeURIComponent(projectName)}/domains/${encodeURIComponent(name)}`,
|
|
2358
|
-
{ env, planOnly: false, name, type: "pages-custom-domain" }
|
|
2359
|
-
));
|
|
2360
|
-
}
|
|
2361
|
-
function normalizePagesDeploymentId(deployment) {
|
|
2362
|
-
return typeof deployment?.id === "string" ? deployment.id : typeof deployment?.Id === "string" ? deployment.Id : "";
|
|
2363
|
-
}
|
|
2364
|
-
function normalizePagesDeployments(value) {
|
|
2365
|
-
return (Array.isArray(value) ? value : Array.isArray(value?.result) ? value.result : []).filter((entry) => normalizePagesDeploymentId(entry));
|
|
2366
|
-
}
|
|
2367
|
-
function pagesDeploymentEnvironments(environment = "all") {
|
|
2368
|
-
return environment === "preview" ? ["preview"] : environment === "production" ? ["production"] : ["preview", "production"];
|
|
2369
|
-
}
|
|
2370
|
-
function listPagesDeploymentsWithApi(projectName, { env, environment = "all" }) {
|
|
2371
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2372
|
-
if (!projectName || !accountId) {
|
|
2373
|
-
return [];
|
|
2374
|
-
}
|
|
2375
|
-
const deployments = [];
|
|
2376
|
-
for (const pagesEnvironment of pagesDeploymentEnvironments(environment)) {
|
|
2377
|
-
let page = 1;
|
|
2378
|
-
let totalPages = 1;
|
|
2379
|
-
while (page <= totalPages && page <= 50) {
|
|
2380
|
-
const payload = cloudflareApiRequest(
|
|
2381
|
-
`/accounts/${encodeURIComponent(accountId)}/pages/projects/${encodeURIComponent(projectName)}/deployments?per_page=100&page=${page}&env=${pagesEnvironment}`,
|
|
2382
|
-
{ env, allowFailure: true }
|
|
2383
|
-
);
|
|
2384
|
-
if (payload?.success === false) {
|
|
2385
|
-
break;
|
|
2386
|
-
}
|
|
2387
|
-
deployments.push(...normalizePagesDeployments(payload));
|
|
2388
|
-
const reportedTotal = Number(payload?.result_info?.total_pages);
|
|
2389
|
-
totalPages = Number.isFinite(reportedTotal) && reportedTotal > 0 ? reportedTotal : page;
|
|
2390
|
-
page += 1;
|
|
2391
|
-
}
|
|
2392
|
-
}
|
|
2393
|
-
return deployments;
|
|
2394
|
-
}
|
|
2395
|
-
function listPagesDeployments(tenantRoot, projectName, { env, environment = "all" }) {
|
|
2396
|
-
const deployments = [];
|
|
2397
|
-
for (const pagesEnvironment of pagesDeploymentEnvironments(environment)) {
|
|
2398
|
-
const result = runWrangler(["pages", "deployment", "list", "--project-name", projectName, "--environment", pagesEnvironment, "--json"], {
|
|
2399
|
-
cwd: tenantRoot,
|
|
2400
|
-
allowFailure: true,
|
|
2401
|
-
capture: true,
|
|
2402
|
-
env
|
|
2403
|
-
});
|
|
2404
|
-
if (result.status !== 0) {
|
|
2405
|
-
continue;
|
|
2406
|
-
}
|
|
2407
|
-
try {
|
|
2408
|
-
deployments.push(...normalizePagesDeployments(JSON.parse(result.stdout || "[]")));
|
|
2409
|
-
} catch {
|
|
2410
|
-
}
|
|
2411
|
-
}
|
|
2412
|
-
if (deployments.length > 0) {
|
|
2413
|
-
const byId = new Map(deployments.map((deployment) => [normalizePagesDeploymentId(deployment), deployment]));
|
|
2414
|
-
return [...byId.values()];
|
|
2415
|
-
}
|
|
2416
|
-
return listPagesDeploymentsWithApi(projectName, { env, environment });
|
|
2417
|
-
}
|
|
2418
|
-
function deletePagesDeployments(tenantRoot, projectName, { env, planOnly, environment = "all" }) {
|
|
2419
|
-
if (!projectName) {
|
|
2420
|
-
return resourceOperation("cloudflare", "pages-deployments", projectName, "missing");
|
|
2421
|
-
}
|
|
2422
|
-
if (planOnly) {
|
|
2423
|
-
return resourceOperation("cloudflare", "pages-deployments", projectName, "planned", { reason: "project_delete_prerequisite", environment });
|
|
2424
|
-
}
|
|
2425
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2426
|
-
if (!accountId) {
|
|
2427
|
-
return resourceOperation("cloudflare", "pages-deployments", projectName, "blocked", { reason: "missing_cloudflare_account_id" });
|
|
2428
|
-
}
|
|
2429
|
-
let deleted = 0;
|
|
2430
|
-
let skipped = 0;
|
|
2431
|
-
let total = 0;
|
|
2432
|
-
for (let batch = 0; batch < 100; batch += 1) {
|
|
2433
|
-
const deployments = listPagesDeployments(tenantRoot, projectName, { env, environment });
|
|
2434
|
-
if (deployments.length === 0) {
|
|
2435
|
-
return resourceOperation("cloudflare", "pages-deployments", projectName, deleted > 0 ? "deleted" : "missing", {
|
|
2436
|
-
deleted,
|
|
2437
|
-
skipped,
|
|
2438
|
-
total
|
|
2439
|
-
});
|
|
2440
|
-
}
|
|
2441
|
-
total += deployments.length;
|
|
2442
|
-
let batchDeleted = 0;
|
|
2443
|
-
let batchSkipped = 0;
|
|
2444
|
-
for (const deployment of deployments) {
|
|
2445
|
-
const deploymentId = normalizePagesDeploymentId(deployment);
|
|
2446
|
-
if (!deploymentId) {
|
|
2447
|
-
continue;
|
|
2448
|
-
}
|
|
2449
|
-
const result = cloudflareApiRequest(
|
|
2450
|
-
`/accounts/${encodeURIComponent(accountId)}/pages/projects/${encodeURIComponent(projectName)}/deployments/${encodeURIComponent(deploymentId)}`,
|
|
2451
|
-
{ method: "DELETE", env, allowFailure: true }
|
|
2452
|
-
);
|
|
2453
|
-
if (result?.success === false) {
|
|
2454
|
-
const message = formatCloudflareErrors(result);
|
|
2455
|
-
if (/active production deployment|production deployment|deployment is aliased|aliased deployment/iu.test(message)) {
|
|
2456
|
-
skipped += 1;
|
|
2457
|
-
batchSkipped += 1;
|
|
2458
|
-
continue;
|
|
2459
|
-
}
|
|
2460
|
-
if (looksLikeMissingResource(message)) {
|
|
2461
|
-
continue;
|
|
2462
|
-
}
|
|
2463
|
-
throw new Error(message || `Failed to delete Pages deployment ${deploymentId}.`);
|
|
2464
|
-
}
|
|
2465
|
-
deleted += 1;
|
|
2466
|
-
batchDeleted += 1;
|
|
2467
|
-
}
|
|
2468
|
-
if (batchDeleted === 0 && batchSkipped >= deployments.length) {
|
|
2469
|
-
break;
|
|
2470
|
-
}
|
|
2471
|
-
}
|
|
2472
|
-
return resourceOperation("cloudflare", "pages-deployments", projectName, deleted > 0 ? "deleted" : "skipped", {
|
|
2473
|
-
deleted,
|
|
2474
|
-
skipped,
|
|
2475
|
-
total
|
|
2476
|
-
});
|
|
2477
|
-
}
|
|
2478
|
-
function deletePagesProject(projectName, { env, planOnly }) {
|
|
2479
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2480
|
-
if (!projectName || !accountId) {
|
|
2481
|
-
return resourceOperation("cloudflare", "pages-project", projectName, "missing");
|
|
2482
|
-
}
|
|
2483
|
-
return deleteCloudflareApiResource(
|
|
2484
|
-
`/accounts/${encodeURIComponent(accountId)}/pages/projects/${encodeURIComponent(projectName)}`,
|
|
2485
|
-
{ env, planOnly, name: projectName, type: "pages-project" }
|
|
2486
|
-
);
|
|
2487
|
-
}
|
|
2488
|
-
function listDnsRecordsForName(zoneId, name, env) {
|
|
2489
|
-
if (!zoneId || !name) {
|
|
2490
|
-
return [];
|
|
2491
|
-
}
|
|
2492
|
-
const result = cloudflareApiRequest(`/zones/${encodeURIComponent(zoneId)}/dns_records?name=${encodeURIComponent(name)}&per_page=100`, { env, allowFailure: true });
|
|
2493
|
-
return Array.isArray(result?.result) ? result.result : [];
|
|
2494
|
-
}
|
|
2495
|
-
function deleteDnsRecordsForName(deployConfig, name, { env, planOnly }) {
|
|
2496
|
-
if (!name) {
|
|
2497
|
-
return [resourceOperation("cloudflare", "dns-record", name, "missing")];
|
|
2498
|
-
}
|
|
2499
|
-
if (planOnly) {
|
|
2500
|
-
return [resourceOperation("cloudflare", "dns-record", name, "planned")];
|
|
2501
|
-
}
|
|
2502
|
-
const zoneId = resolveCloudflareZoneIdForHost(deployConfig, name, env);
|
|
2503
|
-
if (!zoneId) {
|
|
2504
|
-
return [resourceOperation("cloudflare", "dns-record", name, "blocked", { reason: "zone_unresolved" })];
|
|
2505
|
-
}
|
|
2506
|
-
const records = listDnsRecordsForName(zoneId, name, env);
|
|
2507
|
-
if (records.length === 0) {
|
|
2508
|
-
return [resourceOperation("cloudflare", "dns-record", name, "missing", { zoneId })];
|
|
2509
|
-
}
|
|
2510
|
-
return records.map((record) => {
|
|
2511
|
-
const recordName = record?.name ?? name;
|
|
2512
|
-
return deleteCloudflareApiResource(
|
|
2513
|
-
`/zones/${encodeURIComponent(zoneId)}/dns_records/${encodeURIComponent(record.id)}`,
|
|
2514
|
-
{ env, planOnly: false, name: recordName, type: "dns-record" }
|
|
2515
|
-
);
|
|
2516
|
-
});
|
|
2517
|
-
}
|
|
2518
|
-
function deleteTreeseedCacheRules(deployConfig, state, { env, planOnly }) {
|
|
2519
|
-
const targets = [
|
|
2520
|
-
{ role: "web", zoneId: state.webCache?.webZoneId, host: state.webCache?.webHost },
|
|
2521
|
-
{ role: "content", zoneId: state.webCache?.contentZoneId, host: state.webCache?.contentHost }
|
|
2522
|
-
].filter((entry) => entry.host || entry.zoneId);
|
|
2523
|
-
if (targets.length === 0) {
|
|
2524
|
-
return [resourceOperation("cloudflare", "cache-rules", null, "missing")];
|
|
2525
|
-
}
|
|
2526
|
-
return targets.map((target) => {
|
|
2527
|
-
const zoneId = target.zoneId ?? resolveCloudflareZoneIdForHost(deployConfig, target.host, env);
|
|
2528
|
-
if (!zoneId) {
|
|
2529
|
-
return resourceOperation("cloudflare", "cache-rules", target.host, "blocked", { reason: "zone_unresolved" });
|
|
2530
|
-
}
|
|
2531
|
-
if (planOnly) {
|
|
2532
|
-
return resourceOperation("cloudflare", "cache-rules", target.host, "planned", { zoneId });
|
|
2533
|
-
}
|
|
2534
|
-
const rulesets = listCloudflareZoneRulesets(zoneId, env);
|
|
2535
|
-
const ruleset = rulesets.find((entry) => entry?.phase === "http_request_cache_settings") ?? null;
|
|
2536
|
-
const rules = Array.isArray(ruleset?.rules) ? ruleset.rules : [];
|
|
2537
|
-
const kept = rules.filter((rule) => typeof rule?.description !== "string" || !rule.description.startsWith("treeseed-managed:"));
|
|
2538
|
-
if (!ruleset || kept.length === rules.length) {
|
|
2539
|
-
return resourceOperation("cloudflare", "cache-rules", target.host, "missing", { zoneId });
|
|
2540
|
-
}
|
|
2541
|
-
cloudflareApiRequest(`/zones/${encodeURIComponent(zoneId)}/rulesets/${encodeURIComponent(ruleset.id)}`, {
|
|
2542
|
-
method: "PUT",
|
|
2543
|
-
body: { rules: kept },
|
|
2544
|
-
env
|
|
2545
|
-
});
|
|
2546
|
-
return resourceOperation("cloudflare", "cache-rules", target.host, "deleted", { zoneId, rulesetId: ruleset.id, removed: rules.length - kept.length });
|
|
2547
|
-
});
|
|
2548
|
-
}
|
|
2549
|
-
function configuredRailwayDestroyTargets(tenantRoot, deployConfig, scope) {
|
|
2550
|
-
const normalizedScope = scope === "prod" ? "prod" : scope === "staging" ? "staging" : "local";
|
|
2551
|
-
if (normalizedScope === "local" || deployConfig.runtime?.mode !== "treeseed_managed") {
|
|
2552
|
-
return [];
|
|
2553
|
-
}
|
|
2554
|
-
let identity;
|
|
2555
|
-
try {
|
|
2556
|
-
identity = resolveTreeseedResourceIdentity(deployConfig, createPersistentDeployTarget(normalizedScope));
|
|
2557
|
-
} catch {
|
|
2558
|
-
identity = { deploymentKey: deployConfig.slug ?? deployConfig.name ?? "treeseed" };
|
|
2559
|
-
}
|
|
2560
|
-
const services = [];
|
|
2561
|
-
for (const serviceKey of ["api", "operationsRunner"]) {
|
|
2562
|
-
const service = deployConfig.services?.[serviceKey];
|
|
2563
|
-
if (!service || service.enabled === false || (service.provider ?? "railway") !== "railway") {
|
|
2564
|
-
continue;
|
|
2565
|
-
}
|
|
2566
|
-
const baseServiceName = service.environments?.[normalizedScope]?.serviceName ?? service.railway?.serviceName ?? `${identity.deploymentKey}-${serviceKey === "operationsRunner" ? "operations-runner" : serviceKey}`;
|
|
2567
|
-
const runnerPool = serviceKey === "operationsRunner" && service.railway?.runnerPool && typeof service.railway.runnerPool === "object" ? service.railway.runnerPool : null;
|
|
2568
|
-
const count = serviceKey === "operationsRunner" ? Math.max(1, Number.parseInt(String(runnerPool?.bootstrapCount ?? 1), 10) || 1) : 1;
|
|
2569
|
-
for (let index = 1; index <= count; index += 1) {
|
|
2570
|
-
const serviceName = serviceKey === "operationsRunner" ? `${String(baseServiceName).replace(/-\d+$/u, "").replace(/-\d{2}$/u, "")}-${String(index).padStart(2, "0")}` : baseServiceName;
|
|
2571
|
-
services.push({
|
|
2572
|
-
key: serviceKey,
|
|
2573
|
-
projectName: service.railway?.projectName ?? identity.deploymentKey,
|
|
2574
|
-
serviceName,
|
|
2575
|
-
railwayEnvironment: normalizeRailwayEnvironmentName(service.environments?.[normalizedScope]?.railwayEnvironment ?? normalizedScope),
|
|
2576
|
-
domain: service.environments?.[normalizedScope]?.domain ?? null,
|
|
2577
|
-
volumeMountPath: serviceKey === "operationsRunner" ? service.railway?.volumeMountPath ?? runnerPool?.volumeMountPath ?? "/data" : null
|
|
2578
|
-
});
|
|
2579
|
-
}
|
|
2580
|
-
}
|
|
2581
|
-
const treeseedDatabase = deployConfig.services?.treeseedDatabase;
|
|
2582
|
-
if (treeseedDatabase?.enabled !== false && treeseedDatabase?.provider === "railway" && treeseedDatabase?.railway?.resourceType === "postgres") {
|
|
2583
|
-
const baseName = typeof treeseedDatabase.railway?.serviceName === "string" && treeseedDatabase.railway.serviceName.trim() ? treeseedDatabase.railway.serviceName.trim() : `${deployConfig.slug ?? "treeseed-market"}-postgres`;
|
|
2584
|
-
services.push({
|
|
2585
|
-
key: "treeseedDatabase",
|
|
2586
|
-
projectName: deployConfig.services?.api?.railway?.projectName ?? identity.deploymentKey,
|
|
2587
|
-
serviceName: baseName.replace(/-(staging|prod|production)$/u, ""),
|
|
2588
|
-
railwayEnvironment: normalizeRailwayEnvironmentName(normalizedScope),
|
|
2589
|
-
domain: null,
|
|
2590
|
-
volumeMountPath: null,
|
|
2591
|
-
dataStore: true
|
|
2592
|
-
});
|
|
2593
|
-
}
|
|
2594
|
-
return services;
|
|
2595
|
-
}
|
|
2596
|
-
async function destroyRailwayResources(tenantRoot, deployConfig, target, { planOnly = false, deleteData = false, env = process.env } = {}) {
|
|
2597
|
-
const scope = target.kind === "persistent" ? target.scope : target.branchName;
|
|
2598
|
-
const services = configuredRailwayDestroyTargets(tenantRoot, deployConfig, scope);
|
|
2599
|
-
if (services.length === 0) {
|
|
2600
|
-
return { operations: [resourceOperation("railway", "environment", scope, "skipped", { reason: "not_applicable" })] };
|
|
2601
|
-
}
|
|
2602
|
-
const operations = [];
|
|
2603
|
-
if (!resolveRailwayApiToken(env)) {
|
|
2604
|
-
return {
|
|
2605
|
-
operations: services.map((service) => resourceOperation("railway", "service", service.serviceName, "blocked", { reason: "missing_railway_api_token" }))
|
|
2606
|
-
};
|
|
2607
|
-
}
|
|
2608
|
-
const workspace = await resolveRailwayWorkspaceContext({ env, workspace: resolveRailwayWorkspace(env) });
|
|
2609
|
-
const projects = await listRailwayProjects({ env, workspaceId: workspace.id });
|
|
2610
|
-
const projectNames = [...new Set(services.map((service) => service.projectName).filter(Boolean))];
|
|
2611
|
-
for (const projectName of projectNames) {
|
|
2612
|
-
const project = projects.find((entry) => !entry.deletedAt && (entry.name === projectName || entry.id === projectName)) ?? null;
|
|
2613
|
-
if (!project) {
|
|
2614
|
-
operations.push(resourceOperation("railway", "project", projectName, "missing"));
|
|
2615
|
-
continue;
|
|
2616
|
-
}
|
|
2617
|
-
const serviceTargets = services.filter((service) => service.projectName === projectName);
|
|
2618
|
-
const environmentName = normalizeRailwayEnvironmentName(scope);
|
|
2619
|
-
const environment = project.environments.find((entry) => entry.name === environmentName || entry.id === environmentName) ?? null;
|
|
2620
|
-
if (!environment) {
|
|
2621
|
-
operations.push(resourceOperation("railway", "environment", environmentName, "missing", { projectId: project.id }));
|
|
2622
|
-
}
|
|
2623
|
-
for (const service of serviceTargets) {
|
|
2624
|
-
const railwayService = project.services.find((entry) => entry.name === service.serviceName || entry.id === service.serviceName) ?? null;
|
|
2625
|
-
const shouldDeleteData = service.dataStore ? deleteData : true;
|
|
2626
|
-
operations.push(resourceOperation("railway", service.dataStore ? "postgres-service" : "service", service.serviceName, railwayService ? planOnly ? "planned" : "planned" : "missing", {
|
|
2627
|
-
projectId: project.id,
|
|
2628
|
-
serviceId: railwayService?.id ?? null,
|
|
2629
|
-
...service.dataStore && !shouldDeleteData ? { status: "skipped", reason: "data_preserved" } : {}
|
|
2630
|
-
}));
|
|
2631
|
-
if (!railwayService || !environment) {
|
|
2632
|
-
continue;
|
|
2633
|
-
}
|
|
2634
|
-
if (shouldDeleteData) {
|
|
2635
|
-
const variables = await listRailwayVariables({
|
|
2636
|
-
projectId: project.id,
|
|
2637
|
-
environmentId: environment.id,
|
|
2638
|
-
serviceId: railwayService.id,
|
|
2639
|
-
env
|
|
2640
|
-
});
|
|
2641
|
-
for (const variableName of Object.keys(variables).sort()) {
|
|
2642
|
-
operations.push(resourceOperation("railway", "variable", `${service.serviceName}:${variableName}`, planOnly ? "planned" : "deleted", {
|
|
2643
|
-
projectId: project.id,
|
|
2644
|
-
serviceId: railwayService.id,
|
|
2645
|
-
environmentId: environment.id,
|
|
2646
|
-
reason: scope === "prod" && deleteData ? "project_delete" : "environment_delete"
|
|
2647
|
-
}));
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
const instance = await getRailwayServiceInstance({ serviceId: railwayService.id, environmentId: environment.id, env });
|
|
2651
|
-
if (instance.cronSchedule) {
|
|
2652
|
-
operations.push(resourceOperation("railway", "schedule", `${service.serviceName}:${instance.cronSchedule}`, planOnly ? "planned" : "deleted", {
|
|
2653
|
-
projectId: project.id,
|
|
2654
|
-
serviceId: railwayService.id,
|
|
2655
|
-
environmentId: environment.id,
|
|
2656
|
-
reason: scope === "prod" && deleteData ? "project_delete" : "environment_delete"
|
|
2657
|
-
}));
|
|
2658
|
-
}
|
|
2659
|
-
if (!service.dataStore && service.domain) {
|
|
2660
|
-
const domains = await listRailwayCustomDomains({ projectId: project.id, environmentId: environment.id, serviceId: railwayService.id, env });
|
|
2661
|
-
for (const domain of domains.filter((entry) => entry.domain === service.domain)) {
|
|
2662
|
-
if (planOnly) {
|
|
2663
|
-
operations.push(resourceOperation("railway", "custom-domain", domain.domain, "planned", { id: domain.id }));
|
|
2664
|
-
} else {
|
|
2665
|
-
const result = await deleteRailwayCustomDomain({ projectId: project.id, environmentId: environment.id, serviceId: railwayService.id, domainId: domain.id, env });
|
|
2666
|
-
operations.push(resourceOperation("railway", "custom-domain", domain.domain, result.status, { id: domain.id }));
|
|
2667
|
-
}
|
|
2668
|
-
}
|
|
2669
|
-
}
|
|
2670
|
-
}
|
|
2671
|
-
if (deleteData) {
|
|
2672
|
-
const volumes = await listRailwayVolumes({ projectId: project.id, env });
|
|
2673
|
-
for (const volume of volumes) {
|
|
2674
|
-
const matchingInstance = volume.instances.find((instance) => instance.environmentId === environment?.id);
|
|
2675
|
-
if (!matchingInstance) {
|
|
2676
|
-
continue;
|
|
2677
|
-
}
|
|
2678
|
-
if (planOnly) {
|
|
2679
|
-
operations.push(resourceOperation("railway", "volume", volume.name, "planned", { id: volume.id, projectId: project.id }));
|
|
2680
|
-
} else {
|
|
2681
|
-
const result = await deleteRailwayVolume({ projectId: project.id, environmentId: environment.id, volumeId: volume.id, env });
|
|
2682
|
-
operations.push(resourceOperation("railway", "volume", volume.name, result.status, { id: volume.id, projectId: project.id }));
|
|
2683
|
-
}
|
|
2684
|
-
}
|
|
2685
|
-
}
|
|
2686
|
-
const shouldDeleteProject = shouldDeleteRailwayProjectAfterEnvironmentDestroy(project, scope, deleteData, environment?.id ?? null);
|
|
2687
|
-
if (scope === "prod" && deleteData || shouldDeleteProject) {
|
|
2688
|
-
if (planOnly) {
|
|
2689
|
-
operations.push(resourceOperation("railway", "project", project.name, "planned", {
|
|
2690
|
-
id: project.id,
|
|
2691
|
-
reason: scope === "prod" ? "prod_delete_data_cleanup" : "no_managed_persistent_environments"
|
|
2692
|
-
}));
|
|
2693
|
-
} else {
|
|
2694
|
-
throw new Error("Railway project deletion is reconciler-owned. Use trsd reconcile destroy or live acceptance cleanup for project-scoped deletion.");
|
|
2695
|
-
}
|
|
2696
|
-
} else if (environment) {
|
|
2697
|
-
if (planOnly) {
|
|
2698
|
-
operations.push(resourceOperation("railway", "environment", environment.name, "planned", { id: environment.id, projectId: project.id }));
|
|
2699
|
-
} else {
|
|
2700
|
-
const result = await deleteRailwayEnvironment({ projectId: project.id, environmentId: environment.id, env });
|
|
2701
|
-
operations.push(resourceOperation("railway", "environment", environment.name, result.status, { id: environment.id, projectId: project.id }));
|
|
2702
|
-
}
|
|
2703
|
-
}
|
|
2704
|
-
}
|
|
2705
|
-
return { operations };
|
|
2706
|
-
}
|
|
2707
|
-
function shouldDeleteRailwayProjectAfterEnvironmentDestroy(project, scope, deleteData, deletedEnvironmentId = null) {
|
|
2708
|
-
if (!deleteData || scope === "prod") {
|
|
2709
|
-
return false;
|
|
2710
|
-
}
|
|
2711
|
-
const managedPersistentNames = /* @__PURE__ */ new Set(["staging", "production", "prod"]);
|
|
2712
|
-
const targetEnvironmentName = normalizeRailwayEnvironmentName(scope);
|
|
2713
|
-
const remainingManagedEnvironments = (project?.environments ?? []).filter((environment) => environment?.id !== deletedEnvironmentId).filter((environment) => environment?.name !== targetEnvironmentName).filter((environment) => managedPersistentNames.has(environment?.name));
|
|
2714
|
-
return remainingManagedEnvironments.length === 0;
|
|
2715
|
-
}
|
|
2716
|
-
function killPidFromFile(filePath, { planOnly }) {
|
|
2717
|
-
const pid = Number.parseInt(readFileSync(filePath, "utf8").trim(), 10);
|
|
2718
|
-
if (!Number.isFinite(pid) || pid <= 0) {
|
|
2719
|
-
return resourceOperation("local", "dev-process", filePath, "missing");
|
|
2720
|
-
}
|
|
2721
|
-
if (planOnly) {
|
|
2722
|
-
return resourceOperation("local", "dev-process", String(pid), "planned", { pidFile: filePath });
|
|
2723
|
-
}
|
|
2724
|
-
try {
|
|
2725
|
-
process.kill(-pid, "SIGTERM");
|
|
2726
|
-
} catch {
|
|
2727
|
-
try {
|
|
2728
|
-
process.kill(pid, "SIGTERM");
|
|
2729
|
-
} catch {
|
|
2730
|
-
}
|
|
2731
|
-
}
|
|
2732
|
-
try {
|
|
2733
|
-
unlinkSync(filePath);
|
|
2734
|
-
} catch {
|
|
2735
|
-
}
|
|
2736
|
-
return resourceOperation("local", "dev-process", String(pid), "deleted", { pidFile: filePath });
|
|
2737
|
-
}
|
|
2738
|
-
const LOCAL_DOCKER_RESOURCE_PATTERN = /(?:^|[-_.])(?:treeseed|treedx|treedb)(?:[-_.]|$)|(?:treeseed|treedx|treedb)/iu;
|
|
2739
|
-
let destroyDockerRunnerForTests = null;
|
|
2740
|
-
function setDestroyDockerRunnerForTests(runner) {
|
|
2741
|
-
destroyDockerRunnerForTests = runner;
|
|
2742
|
-
}
|
|
2743
|
-
function runDestroyDocker(args) {
|
|
2744
|
-
if (destroyDockerRunnerForTests) {
|
|
2745
|
-
return destroyDockerRunnerForTests(args);
|
|
2746
|
-
}
|
|
2747
|
-
return spawnSync("docker", args, { encoding: "utf8", stdio: "pipe" });
|
|
2748
|
-
}
|
|
2749
|
-
function dockerAvailable() {
|
|
2750
|
-
const result = runDestroyDocker(["info"]);
|
|
2751
|
-
return result.status === 0;
|
|
2752
|
-
}
|
|
2753
|
-
function dockerList(formatArgs) {
|
|
2754
|
-
const result = runDestroyDocker(formatArgs);
|
|
2755
|
-
if (result.status !== 0) {
|
|
2756
|
-
return [];
|
|
2757
|
-
}
|
|
2758
|
-
return result.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
2759
|
-
}
|
|
2760
|
-
function matchingDockerEntries(lines, parser) {
|
|
2761
|
-
return lines.map(parser).filter((entry) => entry && LOCAL_DOCKER_RESOURCE_PATTERN.test(`${entry.name} ${entry.image ?? ""}`));
|
|
2762
|
-
}
|
|
2763
|
-
function removeDockerResource(kind, id, name) {
|
|
2764
|
-
const args = kind === "container" ? ["rm", "-f", id] : kind === "volume" ? ["volume", "rm", "-f", id] : ["network", "rm", id];
|
|
2765
|
-
const result = runDestroyDocker(args);
|
|
2766
|
-
if (result.status === 0) {
|
|
2767
|
-
return resourceOperation("local", `docker-${kind}`, name, "deleted", { id });
|
|
2768
|
-
}
|
|
2769
|
-
return resourceOperation("local", `docker-${kind}`, name, "blocked", {
|
|
2770
|
-
id,
|
|
2771
|
-
reason: result.stderr?.trim() || result.stdout?.trim() || "docker_remove_failed"
|
|
2772
|
-
});
|
|
2773
|
-
}
|
|
2774
|
-
function dockerLocalRuntimeResourceOperations({ planOnly = false } = {}) {
|
|
2775
|
-
if (!dockerAvailable()) {
|
|
2776
|
-
return [resourceOperation("local", "docker-cleanup", "docker", "skipped", { reason: "docker_unavailable" })];
|
|
2777
|
-
}
|
|
2778
|
-
const containers = matchingDockerEntries(
|
|
2779
|
-
dockerList(["ps", "-a", "--format", "{{.ID}} {{.Names}} {{.Image}}"]),
|
|
2780
|
-
(line) => {
|
|
2781
|
-
const [id, name, image] = line.split(" ");
|
|
2782
|
-
return id && name ? { id, name, image } : null;
|
|
2783
|
-
}
|
|
2784
|
-
);
|
|
2785
|
-
const volumes = matchingDockerEntries(
|
|
2786
|
-
dockerList(["volume", "ls", "--format", "{{.Name}}"]),
|
|
2787
|
-
(line) => ({ id: line, name: line })
|
|
2788
|
-
);
|
|
2789
|
-
const networks = matchingDockerEntries(
|
|
2790
|
-
dockerList(["network", "ls", "--format", "{{.ID}} {{.Name}}"]),
|
|
2791
|
-
(line) => {
|
|
2792
|
-
const [id, name] = line.split(" ");
|
|
2793
|
-
return id && name ? { id, name } : null;
|
|
2794
|
-
}
|
|
2795
|
-
).filter((entry) => !["bridge", "host", "none"].includes(entry.name));
|
|
2796
|
-
if (planOnly) {
|
|
2797
|
-
return [
|
|
2798
|
-
...containers.map((entry) => resourceOperation("local", "docker-container", entry.name, "planned", { id: entry.id })),
|
|
2799
|
-
...volumes.map((entry) => resourceOperation("local", "docker-volume", entry.name, "planned", { id: entry.id })),
|
|
2800
|
-
...networks.map((entry) => resourceOperation("local", "docker-network", entry.name, "planned", { id: entry.id })),
|
|
2801
|
-
...containers.length || volumes.length || networks.length ? [] : [resourceOperation("local", "docker-cleanup", "docker", "missing", { reason: "no_matching_resources" })]
|
|
2802
|
-
];
|
|
2803
|
-
}
|
|
2804
|
-
const operations = [];
|
|
2805
|
-
for (const entry of containers) {
|
|
2806
|
-
operations.push(removeDockerResource("container", entry.id, entry.name));
|
|
2807
|
-
}
|
|
2808
|
-
for (const entry of volumes) {
|
|
2809
|
-
operations.push(removeDockerResource("volume", entry.id, entry.name));
|
|
2810
|
-
}
|
|
2811
|
-
for (const entry of networks) {
|
|
2812
|
-
operations.push(removeDockerResource("network", entry.id, entry.name));
|
|
2813
|
-
}
|
|
2814
|
-
if (!operations.length) {
|
|
2815
|
-
operations.push(resourceOperation("local", "docker-cleanup", "docker", "missing", { reason: "no_matching_resources" }));
|
|
2816
|
-
}
|
|
2817
|
-
return operations;
|
|
2818
|
-
}
|
|
2819
|
-
function destroyLocalRuntimeResources(tenantRoot, { planOnly = false, deleteData = false } = {}) {
|
|
2820
|
-
const operations = [];
|
|
2821
|
-
const pidDir = resolve(tenantRoot, ".treeseed/dev-pids");
|
|
2822
|
-
if (existsSync(pidDir)) {
|
|
2823
|
-
for (const entry of readdirSync(pidDir)) {
|
|
2824
|
-
if (entry.endsWith(".pid")) {
|
|
2825
|
-
operations.push(killPidFromFile(resolve(pidDir, entry), { planOnly }));
|
|
2826
|
-
}
|
|
2827
|
-
}
|
|
2828
|
-
} else {
|
|
2829
|
-
operations.push(resourceOperation("local", "dev-pids", pidDir, "missing"));
|
|
2830
|
-
}
|
|
2831
|
-
if (deleteData) {
|
|
2832
|
-
for (const relativePath of [
|
|
2833
|
-
".treeseed/generated/environments/local",
|
|
2834
|
-
".treeseed/generated/dev",
|
|
2835
|
-
".treeseed/operations-runner",
|
|
2836
|
-
".treeseed/local-capacity-provider/data"
|
|
2837
|
-
]) {
|
|
2838
|
-
const absolutePath = resolve(tenantRoot, relativePath);
|
|
2839
|
-
if (!existsSync(absolutePath)) {
|
|
2840
|
-
operations.push(resourceOperation("local", "data-path", relativePath, "missing"));
|
|
2841
|
-
continue;
|
|
2842
|
-
}
|
|
2843
|
-
if (planOnly) {
|
|
2844
|
-
operations.push(resourceOperation("local", "data-path", relativePath, "planned"));
|
|
2845
|
-
continue;
|
|
2846
|
-
}
|
|
2847
|
-
rmSync(absolutePath, { recursive: true, force: true });
|
|
2848
|
-
operations.push(resourceOperation("local", "data-path", relativePath, "deleted"));
|
|
2849
|
-
}
|
|
2850
|
-
operations.push(...dockerLocalRuntimeResourceOperations({ planOnly }));
|
|
2851
|
-
} else {
|
|
2852
|
-
operations.push(resourceOperation("local", "data-path", ".treeseed/generated/environments/local", "skipped", { reason: "data_preserved" }));
|
|
2853
|
-
}
|
|
2854
|
-
return { operations };
|
|
2855
|
-
}
|
|
2856
|
-
function treeSeedSweepTokens(deployConfig, state) {
|
|
2857
|
-
const configuredHosts = [
|
|
2858
|
-
deployConfig.siteUrl,
|
|
2859
|
-
deployConfig.surfaces?.web?.publicBaseUrl,
|
|
2860
|
-
deployConfig.surfaces?.web?.environments?.staging?.domain,
|
|
2861
|
-
deployConfig.surfaces?.web?.environments?.prod?.domain,
|
|
2862
|
-
deployConfig.surfaces?.api?.environments?.staging?.domain,
|
|
2863
|
-
deployConfig.surfaces?.api?.environments?.prod?.domain,
|
|
2864
|
-
deployConfig.services?.api?.environments?.staging?.domain,
|
|
2865
|
-
deployConfig.services?.api?.environments?.prod?.domain
|
|
2866
|
-
].map((value) => primaryHost(value) ?? value);
|
|
2867
|
-
return [...new Set([
|
|
2868
|
-
"treeseed",
|
|
2869
|
-
deployConfig.slug,
|
|
2870
|
-
deployConfig.name,
|
|
2871
|
-
state.identity?.deploymentKey,
|
|
2872
|
-
state.identity?.environmentKey,
|
|
2873
|
-
state.pages?.projectName,
|
|
2874
|
-
state.workerName,
|
|
2875
|
-
state.content?.bucketName,
|
|
2876
|
-
state.kvNamespaces?.FORM_GUARD_KV?.name,
|
|
2877
|
-
state.kvNamespaces?.SESSION?.name,
|
|
2878
|
-
state.d1Databases?.SITE_DATA_DB?.databaseName,
|
|
2879
|
-
...configuredHosts
|
|
2880
|
-
].map((value) => String(value ?? "").trim().toLowerCase()).filter((value) => value.length >= 4))];
|
|
2881
|
-
}
|
|
2882
|
-
function isProtectedAiIntegrationResource(value) {
|
|
2883
|
-
return /(?:^|[-_.])(?:ai-gateway|workers-ai|ai-integration|openai|anthropic)(?:[-_.]|$)/iu.test(String(value ?? ""));
|
|
2884
|
-
}
|
|
2885
|
-
function matchesTreeSeedSweep(value, tokens) {
|
|
2886
|
-
const normalized = String(value ?? "").trim().toLowerCase();
|
|
2887
|
-
if (!normalized || isProtectedAiIntegrationResource(normalized)) {
|
|
2888
|
-
return false;
|
|
2889
|
-
}
|
|
2890
|
-
return tokens.some((token) => normalized === token || normalized.includes(token));
|
|
2891
|
-
}
|
|
2892
|
-
function cloudflareNameCandidates(entry) {
|
|
2893
|
-
return [
|
|
2894
|
-
entry?.name,
|
|
2895
|
-
entry?.title,
|
|
2896
|
-
entry?.id,
|
|
2897
|
-
entry?.queue_name,
|
|
2898
|
-
entry?.script,
|
|
2899
|
-
entry?.domain,
|
|
2900
|
-
entry?.hostname,
|
|
2901
|
-
entry?.content,
|
|
2902
|
-
entry?.comment,
|
|
2903
|
-
...Array.isArray(entry?.domains) ? entry.domains : [],
|
|
2904
|
-
...Array.isArray(entry?.tags) ? entry.tags : []
|
|
2905
|
-
].filter(Boolean);
|
|
2906
|
-
}
|
|
2907
|
-
function cloudflareEntryMatchesTreeSeed(entry, tokens) {
|
|
2908
|
-
return cloudflareNameCandidates(entry).some((candidate) => matchesTreeSeedSweep(candidate, tokens));
|
|
2909
|
-
}
|
|
2910
|
-
function deleteDnsRecord(zoneId, record, { env, planOnly }) {
|
|
2911
|
-
const name = record?.name ?? record?.content ?? record?.id ?? null;
|
|
2912
|
-
if (!zoneId || !record?.id) {
|
|
2913
|
-
return resourceOperation("cloudflare", "dns-record", name, "missing", { zoneId });
|
|
2914
|
-
}
|
|
2915
|
-
if (planOnly) {
|
|
2916
|
-
return resourceOperation("cloudflare", "dns-record", name, "planned", {
|
|
2917
|
-
zoneId,
|
|
2918
|
-
id: record.id,
|
|
2919
|
-
content: record.content ?? null,
|
|
2920
|
-
recordType: record.type ?? null
|
|
2921
|
-
});
|
|
2922
|
-
}
|
|
2923
|
-
return deleteCloudflareApiResource(
|
|
2924
|
-
`/zones/${encodeURIComponent(zoneId)}/dns_records/${encodeURIComponent(record.id)}`,
|
|
2925
|
-
{ env, planOnly: false, name, type: "dns-record" }
|
|
2926
|
-
);
|
|
2927
|
-
}
|
|
2928
|
-
function sweepTreeSeedCloudflareResources(tenantRoot, deployConfig, state, { env, planOnly, deleteData }) {
|
|
2929
|
-
const tokens = treeSeedSweepTokens(deployConfig, state);
|
|
2930
|
-
const operations = [];
|
|
2931
|
-
const pagesProjects = listPagesProjects(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens));
|
|
2932
|
-
for (const project of pagesProjects) {
|
|
2933
|
-
const projectName = project?.name ?? project?.id ?? null;
|
|
2934
|
-
operations.push(...deletePagesCustomDomains(tenantRoot, projectName, [], { env, planOnly, knownOnly: false }));
|
|
2935
|
-
operations.push(deletePagesDeployments(tenantRoot, projectName, { env, planOnly, environment: "all" }));
|
|
2936
|
-
operations.push(deletePagesProject(projectName, { env, planOnly }));
|
|
2937
|
-
}
|
|
2938
|
-
for (const worker of listWorkers(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2939
|
-
const name = worker?.id ?? worker?.name ?? worker?.script ?? null;
|
|
2940
|
-
const deleted = deleteWorker(tenantRoot, name, { env, planOnly, force: true });
|
|
2941
|
-
operations.push(resourceOperation("cloudflare", "worker", name, deleted.status, { ...deleted, sweep: true }));
|
|
2942
|
-
}
|
|
2943
|
-
for (const namespace of listKvNamespaces(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2944
|
-
const deleted = deleteKvNamespace(tenantRoot, namespace.id, { env, planOnly });
|
|
2945
|
-
operations.push(resourceOperation("cloudflare", "kv-namespace", namespace.title ?? namespace.id, deleted.status, { ...deleted, sweep: true }));
|
|
2946
|
-
}
|
|
2947
|
-
for (const queue of listQueues(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2948
|
-
operations.push({ ...deleteQueueByName(tenantRoot, queue, { env, planOnly }), sweep: true });
|
|
2949
|
-
}
|
|
2950
|
-
for (const database of listD1Databases(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2951
|
-
const name = database?.name ?? database?.uuid ?? database?.id ?? null;
|
|
2952
|
-
const deleted = deleteData ? deleteD1Database(tenantRoot, name, { env, planOnly }) : null;
|
|
2953
|
-
operations.push(resourceOperation("cloudflare", "d1-database", name, deleteData ? deleted?.status : "skipped", {
|
|
2954
|
-
...deleteData ? deleted : { reason: "data_preserved" },
|
|
2955
|
-
sweep: true
|
|
2956
|
-
}));
|
|
2957
|
-
}
|
|
2958
|
-
for (const bucket of listR2Buckets(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2959
|
-
operations.push({ ...deleteR2Bucket(tenantRoot, bucket.name, { env, planOnly, deleteData }), sweep: true });
|
|
2960
|
-
}
|
|
2961
|
-
for (const widget of listTurnstileWidgets(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2962
|
-
const deleted = deleteTurnstileWidget(widget.sitekey, { env, planOnly, name: widget.name });
|
|
2963
|
-
operations.push(resourceOperation("cloudflare", "turnstile-widget", widget.name ?? widget.sitekey, deleted.status, { ...deleted, sweep: true }));
|
|
2964
|
-
}
|
|
2965
|
-
for (const zone of listDnsZones(env)) {
|
|
2966
|
-
const zoneId = zone?.id ?? null;
|
|
2967
|
-
for (const record of listDnsRecords(zoneId, env)) {
|
|
2968
|
-
if (record?.type === "SOA" || record?.type === "NS") {
|
|
2969
|
-
continue;
|
|
2970
|
-
}
|
|
2971
|
-
if (!cloudflareEntryMatchesTreeSeed(record, tokens)) {
|
|
2972
|
-
continue;
|
|
2973
|
-
}
|
|
2974
|
-
operations.push({ ...deleteDnsRecord(zoneId, record, { env, planOnly }), zoneName: zone?.name ?? null, sweep: true });
|
|
2975
|
-
}
|
|
2976
|
-
}
|
|
2977
|
-
return operations.length > 0 ? operations : [resourceOperation("cloudflare", "treeseed-sweep", "cloudflare", "missing", { reason: "no_matching_resources" })];
|
|
2978
|
-
}
|
|
2979
|
-
function countMatchingCloudflareEntries(entries, tokens) {
|
|
2980
|
-
return entries.filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens)).length;
|
|
2981
|
-
}
|
|
2982
|
-
function cloudflareDestroyVerification(tenantRoot, deployConfig, state, env) {
|
|
2983
|
-
const tokens = treeSeedSweepTokens(deployConfig, state);
|
|
2984
|
-
const zoneIds = /* @__PURE__ */ new Set([
|
|
2985
|
-
deployConfig.cloudflare?.zoneId,
|
|
2986
|
-
state.webCache?.webZoneId,
|
|
2987
|
-
state.webCache?.contentZoneId
|
|
2988
|
-
]);
|
|
2989
|
-
for (const zone of listDnsZones(env)) {
|
|
2990
|
-
if (zone?.id) {
|
|
2991
|
-
zoneIds.add(zone.id);
|
|
2992
|
-
}
|
|
2993
|
-
}
|
|
2994
|
-
const dnsRecords = [];
|
|
2995
|
-
for (const zoneId of [...zoneIds].filter(Boolean)) {
|
|
2996
|
-
dnsRecords.push(...listDnsRecords(zoneId, env));
|
|
2997
|
-
}
|
|
2998
|
-
const remaining = {
|
|
2999
|
-
pages: countMatchingCloudflareEntries(listPagesProjects(tenantRoot, env), tokens),
|
|
3000
|
-
workers: countMatchingCloudflareEntries(listWorkers(tenantRoot, env), tokens),
|
|
3001
|
-
kvNamespaces: countMatchingCloudflareEntries(listKvNamespaces(tenantRoot, env), tokens),
|
|
3002
|
-
queues: countMatchingCloudflareEntries(listQueues(tenantRoot, env), tokens),
|
|
3003
|
-
d1Databases: countMatchingCloudflareEntries(listD1Databases(tenantRoot, env), tokens),
|
|
3004
|
-
r2Buckets: countMatchingCloudflareEntries(listR2Buckets(tenantRoot, env), tokens),
|
|
3005
|
-
turnstileWidgets: countMatchingCloudflareEntries(listTurnstileWidgets(tenantRoot, env), tokens),
|
|
3006
|
-
dnsRecords: countMatchingCloudflareEntries(
|
|
3007
|
-
dnsRecords.filter((record) => record?.type !== "SOA" && record?.type !== "NS"),
|
|
3008
|
-
tokens
|
|
3009
|
-
)
|
|
3010
|
-
};
|
|
3011
|
-
const totalRemaining = Object.values(remaining).reduce((sum, value) => sum + value, 0);
|
|
3012
|
-
return {
|
|
3013
|
-
provider: "cloudflare",
|
|
3014
|
-
method: "cloudflare-api",
|
|
3015
|
-
status: totalRemaining === 0 ? "clean" : "remaining",
|
|
3016
|
-
remaining,
|
|
3017
|
-
totalRemaining
|
|
3018
|
-
};
|
|
3019
|
-
}
|
|
3020
|
-
function localDockerDestroyVerification() {
|
|
3021
|
-
if (!dockerAvailable()) {
|
|
3022
|
-
return {
|
|
3023
|
-
provider: "local-docker",
|
|
3024
|
-
method: "docker-cli",
|
|
3025
|
-
status: "skipped",
|
|
3026
|
-
reason: "docker_unavailable",
|
|
3027
|
-
remaining: {
|
|
3028
|
-
containers: 0,
|
|
3029
|
-
volumes: 0,
|
|
3030
|
-
networks: 0
|
|
3031
|
-
},
|
|
3032
|
-
totalRemaining: 0
|
|
3033
|
-
};
|
|
3034
|
-
}
|
|
3035
|
-
const containers = matchingDockerEntries(
|
|
3036
|
-
dockerList(["ps", "-a", "--format", "{{.ID}} {{.Names}} {{.Image}}"]),
|
|
3037
|
-
(line) => {
|
|
3038
|
-
const [id, name, image] = line.split(" ");
|
|
3039
|
-
return id && name ? { id, name, image } : null;
|
|
3040
|
-
}
|
|
3041
|
-
).length;
|
|
3042
|
-
const volumes = matchingDockerEntries(
|
|
3043
|
-
dockerList(["volume", "ls", "--format", "{{.Name}}"]),
|
|
3044
|
-
(line) => ({ id: line, name: line })
|
|
3045
|
-
).length;
|
|
3046
|
-
const networks = matchingDockerEntries(
|
|
3047
|
-
dockerList(["network", "ls", "--format", "{{.ID}} {{.Name}}"]),
|
|
3048
|
-
(line) => {
|
|
3049
|
-
const [id, name] = line.split(" ");
|
|
3050
|
-
return id && name ? { id, name } : null;
|
|
3051
|
-
}
|
|
3052
|
-
).filter((entry) => !["bridge", "host", "none"].includes(entry.name)).length;
|
|
3053
|
-
const remaining = { containers, volumes, networks };
|
|
3054
|
-
const totalRemaining = containers + volumes + networks;
|
|
3055
|
-
return {
|
|
3056
|
-
provider: "local-docker",
|
|
3057
|
-
method: "docker-cli",
|
|
3058
|
-
status: totalRemaining === 0 ? "clean" : "remaining",
|
|
3059
|
-
remaining,
|
|
3060
|
-
totalRemaining
|
|
3061
|
-
};
|
|
3062
|
-
}
|
|
3063
|
-
async function sweepTreeSeedRailwayResources(deployConfig, state, { env, planOnly }) {
|
|
3064
|
-
if (!resolveRailwayApiToken(env)) {
|
|
3065
|
-
return [resourceOperation("railway", "treeseed-sweep", "railway", "blocked", { reason: "missing_railway_api_token" })];
|
|
3066
|
-
}
|
|
3067
|
-
const tokens = treeSeedSweepTokens(deployConfig, state);
|
|
3068
|
-
const workspace = await resolveRailwayWorkspaceContext({ env, workspace: resolveRailwayWorkspace(env) });
|
|
3069
|
-
const projects = await listRailwayProjects({ env, workspaceId: workspace.id });
|
|
3070
|
-
const operations = [];
|
|
3071
|
-
for (const project of projects) {
|
|
3072
|
-
if (project.deletedAt || !matchesTreeSeedSweep(project.name, tokens)) {
|
|
3073
|
-
continue;
|
|
3074
|
-
}
|
|
3075
|
-
if (planOnly) {
|
|
3076
|
-
operations.push(resourceOperation("railway", "project", project.name, "planned", {
|
|
3077
|
-
id: project.id,
|
|
3078
|
-
workspaceId: workspace.id,
|
|
3079
|
-
sweep: true
|
|
3080
|
-
}));
|
|
3081
|
-
} else {
|
|
3082
|
-
throw new Error("Railway project sweep deletion is reconciler-owned. Use trsd reconcile test-live --mode cleanup for isolated cleanup.");
|
|
3083
|
-
}
|
|
3084
|
-
}
|
|
3085
|
-
return operations.length > 0 ? operations : [resourceOperation("railway", "treeseed-sweep", "railway", "missing", { reason: "no_matching_projects" })];
|
|
3086
|
-
}
|
|
3087
|
-
async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
|
|
3088
|
-
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
3089
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
3090
|
-
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
3091
|
-
state.workerName = targetWorkerName(deployConfig, target);
|
|
3092
|
-
const env = {
|
|
3093
|
-
CLOUDFLARE_ACCOUNT_ID: resolveConfiguredCloudflareAccountId(deployConfig)
|
|
3094
|
-
};
|
|
3095
|
-
const planOnly = options.planOnly ?? false;
|
|
3096
|
-
const deleteData = options.deleteData === true;
|
|
3097
|
-
const force = options.force ?? false;
|
|
3098
|
-
const sweepTreeseed = options.sweepTreeseed === true;
|
|
3099
|
-
const destroysSharedWebSurface = target.kind === "persistent" && target.scope === "prod" && deleteData;
|
|
3100
|
-
const kvNamespaces = planOnly ? [] : listKvNamespaces(tenantRoot, env);
|
|
3101
|
-
const d1Databases = planOnly ? [] : listD1Databases(tenantRoot, env);
|
|
3102
|
-
const queues = planOnly ? [] : listQueues(tenantRoot, env);
|
|
3103
|
-
const buckets = planOnly ? [] : listR2Buckets(tenantRoot, env);
|
|
3104
|
-
const pagesProjects = planOnly ? [] : listPagesProjects(tenantRoot, env);
|
|
3105
|
-
const turnstileWidgets = planOnly ? [] : listTurnstileWidgets(tenantRoot, env);
|
|
3106
|
-
state.kvNamespaces.FORM_GUARD_KV.id = resolveExistingKvIdByName(
|
|
3107
|
-
kvNamespaces,
|
|
3108
|
-
state.kvNamespaces.FORM_GUARD_KV.name,
|
|
3109
|
-
state.kvNamespaces.FORM_GUARD_KV.id
|
|
3110
|
-
);
|
|
3111
|
-
if (state.kvNamespaces.SESSION?.name) {
|
|
3112
|
-
state.kvNamespaces.SESSION.id = resolveExistingKvIdByName(
|
|
3113
|
-
kvNamespaces,
|
|
3114
|
-
state.kvNamespaces.SESSION.name,
|
|
3115
|
-
state.kvNamespaces.SESSION.id
|
|
3116
|
-
);
|
|
3117
|
-
}
|
|
3118
|
-
state.d1Databases.SITE_DATA_DB = resolveExistingD1ByName(
|
|
3119
|
-
d1Databases,
|
|
3120
|
-
state.d1Databases.SITE_DATA_DB.databaseName,
|
|
3121
|
-
state.d1Databases.SITE_DATA_DB
|
|
3122
|
-
);
|
|
3123
|
-
state.turnstileWidgets.formGuard = resolveExistingTurnstileWidget(turnstileWidgets, state.turnstileWidgets?.formGuard);
|
|
3124
|
-
const pagesProject = pagesProjects.find((entry) => entry?.name === state.pages?.projectName);
|
|
3125
|
-
const bucket = buckets.find((entry) => entry?.name === state.content?.bucketName);
|
|
3126
|
-
const workerResult = deleteWorker(tenantRoot, state.workerName, { env, planOnly, force });
|
|
3127
|
-
const turnstileWidget = deleteTurnstileWidget(state.turnstileWidgets?.formGuard?.sitekey, {
|
|
3128
|
-
env,
|
|
3129
|
-
planOnly,
|
|
3130
|
-
name: state.turnstileWidgets?.formGuard?.name
|
|
3131
|
-
});
|
|
3132
|
-
const formGuard = deleteKvNamespace(tenantRoot, state.kvNamespaces.FORM_GUARD_KV.id, { env, planOnly });
|
|
3133
|
-
const formGuardPreview = state.kvNamespaces.FORM_GUARD_KV.previewId && state.kvNamespaces.FORM_GUARD_KV.previewId !== state.kvNamespaces.FORM_GUARD_KV.id ? deleteKvNamespace(tenantRoot, state.kvNamespaces.FORM_GUARD_KV.previewId, { env, planOnly, preview: true }) : null;
|
|
3134
|
-
const session = state.kvNamespaces.SESSION?.id ? deleteKvNamespace(tenantRoot, state.kvNamespaces.SESSION.id, { env, planOnly }) : null;
|
|
3135
|
-
const sessionPreview = state.kvNamespaces.SESSION?.previewId && state.kvNamespaces.SESSION.previewId !== state.kvNamespaces.SESSION.id ? deleteKvNamespace(tenantRoot, state.kvNamespaces.SESSION.previewId, { env, planOnly, preview: true }) : null;
|
|
3136
|
-
const knownKvIds = new Set([
|
|
3137
|
-
state.kvNamespaces.FORM_GUARD_KV.id,
|
|
3138
|
-
state.kvNamespaces.FORM_GUARD_KV.previewId,
|
|
3139
|
-
state.kvNamespaces.SESSION?.id,
|
|
3140
|
-
state.kvNamespaces.SESSION?.previewId
|
|
3141
|
-
].filter(Boolean));
|
|
3142
|
-
const legacyKvPrefix = state.identity?.deploymentKey ?? state.pages?.projectName ?? "";
|
|
3143
|
-
const legacyKvNamespaces = planOnly ? [] : kvNamespaces.filter((namespace) => {
|
|
3144
|
-
const title = typeof namespace?.title === "string" ? namespace.title : "";
|
|
3145
|
-
const id = typeof namespace?.id === "string" ? namespace.id : "";
|
|
3146
|
-
return title && id && !knownKvIds.has(id) && legacyKvPrefix && title.includes(legacyKvPrefix) && title.includes(target.scope);
|
|
3147
|
-
}).map((namespace) => {
|
|
3148
|
-
const result = deleteKvNamespace(tenantRoot, namespace.id, { env, planOnly: false });
|
|
3149
|
-
return resourceOperation("cloudflare", "kv-namespace", namespace.title, result.status, { ...result, legacy: true });
|
|
3150
|
-
});
|
|
3151
|
-
const database = deleteD1DatabaseForDestroy(tenantRoot, state.d1Databases.SITE_DATA_DB.databaseName, { env, planOnly, deleteData });
|
|
3152
|
-
const r2Bucket = bucket || planOnly ? deleteR2Bucket(tenantRoot, state.content?.bucketName, { env, planOnly, deleteData }) : resourceOperation("cloudflare", "r2-bucket", state.content?.bucketName, "missing");
|
|
3153
|
-
const pageDnsNames = [
|
|
3154
|
-
state.pages?.customDomain,
|
|
3155
|
-
deployConfig.surfaces?.web?.environments?.[target.scope]?.domain,
|
|
3156
|
-
target.scope === "prod" ? primaryHost(deployConfig.surfaces?.web?.publicBaseUrl ?? deployConfig.siteUrl) : null
|
|
3157
|
-
].filter(Boolean);
|
|
3158
|
-
const apiDnsNames = [
|
|
3159
|
-
deployConfig.services?.api?.environments?.[target.scope]?.domain,
|
|
3160
|
-
deployConfig.surfaces?.api?.environments?.[target.scope]?.domain
|
|
3161
|
-
].filter(Boolean);
|
|
3162
|
-
const dnsRecords = [.../* @__PURE__ */ new Set([...pageDnsNames, ...apiDnsNames])].flatMap((name) => deleteDnsRecordsForName(deployConfig, name, { env, planOnly }));
|
|
3163
|
-
const cacheRules = deleteTreeseedCacheRules(deployConfig, state, { env, planOnly });
|
|
3164
|
-
const pageCustomDomains = pagesProject || planOnly ? deletePagesCustomDomains(tenantRoot, state.pages?.projectName, pageDnsNames, { env, planOnly, knownOnly: !destroysSharedWebSurface }) : [resourceOperation("cloudflare", "pages-custom-domain", state.pages?.projectName, "missing")];
|
|
3165
|
-
const pageDeployments = pagesProject || planOnly ? deletePagesDeployments(tenantRoot, state.pages?.projectName, {
|
|
3166
|
-
env,
|
|
3167
|
-
planOnly,
|
|
3168
|
-
environment: destroysSharedWebSurface ? "all" : "preview"
|
|
3169
|
-
}) : resourceOperation("cloudflare", "pages-deployments", state.pages?.projectName, "missing");
|
|
3170
|
-
const pages = destroysSharedWebSurface && (pagesProject || planOnly) ? deletePagesProject(state.pages?.projectName, { env, planOnly }) : resourceOperation("cloudflare", "pages-project", state.pages?.projectName, "skipped", {
|
|
3171
|
-
reason: target.scope === "prod" ? "delete_data_required" : "shared_web_surface"
|
|
3172
|
-
});
|
|
3173
|
-
const local = target.kind === "persistent" && target.scope === "local" ? destroyLocalRuntimeResources(tenantRoot, { planOnly, deleteData }) : { operations: [] };
|
|
3174
|
-
const railway = await destroyRailwayResources(tenantRoot, deployConfig, target, { planOnly, deleteData, env: process.env });
|
|
3175
|
-
const sweep = sweepTreeseed ? {
|
|
3176
|
-
cloudflare: sweepTreeSeedCloudflareResources(tenantRoot, deployConfig, state, { env, planOnly, deleteData }),
|
|
3177
|
-
railway: await sweepTreeSeedRailwayResources(deployConfig, state, { env: process.env, planOnly })
|
|
3178
|
-
} : { cloudflare: [], railway: [] };
|
|
3179
|
-
const operations = {
|
|
3180
|
-
cloudflare: [
|
|
3181
|
-
resourceOperation("cloudflare", "worker", state.workerName, workerResult.status, workerResult),
|
|
3182
|
-
resourceOperation("cloudflare", "turnstile-widget", state.turnstileWidgets?.formGuard?.name, turnstileWidget.status, turnstileWidget),
|
|
3183
|
-
resourceOperation("cloudflare", "kv-namespace", state.kvNamespaces.FORM_GUARD_KV.name, formGuard.status, formGuard),
|
|
3184
|
-
...formGuardPreview ? [resourceOperation("cloudflare", "kv-namespace-preview", state.kvNamespaces.FORM_GUARD_KV.name, formGuardPreview.status, formGuardPreview)] : [],
|
|
3185
|
-
...session ? [resourceOperation("cloudflare", "kv-namespace", state.kvNamespaces.SESSION.name, session.status, session)] : [],
|
|
3186
|
-
...sessionPreview ? [resourceOperation("cloudflare", "kv-namespace-preview", state.kvNamespaces.SESSION.name, sessionPreview.status, sessionPreview)] : [],
|
|
3187
|
-
...legacyKvNamespaces,
|
|
3188
|
-
database,
|
|
3189
|
-
r2Bucket,
|
|
3190
|
-
...pageCustomDomains,
|
|
3191
|
-
pageDeployments,
|
|
3192
|
-
pages,
|
|
3193
|
-
...dnsRecords,
|
|
3194
|
-
...cacheRules,
|
|
3195
|
-
...sweep.cloudflare
|
|
3196
|
-
],
|
|
3197
|
-
railway: [
|
|
3198
|
-
...railway.operations,
|
|
3199
|
-
...sweep.railway
|
|
3200
|
-
],
|
|
3201
|
-
local: local.operations
|
|
3202
|
-
};
|
|
3203
|
-
const verification = planOnly ? null : {
|
|
3204
|
-
cloudflare: cloudflareDestroyVerification(tenantRoot, deployConfig, state, env),
|
|
3205
|
-
...target.kind === "persistent" && target.scope === "local" ? { localDocker: localDockerDestroyVerification() } : {}
|
|
3206
|
-
};
|
|
3207
|
-
return {
|
|
3208
|
-
target,
|
|
3209
|
-
deleteData,
|
|
3210
|
-
sweepTreeseed,
|
|
3211
|
-
summary: buildDestroySummary(deployConfig, state, target),
|
|
3212
|
-
operations,
|
|
3213
|
-
verification
|
|
3214
|
-
};
|
|
3215
|
-
}
|
|
3216
|
-
function destroyCloudflareResources(tenantRoot, options = {}) {
|
|
3217
|
-
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
3218
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
3219
|
-
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
3220
|
-
state.workerName = targetWorkerName(deployConfig, target);
|
|
3221
|
-
const env = {
|
|
3222
|
-
CLOUDFLARE_ACCOUNT_ID: resolveConfiguredCloudflareAccountId(deployConfig)
|
|
3223
|
-
};
|
|
3224
|
-
const planOnly = options.planOnly ?? false;
|
|
3225
|
-
const deleteData = options.deleteData === true;
|
|
3226
|
-
const force = options.force ?? false;
|
|
3227
|
-
const kvNamespaces = planOnly ? [] : listKvNamespaces(tenantRoot, env);
|
|
3228
|
-
const d1Databases = planOnly ? [] : listD1Databases(tenantRoot, env);
|
|
3229
|
-
const queues = listQueues(tenantRoot, env);
|
|
3230
|
-
const buckets = planOnly ? [] : listR2Buckets(tenantRoot, env);
|
|
3231
|
-
const pagesProjects = planOnly ? [] : listPagesProjects(tenantRoot, env);
|
|
3232
|
-
const turnstileWidgets = planOnly ? [] : listTurnstileWidgets(tenantRoot, env);
|
|
3233
|
-
state.kvNamespaces.FORM_GUARD_KV.id = resolveExistingKvIdByName(
|
|
3234
|
-
kvNamespaces,
|
|
3235
|
-
state.kvNamespaces.FORM_GUARD_KV.name,
|
|
3236
|
-
state.kvNamespaces.FORM_GUARD_KV.id
|
|
3237
|
-
);
|
|
3238
|
-
state.d1Databases.SITE_DATA_DB = resolveExistingD1ByName(
|
|
3239
|
-
d1Databases,
|
|
3240
|
-
state.d1Databases.SITE_DATA_DB.databaseName,
|
|
3241
|
-
state.d1Databases.SITE_DATA_DB
|
|
3242
|
-
);
|
|
3243
|
-
state.turnstileWidgets.formGuard = resolveExistingTurnstileWidget(turnstileWidgets, state.turnstileWidgets?.formGuard);
|
|
3244
|
-
const bucket = buckets.find((entry) => entry?.name === state.content?.bucketName);
|
|
3245
|
-
const pagesProject = pagesProjects.find((entry) => entry?.name === state.pages?.projectName);
|
|
3246
|
-
const worker = deleteWorker(tenantRoot, state.workerName, { env, planOnly, force });
|
|
3247
|
-
const turnstileWidget = deleteTurnstileWidget(state.turnstileWidgets?.formGuard?.sitekey, {
|
|
3248
|
-
env,
|
|
3249
|
-
planOnly,
|
|
3250
|
-
name: state.turnstileWidgets?.formGuard?.name
|
|
3251
|
-
});
|
|
3252
|
-
const formGuard = deleteKvNamespace(tenantRoot, state.kvNamespaces.FORM_GUARD_KV.id, { env, planOnly });
|
|
3253
|
-
const database = deleteD1DatabaseForDestroy(tenantRoot, state.d1Databases.SITE_DATA_DB.databaseName, { env, planOnly, deleteData });
|
|
3254
|
-
const r2Bucket = bucket || planOnly ? deleteR2Bucket(tenantRoot, state.content?.bucketName, { env, planOnly, deleteData }) : resourceOperation("cloudflare", "r2-bucket", state.content?.bucketName, "missing");
|
|
3255
|
-
const pages = pagesProject || planOnly ? deletePagesProject(state.pages?.projectName, { env, planOnly }) : resourceOperation("cloudflare", "pages-project", state.pages?.projectName, "missing");
|
|
3256
|
-
const operations = {
|
|
3257
|
-
worker,
|
|
3258
|
-
turnstileWidget,
|
|
3259
|
-
formGuard,
|
|
3260
|
-
database,
|
|
3261
|
-
r2Bucket,
|
|
3262
|
-
pages
|
|
3263
|
-
};
|
|
3264
|
-
return {
|
|
3265
|
-
target,
|
|
3266
|
-
deleteData,
|
|
3267
|
-
summary: buildDestroySummary(deployConfig, state, target),
|
|
3268
|
-
operations
|
|
3269
|
-
};
|
|
3270
|
-
}
|
|
3271
|
-
function cleanupDestroyedState(tenantRoot, options = {}) {
|
|
3272
|
-
const target = options.scope || options.target ? normalizeTarget(options.scope ?? options.target) : null;
|
|
3273
|
-
if (target) {
|
|
3274
|
-
const { statePath, generatedRoot } = resolveTargetPaths(tenantRoot, target);
|
|
3275
|
-
rmSync(statePath, { force: true });
|
|
3276
|
-
rmSync(generatedRoot, { recursive: true, force: true });
|
|
3277
|
-
if (options.removeBuildArtifacts) {
|
|
3278
|
-
rmSync(resolve(tenantRoot, "dist"), { recursive: true, force: true });
|
|
3279
|
-
}
|
|
3280
|
-
return;
|
|
3281
|
-
}
|
|
3282
|
-
rmSync(resolve(tenantRoot, STATE_ROOT), { recursive: true, force: true });
|
|
3283
|
-
rmSync(resolve(tenantRoot, GENERATED_ROOT), { recursive: true, force: true });
|
|
3284
|
-
if (options.removeBuildArtifacts) {
|
|
3285
|
-
rmSync(resolve(tenantRoot, "dist"), { recursive: true, force: true });
|
|
3286
|
-
}
|
|
3287
|
-
}
|
|
3288
|
-
function provisionCloudflareResources(tenantRoot, options = {}) {
|
|
3289
|
-
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
3290
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
3291
|
-
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
3292
|
-
state.workerName = targetWorkerName(deployConfig, target);
|
|
3293
|
-
const env = {
|
|
3294
|
-
CLOUDFLARE_ACCOUNT_ID: resolveConfiguredCloudflareAccountId(deployConfig)
|
|
3295
|
-
};
|
|
3296
|
-
const planOnly = options.planOnly ?? false;
|
|
3297
|
-
const kvNamespaces = planOnly ? [] : listKvNamespaces(tenantRoot, env);
|
|
3298
|
-
const d1Databases = planOnly ? [] : listD1Databases(tenantRoot, env);
|
|
3299
|
-
const queues = planOnly ? [] : listQueues(tenantRoot, env);
|
|
3300
|
-
const buckets = planOnly ? [] : listR2Buckets(tenantRoot, env);
|
|
3301
|
-
const pagesProjects = planOnly ? [] : listPagesProjects(tenantRoot, env);
|
|
3302
|
-
const ensureKv = (binding) => {
|
|
3303
|
-
const current = state.kvNamespaces[binding];
|
|
3304
|
-
if (current?.id && !isPlaceholderResourceId(current.id)) {
|
|
3305
|
-
state.kvNamespaces[binding].previewId = current.previewId ?? current.id;
|
|
3306
|
-
return;
|
|
3307
|
-
}
|
|
3308
|
-
const existing = kvNamespaces.find((entry) => entry?.title === current.name);
|
|
3309
|
-
if (existing?.id) {
|
|
3310
|
-
state.kvNamespaces[binding].id = existing.id;
|
|
3311
|
-
state.kvNamespaces[binding].previewId = existing.id;
|
|
3312
|
-
return;
|
|
3313
|
-
}
|
|
3314
|
-
if (planOnly) {
|
|
3315
|
-
state.kvNamespaces[binding].id = `plan-${current.name}`;
|
|
3316
|
-
state.kvNamespaces[binding].previewId = `plan-${current.name}-preview`;
|
|
3317
|
-
return;
|
|
3318
|
-
}
|
|
3319
|
-
runWrangler(["kv", "namespace", "create", current.name], { cwd: tenantRoot, capture: true, env });
|
|
3320
|
-
const refreshed = listKvNamespaces(tenantRoot, env);
|
|
3321
|
-
const created = refreshed.find((entry) => entry?.title === current.name);
|
|
3322
|
-
if (!created?.id) {
|
|
3323
|
-
throw new Error(`Unable to resolve created KV namespace id for ${current.name}.`);
|
|
3324
|
-
}
|
|
3325
|
-
state.kvNamespaces[binding].id = created.id;
|
|
3326
|
-
state.kvNamespaces[binding].previewId = created.id;
|
|
3327
|
-
};
|
|
3328
|
-
const ensureD1 = () => {
|
|
3329
|
-
const current = state.d1Databases.SITE_DATA_DB;
|
|
3330
|
-
if (current?.databaseId && !isPlaceholderResourceId(current.databaseId)) {
|
|
3331
|
-
return;
|
|
3332
|
-
}
|
|
3333
|
-
const existing = d1Databases.find((entry) => entry?.name === current.databaseName);
|
|
3334
|
-
if (existing?.uuid) {
|
|
3335
|
-
current.databaseId = existing.uuid;
|
|
3336
|
-
current.previewDatabaseId = existing.previewDatabaseUuid ?? existing.uuid;
|
|
3337
|
-
return;
|
|
3338
|
-
}
|
|
3339
|
-
if (planOnly) {
|
|
3340
|
-
current.databaseId = `plan-${current.databaseName}`;
|
|
3341
|
-
current.previewDatabaseId = `plan-${current.databaseName}-preview`;
|
|
3342
|
-
return;
|
|
3343
|
-
}
|
|
3344
|
-
runWrangler(["d1", "create", current.databaseName], {
|
|
3345
|
-
cwd: tenantRoot,
|
|
3346
|
-
capture: true,
|
|
3347
|
-
env
|
|
3348
|
-
});
|
|
3349
|
-
const refreshed = listD1Databases(tenantRoot, env);
|
|
3350
|
-
const created = refreshed.find((entry) => entry?.name === current.databaseName);
|
|
3351
|
-
if (!created?.uuid) {
|
|
3352
|
-
throw new Error(`Unable to resolve created D1 database id for ${current.databaseName}.`);
|
|
3353
|
-
}
|
|
3354
|
-
current.databaseId = created.uuid;
|
|
3355
|
-
current.previewDatabaseId = created.previewDatabaseUuid ?? created.uuid;
|
|
3356
|
-
};
|
|
3357
|
-
const ensureR2Bucket = () => {
|
|
3358
|
-
const bucketName = state.content?.bucketName;
|
|
3359
|
-
if (!bucketName) {
|
|
3360
|
-
return;
|
|
3361
|
-
}
|
|
3362
|
-
let refreshedBuckets = buckets;
|
|
3363
|
-
const exists = refreshedBuckets.find((entry) => entry?.name === bucketName);
|
|
3364
|
-
if (exists) {
|
|
3365
|
-
return;
|
|
3366
|
-
}
|
|
3367
|
-
if (planOnly) {
|
|
3368
|
-
return;
|
|
3369
|
-
}
|
|
3370
|
-
try {
|
|
3371
|
-
runWrangler(["r2", "bucket", "create", bucketName], {
|
|
3372
|
-
cwd: tenantRoot,
|
|
3373
|
-
capture: true,
|
|
3374
|
-
env
|
|
3375
|
-
});
|
|
3376
|
-
} catch (error) {
|
|
3377
|
-
if (!isWranglerAlreadyExistsError(error, [/bucket you tried to create already exists, and you own it/i, /\[code:\s*10004\]/i])) {
|
|
3378
|
-
throw error;
|
|
3379
|
-
}
|
|
3380
|
-
}
|
|
3381
|
-
refreshedBuckets = listR2Buckets(tenantRoot, env);
|
|
3382
|
-
if (!refreshedBuckets.find((entry) => entry?.name === bucketName)) {
|
|
3383
|
-
throw new Error(`Unable to resolve Cloudflare R2 bucket ${bucketName} after reconciliation.`);
|
|
3384
|
-
}
|
|
3385
|
-
};
|
|
3386
|
-
const ensurePagesProject = () => {
|
|
3387
|
-
const current = state.pages;
|
|
3388
|
-
if (!current?.projectName) {
|
|
3389
|
-
return;
|
|
3390
|
-
}
|
|
3391
|
-
const exists = pagesProjects.find((entry) => entry?.name === current.projectName);
|
|
3392
|
-
if (exists) {
|
|
3393
|
-
current.url = exists.subdomain ? `https://${exists.subdomain}` : current.url ?? `https://${current.projectName}.pages.dev`;
|
|
3394
|
-
ensurePagesProjectCompatibility(env.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "", current.projectName, env, exists, { state, target });
|
|
3395
|
-
return;
|
|
3396
|
-
}
|
|
3397
|
-
if (planOnly) {
|
|
3398
|
-
current.url = `https://${current.projectName}.pages.dev`;
|
|
3399
|
-
return;
|
|
3400
|
-
}
|
|
3401
|
-
runWrangler([
|
|
3402
|
-
"pages",
|
|
3403
|
-
"project",
|
|
3404
|
-
"create",
|
|
3405
|
-
current.projectName,
|
|
3406
|
-
"--production-branch",
|
|
3407
|
-
target.kind === "persistent" && target.scope === "prod" ? current.productionBranch ?? "main" : current.stagingBranch ?? "staging"
|
|
3408
|
-
], {
|
|
3409
|
-
cwd: tenantRoot,
|
|
3410
|
-
capture: true,
|
|
3411
|
-
env
|
|
3412
|
-
});
|
|
3413
|
-
ensurePagesProjectCompatibility(env.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "", current.projectName, env, null, { state, target });
|
|
3414
|
-
current.url = `https://${current.projectName}.pages.dev`;
|
|
3415
|
-
};
|
|
3416
|
-
ensureKv("FORM_GUARD_KV");
|
|
3417
|
-
ensureD1();
|
|
3418
|
-
ensureR2Bucket();
|
|
3419
|
-
ensurePagesProject();
|
|
3420
|
-
reconcileCloudflareWebCacheRules(tenantRoot, deployConfig, state, target, { planOnly });
|
|
3421
|
-
state.readiness.configured = true;
|
|
3422
|
-
state.readiness.provisioned = hasProvisionedCloudflareResources(state);
|
|
3423
|
-
state.readiness.deployable = state.readiness.provisioned === true;
|
|
3424
|
-
state.readiness.phase = state.readiness.provisioned === true ? "provisioned" : "config_complete";
|
|
3425
|
-
state.readiness.initialized = true;
|
|
3426
|
-
state.readiness.initializedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3427
|
-
state.readiness.lastValidatedAt = state.readiness.initializedAt;
|
|
3428
|
-
state.readiness.blockers = [];
|
|
3429
|
-
state.readiness.warnings = [];
|
|
3430
|
-
state.readiness.lastValidationSummary = {
|
|
3431
|
-
cloudflare: state.readiness.provisioned === true ? "ready" : "incomplete",
|
|
3432
|
-
railway: "configured"
|
|
3433
|
-
};
|
|
3434
|
-
writeDeployState(tenantRoot, state, { target });
|
|
3435
|
-
return buildProvisioningSummary(deployConfig, state, target);
|
|
3436
|
-
}
|
|
3437
|
-
function syncCloudflareSecrets(tenantRoot, options = {}) {
|
|
3438
|
-
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
3439
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
3440
|
-
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
3441
|
-
const env = {
|
|
3442
|
-
CLOUDFLARE_ACCOUNT_ID: resolveConfiguredCloudflareAccountId(deployConfig)
|
|
3443
|
-
};
|
|
3444
|
-
const entryFilter = Array.isArray(options.entryIds) && options.entryIds.length > 0 ? new Set(options.entryIds) : null;
|
|
3445
|
-
const extraSecrets = options.extraSecrets && typeof options.extraSecrets === "object" ? Object.fromEntries(Object.entries(options.extraSecrets).filter(([key, value]) => (!entryFilter || entryFilter.has(key)) && typeof value === "string" && value.length > 0)) : {};
|
|
3446
|
-
const secrets = {
|
|
3447
|
-
...buildSecretMap(deployConfig, state),
|
|
3448
|
-
...extraSecrets
|
|
3449
|
-
};
|
|
3450
|
-
const synced = [];
|
|
3451
|
-
const planOnly = options.planOnly ?? false;
|
|
3452
|
-
for (const [key, value] of Object.entries(secrets)) {
|
|
3453
|
-
if (!value) {
|
|
3454
|
-
continue;
|
|
3455
|
-
}
|
|
3456
|
-
synced.push(key);
|
|
3457
|
-
if (planOnly) {
|
|
3458
|
-
continue;
|
|
3459
|
-
}
|
|
3460
|
-
const command = state.pages?.projectName && target.kind === "persistent" ? [resolveWranglerBin(), "pages", "secret", "put", key, "--project-name", state.pages.projectName] : [resolveWranglerBin(), "secret", "put", key, "--config", resolveGeneratedWranglerPath(tenantRoot, { target })];
|
|
3461
|
-
const result = spawnSync(process.execPath, command, {
|
|
3462
|
-
cwd: tenantRoot,
|
|
3463
|
-
input: `${value}
|
|
3464
|
-
`,
|
|
3465
|
-
stdio: ["pipe", "inherit", "inherit"],
|
|
3466
|
-
env: { ...process.env, ...env },
|
|
3467
|
-
encoding: "utf8"
|
|
3468
|
-
});
|
|
3469
|
-
if (result.status !== 0) {
|
|
3470
|
-
throw new Error(`Failed to sync secret ${key}.`);
|
|
3471
|
-
}
|
|
3472
|
-
}
|
|
3473
|
-
state.generatedSecrets = {
|
|
3474
|
-
...state.generatedSecrets ?? {},
|
|
3475
|
-
TREESEED_FORM_TOKEN_SECRET: secrets.TREESEED_FORM_TOKEN_SECRET ?? state.generatedSecrets?.TREESEED_FORM_TOKEN_SECRET,
|
|
3476
|
-
TREESEED_EDITORIAL_PREVIEW_SECRET: secrets.TREESEED_EDITORIAL_PREVIEW_SECRET ?? state.generatedSecrets?.TREESEED_EDITORIAL_PREVIEW_SECRET
|
|
3477
|
-
};
|
|
3478
|
-
writeDeployState(tenantRoot, state, { target });
|
|
3479
|
-
return synced;
|
|
3480
|
-
}
|
|
3481
|
-
function verifyProvisionedCloudflareResources(tenantRoot, options = {}) {
|
|
3482
|
-
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
3483
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
3484
|
-
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
3485
|
-
const env = {
|
|
3486
|
-
CLOUDFLARE_ACCOUNT_ID: resolveConfiguredCloudflareAccountId(deployConfig)
|
|
3487
|
-
};
|
|
3488
|
-
const planOnly = options.planOnly ?? false;
|
|
3489
|
-
const kvNamespaces = planOnly ? [] : listKvNamespaces(tenantRoot, env);
|
|
3490
|
-
const d1Databases = planOnly ? [] : listD1Databases(tenantRoot, env);
|
|
3491
|
-
const queues = planOnly ? [] : listQueues(tenantRoot, env);
|
|
3492
|
-
const buckets = planOnly ? [] : listR2Buckets(tenantRoot, env);
|
|
3493
|
-
const pagesProjects = planOnly ? [] : listPagesProjects(tenantRoot, env);
|
|
3494
|
-
const livePages = pagesProjects.find((entry) => entry?.name === state.pages?.projectName);
|
|
3495
|
-
const pagesProject = planOnly || !env.CLOUDFLARE_ACCOUNT_ID || !state.pages?.projectName ? livePages : cloudflareApiRequest(
|
|
3496
|
-
`/accounts/${encodeURIComponent(env.CLOUDFLARE_ACCOUNT_ID)}/pages/projects/${encodeURIComponent(state.pages.projectName)}`,
|
|
3497
|
-
{ env, allowFailure: true }
|
|
3498
|
-
)?.result ?? livePages;
|
|
3499
|
-
const pagesConfigKey = target.kind === "persistent" && target.scope === "prod" ? "production" : "preview";
|
|
3500
|
-
const pagesConfig = pagesProject?.deployment_configs?.[pagesConfigKey] ?? {};
|
|
3501
|
-
const pagesBindings = buildCloudflarePagesFunctionBindings(state);
|
|
3502
|
-
const pageBindingConfigured = (configKey, binding, expected) => pagesConfig?.[configKey]?.[binding] && Object.entries(expected).every(([key, value]) => pagesConfig[configKey][binding]?.[key] === value);
|
|
3503
|
-
const checks = {
|
|
3504
|
-
pages: Boolean(state.pages?.projectName && (livePages || pagesProject?.name === state.pages.projectName)),
|
|
3505
|
-
formGuardKv: Boolean(state.kvNamespaces?.FORM_GUARD_KV?.name && kvNamespaces.find((entry) => entry?.title === state.kvNamespaces.FORM_GUARD_KV.name)),
|
|
3506
|
-
d1: Boolean(state.d1Databases?.SITE_DATA_DB?.databaseName && d1Databases.find((entry) => entry?.name === state.d1Databases.SITE_DATA_DB.databaseName)),
|
|
3507
|
-
r2: Boolean(state.content?.bucketName && buckets.find((entry) => entry?.name === state.content.bucketName)),
|
|
3508
|
-
pagesFormGuardKvBinding: !pagesBindings.kv_namespaces?.FORM_GUARD_KV || pageBindingConfigured("kv_namespaces", "FORM_GUARD_KV", pagesBindings.kv_namespaces.FORM_GUARD_KV),
|
|
3509
|
-
pagesD1Binding: !pagesBindings.d1_databases?.SITE_DATA_DB || pageBindingConfigured("d1_databases", "SITE_DATA_DB", pagesBindings.d1_databases.SITE_DATA_DB),
|
|
3510
|
-
pagesR2Binding: !state.content?.r2Binding || !pagesBindings.r2_buckets?.[state.content.r2Binding] || pageBindingConfigured("r2_buckets", state.content.r2Binding, pagesBindings.r2_buckets[state.content.r2Binding]),
|
|
3511
|
-
webCache: !shouldManageCloudflareWebCacheRules(deployConfig, target) || state.webCache?.rulesManaged === true
|
|
3512
|
-
};
|
|
3513
|
-
const ok = planOnly ? true : Object.values(checks).every(Boolean);
|
|
3514
|
-
state.readiness.configured = true;
|
|
3515
|
-
state.readiness.provisioned = ok;
|
|
3516
|
-
state.readiness.deployable = ok;
|
|
3517
|
-
state.readiness.phase = ok ? "provisioned" : "config_complete";
|
|
3518
|
-
state.readiness.lastValidatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3519
|
-
state.readiness.blockers = [];
|
|
3520
|
-
state.readiness.warnings = [];
|
|
3521
|
-
state.readiness.lastValidationSummary = checks;
|
|
3522
|
-
if (state.pages) {
|
|
3523
|
-
const configuredWebUrl = resolveConfiguredSurfaceBaseUrl(deployConfig, target, "web");
|
|
3524
|
-
if (configuredWebUrl) {
|
|
3525
|
-
state.pages.url = configuredWebUrl;
|
|
3526
|
-
} else if (livePages?.subdomain) {
|
|
3527
|
-
state.pages.url = target.kind === "persistent" && target.scope === "staging" ? `https://${state.pages.stagingBranch ?? "staging"}.${livePages.subdomain}` : `https://${livePages.subdomain}`;
|
|
3528
|
-
}
|
|
3529
|
-
}
|
|
3530
|
-
if (!planOnly) {
|
|
3531
|
-
try {
|
|
3532
|
-
reconcileCloudflareWebCacheRules(tenantRoot, deployConfig, state, target, { planOnly: false });
|
|
3533
|
-
} catch (error) {
|
|
3534
|
-
state.webCache.rulesManaged = false;
|
|
3535
|
-
state.webCache.lastError = error instanceof Error ? error.message : String(error);
|
|
3536
|
-
}
|
|
3537
|
-
}
|
|
3538
|
-
state.webCache.lastVerifiedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3539
|
-
writeDeployState(tenantRoot, state, { target });
|
|
3540
|
-
return {
|
|
3541
|
-
ok,
|
|
3542
|
-
target: deployTargetLabel(target),
|
|
3543
|
-
checks,
|
|
3544
|
-
state
|
|
3545
|
-
};
|
|
3546
|
-
}
|
|
3547
|
-
function runRemoteD1Migrations(tenantRoot, options = {}) {
|
|
3548
|
-
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
3549
|
-
const { wranglerPath, deployConfig, state } = ensureGeneratedWranglerConfig(tenantRoot, { target });
|
|
3550
|
-
if (options.planOnly) {
|
|
3551
|
-
return { databaseName: state.d1Databases.SITE_DATA_DB.databaseName, planOnly: true };
|
|
3552
|
-
}
|
|
3553
|
-
const args = ["d1", "migrations", "apply", state.d1Databases.SITE_DATA_DB.databaseName, "--remote", "--config", wranglerPath];
|
|
3554
|
-
const env = { CLOUDFLARE_ACCOUNT_ID: resolveConfiguredCloudflareAccountId(deployConfig) };
|
|
3555
|
-
const isTransient = (output) => /fetch failed|timed out|etimedout|econnreset|enetunreach|temporarily unavailable|aborted|internal error/i.test(output || "");
|
|
3556
|
-
let lastOutput = "";
|
|
3557
|
-
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
3558
|
-
const result = runWrangler(args, {
|
|
3559
|
-
cwd: tenantRoot,
|
|
3560
|
-
env,
|
|
3561
|
-
capture: true,
|
|
3562
|
-
allowFailure: true
|
|
3563
|
-
});
|
|
3564
|
-
if (result.status === 0) {
|
|
3565
|
-
return { databaseName: state.d1Databases.SITE_DATA_DB.databaseName, planOnly: false };
|
|
3566
|
-
}
|
|
3567
|
-
lastOutput = [result.stderr?.trim(), result.stdout?.trim()].filter(Boolean).join("\n");
|
|
3568
|
-
if (!isTransient(lastOutput) || attempt === 3) {
|
|
3569
|
-
throw new Error(lastOutput || `Wrangler command failed: ${args.join(" ")}`);
|
|
3570
|
-
}
|
|
3571
|
-
sleepSync(2e3 * attempt);
|
|
3572
|
-
}
|
|
3573
|
-
throw new Error(lastOutput || `Wrangler command failed: ${args.join(" ")}`);
|
|
3574
|
-
}
|
|
3575
|
-
function markDeploymentInitialized(tenantRoot, options = {}) {
|
|
3576
|
-
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
3577
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
3578
|
-
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
3579
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3580
|
-
state.readiness.initialized = true;
|
|
3581
|
-
state.readiness.configured = true;
|
|
3582
|
-
state.readiness.provisioned = hasProvisionedCloudflareResources(state);
|
|
3583
|
-
state.readiness.deployable = state.readiness.provisioned === true;
|
|
3584
|
-
state.readiness.phase = state.readiness.provisioned === true ? "provisioned" : "config_complete";
|
|
3585
|
-
state.readiness.initializedAt = state.readiness.initializedAt ?? timestamp;
|
|
3586
|
-
state.readiness.lastValidatedAt = timestamp;
|
|
3587
|
-
state.readiness.lastConfigFingerprint = state.lastManifestFingerprint ?? state.readiness.lastConfigFingerprint;
|
|
3588
|
-
state.readiness.blockers = [];
|
|
3589
|
-
state.readiness.warnings = [];
|
|
3590
|
-
writeDeployState(tenantRoot, state, { target });
|
|
3591
|
-
return state;
|
|
3592
|
-
}
|
|
3593
|
-
function markManagedServicesInitialized(tenantRoot, options = {}) {
|
|
3594
|
-
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
3595
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
3596
|
-
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
3597
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3598
|
-
for (const serviceKey of MANAGED_SERVICE_KEYS) {
|
|
3599
|
-
if (!state.services?.[serviceKey]?.enabled) {
|
|
3600
|
-
continue;
|
|
3601
|
-
}
|
|
3602
|
-
state.services[serviceKey].initialized = true;
|
|
3603
|
-
state.services[serviceKey].lastDeploymentTimestamp = state.services[serviceKey].lastDeploymentTimestamp ?? timestamp;
|
|
3604
|
-
state.services[serviceKey].lastDeployedUrl = state.services[serviceKey].lastDeployedUrl ?? state.services[serviceKey].publicBaseUrl ?? null;
|
|
3605
|
-
}
|
|
3606
|
-
writeDeployState(tenantRoot, state, { target });
|
|
3607
|
-
return state;
|
|
3608
|
-
}
|
|
3609
|
-
function recordHostedDeploymentState(tenantRoot, options = {}) {
|
|
3610
|
-
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
3611
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
3612
|
-
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
3613
|
-
const timestamp = typeof options.timestamp === "string" && options.timestamp.trim() ? options.timestamp.trim() : (/* @__PURE__ */ new Date()).toISOString();
|
|
3614
|
-
const deployedUrl = typeof options.url === "string" && options.url.trim() ? options.url.trim() : state.lastDeployedUrl ?? resolveConfiguredSurfaceBaseUrl(deployConfig, target, "web");
|
|
3615
|
-
const commit = typeof options.commit === "string" && options.commit.trim() ? options.commit.trim() : null;
|
|
3616
|
-
state.lastDeployedUrl = deployedUrl;
|
|
3617
|
-
state.lastDeploymentTimestamp = timestamp;
|
|
3618
|
-
state.lastDeployedCommit = commit;
|
|
3619
|
-
state.readiness = {
|
|
3620
|
-
...state.readiness ?? {},
|
|
3621
|
-
initialized: true,
|
|
3622
|
-
configured: true,
|
|
3623
|
-
provisioned: true,
|
|
3624
|
-
deployable: true,
|
|
3625
|
-
phase: "provisioned",
|
|
3626
|
-
initializedAt: state.readiness?.initializedAt ?? timestamp,
|
|
3627
|
-
lastValidatedAt: timestamp,
|
|
3628
|
-
blockers: [],
|
|
3629
|
-
warnings: state.readiness?.warnings ?? []
|
|
3630
|
-
};
|
|
3631
|
-
const nextHistoryEntry = {
|
|
3632
|
-
commit,
|
|
3633
|
-
timestamp,
|
|
3634
|
-
url: deployedUrl,
|
|
3635
|
-
target: deployTargetLabel(target),
|
|
3636
|
-
source: options.source ?? "hosted-github-workflow",
|
|
3637
|
-
workflow: options.workflow ?? null,
|
|
3638
|
-
runId: options.runId ?? null
|
|
3639
|
-
};
|
|
3640
|
-
const history = Array.isArray(state.deploymentHistory) ? state.deploymentHistory : [];
|
|
3641
|
-
state.deploymentHistory = [...history, nextHistoryEntry].slice(-20);
|
|
3642
|
-
writeDeployState(tenantRoot, state, { target });
|
|
3643
|
-
return state;
|
|
3644
|
-
}
|
|
3645
|
-
function assertDeploymentInitialized(tenantRoot, options = {}) {
|
|
3646
|
-
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
3647
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
3648
|
-
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
3649
|
-
if (state.readiness?.initialized) {
|
|
3650
|
-
return state;
|
|
3651
|
-
}
|
|
3652
|
-
throw new Error(
|
|
3653
|
-
`Treeseed environment ${deployTargetLabel(target)} has not been initialized. Run \`treeseed config --environment ${scopeFromTarget(target)}\` first.`
|
|
3654
|
-
);
|
|
3655
|
-
}
|
|
3656
|
-
function finalizeDeploymentState(tenantRoot, options = {}) {
|
|
3657
|
-
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
3658
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
3659
|
-
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
3660
|
-
state.lastManifestFingerprint = stableHash(JSON.stringify({ deployConfig, targetKey: targetKey(target) }));
|
|
3661
|
-
state.lastDeployedUrl = target.kind === "branch" ? targetWorkersDevUrl(state.workerName) : resolveConfiguredSurfaceBaseUrl(deployConfig, target, "web");
|
|
3662
|
-
state.lastDeploymentTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3663
|
-
state.lastDeployedCommit = envOrNull("GITHUB_SHA") ?? envOrNull("TREESEED_DEPLOY_COMMIT") ?? null;
|
|
3664
|
-
state.runtimeCompatibility = {
|
|
3665
|
-
envelopeSchemaGeneration: TRESEED_ENVELOPE_SCHEMA_GENERATION,
|
|
3666
|
-
migrationWaveId: TRESEED_MIGRATION_WAVE_ID,
|
|
3667
|
-
supportedPayloadVersionRange: TRESEED_SUPPORTED_PAYLOAD_RANGE
|
|
3668
|
-
};
|
|
3669
|
-
const nextHistoryEntry = {
|
|
3670
|
-
commit: state.lastDeployedCommit,
|
|
3671
|
-
timestamp: state.lastDeploymentTimestamp,
|
|
3672
|
-
url: state.lastDeployedUrl,
|
|
3673
|
-
target: deployTargetLabel(target),
|
|
3674
|
-
appVersion: envOrNull("npm_package_version") ?? envOrNull("TREESEED_APP_VERSION") ?? null,
|
|
3675
|
-
envelopeSchemaGeneration: TRESEED_ENVELOPE_SCHEMA_GENERATION,
|
|
3676
|
-
migrationWaveId: TRESEED_MIGRATION_WAVE_ID,
|
|
3677
|
-
supportedPayloadVersionRange: TRESEED_SUPPORTED_PAYLOAD_RANGE
|
|
3678
|
-
};
|
|
3679
|
-
const history = Array.isArray(state.deploymentHistory) ? state.deploymentHistory : [];
|
|
3680
|
-
state.deploymentHistory = [...history, nextHistoryEntry].slice(-20);
|
|
3681
|
-
state.readiness.initialized = true;
|
|
3682
|
-
state.readiness.configured = true;
|
|
3683
|
-
state.readiness.provisioned = hasProvisionedCloudflareResources(state);
|
|
3684
|
-
state.readiness.deployable = state.readiness.provisioned === true;
|
|
3685
|
-
state.readiness.phase = state.readiness.provisioned === true ? "provisioned" : "config_complete";
|
|
3686
|
-
state.readiness.lastValidatedAt = state.lastDeploymentTimestamp;
|
|
3687
|
-
state.readiness.blockers = [];
|
|
3688
|
-
state.readiness.warnings = [];
|
|
3689
|
-
for (const result of options.serviceResults ?? []) {
|
|
3690
|
-
if (!result?.service || !state.services?.[result.service]) {
|
|
3691
|
-
continue;
|
|
3692
|
-
}
|
|
3693
|
-
state.services[result.service].initialized = true;
|
|
3694
|
-
state.services[result.service].lastDeploymentTimestamp = state.lastDeploymentTimestamp;
|
|
3695
|
-
state.services[result.service].lastDeployedUrl = result.publicBaseUrl ?? state.services[result.service].publicBaseUrl ?? state.services[result.service].lastDeployedUrl ?? null;
|
|
3696
|
-
state.services[result.service].lastDeploymentCommand = result.command ?? null;
|
|
3697
|
-
}
|
|
3698
|
-
writeDeployState(tenantRoot, state, { target });
|
|
3699
|
-
if (target.kind === "persistent") {
|
|
3700
|
-
try {
|
|
3701
|
-
const purgeResult = purgeSourcePageCaches(tenantRoot, { target, env: options.env });
|
|
3702
|
-
if (target.scope === "prod" && purgeResult?.skipped) {
|
|
3703
|
-
throw new Error(`Production source-page cache purge was skipped: ${purgeResult.reason ?? "unknown"}.`);
|
|
3704
|
-
}
|
|
3705
|
-
} catch (error) {
|
|
3706
|
-
if (target.scope === "prod") {
|
|
3707
|
-
throw error;
|
|
3708
|
-
}
|
|
3709
|
-
}
|
|
3710
|
-
return loadDeployState(tenantRoot, deployConfig, { target });
|
|
3711
|
-
}
|
|
3712
|
-
return state;
|
|
3713
|
-
}
|
|
3714
|
-
function printDeploySummary(summary) {
|
|
3715
|
-
console.log("Treeseed deployment summary");
|
|
3716
|
-
console.log(` Target: ${summary.target}`);
|
|
3717
|
-
console.log(` Worker: ${summary.workerName}`);
|
|
3718
|
-
console.log(` Site URL: ${summary.siteUrl}`);
|
|
3719
|
-
console.log(` Account ID: ${summary.accountId}`);
|
|
3720
|
-
console.log(` D1: ${summary.siteDataDb.databaseName} (${summary.siteDataDb.databaseId})`);
|
|
3721
|
-
console.log(` KV FORM_GUARD_KV: ${summary.formGuardKv.id}`);
|
|
3722
|
-
}
|
|
3723
|
-
function printDestroySummary(result) {
|
|
3724
|
-
const { summary, operations } = result;
|
|
3725
|
-
const cloudflare = Array.isArray(operations?.cloudflare) ? operations.cloudflare : null;
|
|
3726
|
-
const legacy = cloudflare ? {
|
|
3727
|
-
worker: cloudflare.find((entry) => entry.type === "worker"),
|
|
3728
|
-
database: cloudflare.find((entry) => entry.type === "d1-database"),
|
|
3729
|
-
formGuard: cloudflare.find((entry) => entry.type === "kv-namespace"),
|
|
3730
|
-
formGuardPreview: cloudflare.find((entry) => entry.type === "kv-namespace-preview"),
|
|
3731
|
-
session: cloudflare.find((entry) => entry.type === "kv-namespace" && entry.name === summary.sessionKv?.name),
|
|
3732
|
-
sessionPreview: cloudflare.find((entry) => entry.type === "kv-namespace-preview" && entry.name === summary.sessionKv?.name)
|
|
3733
|
-
} : operations;
|
|
3734
|
-
console.log("Treeseed destroy summary");
|
|
3735
|
-
console.log(` Target: ${summary.target}`);
|
|
3736
|
-
console.log(` Worker: ${summary.workerName} -> ${legacy.worker?.status ?? "unknown"}`);
|
|
3737
|
-
console.log(` Site URL: ${summary.siteUrl}`);
|
|
3738
|
-
console.log(` Account ID: ${summary.accountId}`);
|
|
3739
|
-
console.log(` D1: ${summary.siteDataDb.databaseName} -> ${legacy.database?.status ?? "unknown"}`);
|
|
3740
|
-
console.log(` KV FORM_GUARD_KV: ${summary.formGuardKv.name} -> ${legacy.formGuard?.status ?? "unknown"}`);
|
|
3741
|
-
if (legacy.formGuardPreview) {
|
|
3742
|
-
console.log(` KV FORM_GUARD_KV preview -> ${legacy.formGuardPreview.status}`);
|
|
3743
|
-
}
|
|
3744
|
-
if (summary.sessionKv && legacy.session) {
|
|
3745
|
-
console.log(` KV SESSION (deprecated): ${summary.sessionKv.name} -> ${legacy.session.status}`);
|
|
3746
|
-
}
|
|
3747
|
-
if (legacy.sessionPreview) {
|
|
3748
|
-
console.log(` KV SESSION preview -> ${legacy.sessionPreview.status}`);
|
|
3749
|
-
}
|
|
3750
|
-
if (cloudflare) {
|
|
3751
|
-
for (const entry of [
|
|
3752
|
-
...cloudflare.filter((item) => !["worker", "d1-database", "kv-namespace", "kv-namespace-preview"].includes(item.type)),
|
|
3753
|
-
...Array.isArray(operations?.railway) ? operations.railway : [],
|
|
3754
|
-
...Array.isArray(operations?.local) ? operations.local : []
|
|
3755
|
-
]) {
|
|
3756
|
-
console.log(` ${entry.provider} ${entry.type} ${entry.name ?? "(none)"} -> ${entry.status}`);
|
|
3757
|
-
}
|
|
3758
|
-
}
|
|
3759
|
-
}
|
|
3760
|
-
export {
|
|
3761
|
-
assertDeploymentInitialized,
|
|
3762
|
-
buildCloudflarePagesFunctionBindings,
|
|
3763
|
-
buildProvisioningSummary,
|
|
3764
|
-
buildPublicVars,
|
|
3765
|
-
buildSecretMap,
|
|
3766
|
-
buildTreeseedManagedCloudflareCacheRules,
|
|
3767
|
-
buildWranglerConfigContents,
|
|
3768
|
-
cleanupDestroyedState,
|
|
3769
|
-
cloudflareApiRequest,
|
|
3770
|
-
cloudflareDestroyVerification,
|
|
3771
|
-
collectMissingDeployInputs,
|
|
3772
|
-
createBranchPreviewDeployTarget,
|
|
3773
|
-
createPersistentDeployTarget,
|
|
3774
|
-
createTurnstileWidget,
|
|
3775
|
-
deployTargetLabel,
|
|
3776
|
-
deriveTreeseedStagingSurfaceDomain,
|
|
3777
|
-
destroyCloudflareResources,
|
|
3778
|
-
destroyTreeseedEnvironmentResources,
|
|
3779
|
-
dockerLocalRuntimeResourceOperations,
|
|
3780
|
-
ensureGeneratedWranglerConfig,
|
|
3781
|
-
finalizeDeploymentState,
|
|
3782
|
-
getTurnstileWidget,
|
|
3783
|
-
hasProvisionedCloudflareResources,
|
|
3784
|
-
isWranglerAlreadyExistsError,
|
|
3785
|
-
listD1Databases,
|
|
3786
|
-
listDnsRecords,
|
|
3787
|
-
listDnsZones,
|
|
3788
|
-
listKvNamespaces,
|
|
3789
|
-
listPagesProjects,
|
|
3790
|
-
listQueues,
|
|
3791
|
-
listR2Buckets,
|
|
3792
|
-
listTurnstileWidgets,
|
|
3793
|
-
listWorkers,
|
|
3794
|
-
loadDeployState,
|
|
3795
|
-
markDeploymentInitialized,
|
|
3796
|
-
markManagedServicesInitialized,
|
|
3797
|
-
mergeCloudflarePagesDeploymentConfig,
|
|
3798
|
-
normalizePersistentScope,
|
|
3799
|
-
printDeploySummary,
|
|
3800
|
-
printDestroySummary,
|
|
3801
|
-
promptForMissingDeployInputs,
|
|
3802
|
-
provisionCloudflareResources,
|
|
3803
|
-
purgePublishedContentCaches,
|
|
3804
|
-
purgeSourcePageCaches,
|
|
3805
|
-
queueId,
|
|
3806
|
-
queueName,
|
|
3807
|
-
reconcileCloudflareWebCacheRules,
|
|
3808
|
-
recordHostedDeploymentState,
|
|
3809
|
-
resolveCloudflareZoneIdForHost,
|
|
3810
|
-
resolveConfiguredCloudflareAccountId,
|
|
3811
|
-
resolveConfiguredSurfaceBaseUrl,
|
|
3812
|
-
resolveConfiguredSurfaceDomain,
|
|
3813
|
-
resolveGeneratedWranglerPath,
|
|
3814
|
-
resolveTreeseedResourceIdentity,
|
|
3815
|
-
runRemoteD1Migrations,
|
|
3816
|
-
runWrangler,
|
|
3817
|
-
scopeFromTarget,
|
|
3818
|
-
setDestroyDockerRunnerForTests,
|
|
3819
|
-
shouldDeleteRailwayProjectAfterEnvironmentDestroy,
|
|
3820
|
-
syncCloudflareSecrets,
|
|
3821
|
-
updateTurnstileWidget,
|
|
3822
|
-
validateDeployPrerequisites,
|
|
3823
|
-
validateDestroyPrerequisites,
|
|
3824
|
-
verifyProvisionedCloudflareResources,
|
|
3825
|
-
writeDeployState
|
|
3826
|
-
};
|