@empire-builder-kit/nx 1.0.0-rc.38 → 1.0.0-rc.39
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/README.md +18 -0
- package/executors.json +5 -0
- package/generators.json +10 -0
- package/package.json +20 -3
- package/src/aws-authority/index.d.ts +1 -0
- package/src/aws-authority/index.js +1 -0
- package/src/aws-authority/lambda-policy-ordering.d.ts +38 -0
- package/src/aws-authority/lambda-policy-ordering.js +56 -0
- package/src/aws-authority/stage-authority.d.ts +11 -3
- package/src/aws-authority/stage-authority.js +615 -29
- package/src/blueprints/external-sandbox.js +21 -9
- package/src/blueprints/manifest.d.ts +1 -1
- package/src/blueprints/resolver.js +28 -15
- package/src/cli/bootstrap.d.ts +1 -1
- package/src/cli/deploy-runtime.d.ts +6 -0
- package/src/cli/deploy-runtime.js +58 -0
- package/src/cli/dev-command.d.ts +16 -4
- package/src/cli/dev-command.js +63 -0
- package/src/cli/main.d.ts +7 -1
- package/src/cli/main.js +128 -20
- package/src/cli/output.d.ts +1 -1
- package/src/developer-agent/context.js +41 -9
- package/src/developer-agent/generator-planning.js +16 -2
- package/src/developer-agent/git-inspection.d.ts +6 -2
- package/src/developer-agent/git-inspection.js +78 -6
- package/src/developer-agent/model.d.ts +4 -2
- package/src/developer-agent/operation-registry.d.ts +5 -0
- package/src/developer-agent/operation-registry.js +179 -23
- package/src/developer-agent/preview-changes.d.ts +2 -0
- package/src/developer-agent/preview-changes.js +14 -2
- package/src/developer-agent/preview-source.js +3 -0
- package/src/developer-agent/validation.js +20 -8
- package/src/developer-agent/workspace-inspection.js +37 -3
- package/src/executors/deployment-plan/deployment-plan.d.ts +2 -0
- package/src/executors/deployment-plan/deployment-plan.js +51 -4
- package/src/executors/dev-doctor/dev-doctor.js +13 -6
- package/src/executors/dev-task-cleanup/dev-task-cleanup.d.ts +23 -0
- package/src/executors/dev-task-cleanup/dev-task-cleanup.js +145 -0
- package/src/executors/dev-task-cleanup/schema.json +9 -0
- package/src/executors/dev-task-cleanup/task-stack-absence.d.ts +27 -0
- package/src/executors/dev-task-cleanup/task-stack-absence.js +281 -0
- package/src/executors/dev-task-cleanup/task-stack-checkpoint.d.ts +12 -0
- package/src/executors/dev-task-cleanup/task-stack-checkpoint.js +92 -0
- package/src/executors/fleet/fleet.js +1 -1
- package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js +28 -1
- package/src/executors/local-deploy/local-deploy.d.ts +8 -1
- package/src/executors/local-deploy/local-deploy.js +97 -24
- package/src/executors/local-deploy/schema.json +8 -0
- package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js +30 -18
- package/src/executors/sst-lifecycle/governed-native-operation.d.ts +2 -0
- package/src/executors/sst-lifecycle/governed-native-operation.js +9 -0
- package/src/executors/sst-lifecycle/run-sst-command.d.ts +5 -0
- package/src/executors/sst-lifecycle/run-sst-command.js +220 -32
- package/src/executors/sst-lifecycle/sst-environment-source.d.ts +8 -0
- package/src/executors/sst-lifecycle/sst-environment-source.js +54 -0
- package/src/executors/sst-lifecycle/task-unlock.d.ts +15 -0
- package/src/executors/sst-lifecycle/task-unlock.js +126 -0
- package/src/foundation/capability-plan.d.ts +43 -0
- package/src/foundation/capability-plan.js +168 -0
- package/src/foundation/discovery.d.ts +14 -11
- package/src/foundation/discovery.js +32 -6
- package/src/foundation/index.d.ts +1 -0
- package/src/foundation/index.js +1 -0
- package/src/foundation/local-database-contract.d.ts +20 -0
- package/src/foundation/local-database-contract.js +262 -13
- package/src/foundation/local-private-smoke-access.d.ts +20 -0
- package/src/foundation/local-private-smoke-access.js +197 -0
- package/src/foundation/preflight.js +37 -18
- package/src/foundation/private-smoke-access.js +10 -2
- package/src/foundation/private-smoke-admission.d.ts +3 -1
- package/src/foundation/private-smoke-admission.js +29 -8
- package/src/foundation/private-smoke-control-plane.js +17 -6
- package/src/foundation/provision-database-bindings.d.ts +5 -0
- package/src/foundation/provision-database-bindings.js +41 -8
- package/src/foundation/provision-logical-database.js +13 -8
- package/src/foundation/requirements.d.ts +3 -3
- package/src/foundation/requirements.js +24 -12
- package/src/foundation/router-waf.d.ts +1 -0
- package/src/foundation/router-waf.js +14 -1
- package/src/generators/blueprint/blueprint.d.ts +5 -0
- package/src/generators/blueprint/blueprint.js +102 -0
- package/src/generators/blueprint/files.d.ts +6 -0
- package/src/generators/blueprint/files.js +217 -0
- package/src/generators/blueprint/schema.d.ts +4 -0
- package/src/generators/blueprint/schema.json +18 -0
- package/src/generators/formatting.js +24 -1
- package/src/generators/logging-registry.d.ts +2 -0
- package/src/generators/logging-registry.js +22 -0
- package/src/generators/preset/workspace-files-adopter-skills.d.ts +7 -0
- package/src/generators/preset/workspace-files-adopter-skills.js +301 -0
- package/src/generators/preset/workspace-files-ci-install.d.ts +2 -0
- package/src/generators/preset/workspace-files-ci-install.js +196 -0
- package/src/generators/preset/workspace-files-developer-agent.js +40 -13
- package/src/generators/preset/workspace-files-performance-load.js +8 -0
- package/src/generators/preset/workspace-files-stage-authority.js +40 -1
- package/src/generators/preset/workspace-files-thin-deploy.js +66 -34
- package/src/generators/preset/workspace-files-thin-docs.js +64 -18
- package/src/generators/preset/workspace-files.d.ts +4 -0
- package/src/generators/preset/workspace-files.js +159 -91
- package/src/generators/repository-policy/repository-policy.js +37 -0
- package/src/generators/slice/built-in-plan-api-contract-files.js +42 -7
- package/src/generators/slice/built-in-plan-api-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-api-files.js +47 -389
- package/src/generators/slice/built-in-plan-async-work-files.js +19 -1912
- package/src/generators/slice/built-in-plan-authorization-files.js +92 -329
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +36 -2
- package/src/generators/slice/built-in-plan-authorization-storage.d.ts +3 -0
- package/src/generators/slice/built-in-plan-authorization-storage.js +119 -0
- package/src/generators/slice/built-in-plan-background-work-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-background-work-files.js +15 -5
- package/src/generators/slice/built-in-plan-data-deletion-files.js +2 -830
- package/src/generators/slice/built-in-plan-data-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-data-files.js +61 -1164
- package/src/generators/slice/built-in-plan-data-lifecycle-files.js +53 -28
- package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js +18 -2
- package/src/generators/slice/built-in-plan-data-retention-job-files.js +7 -2446
- package/src/generators/slice/built-in-plan-data-retention-launcher-files.js +30 -6
- package/src/generators/slice/built-in-plan-data-rights-files.js +6 -4482
- package/src/generators/slice/built-in-plan-database-lifecycle-files.js +4 -15
- package/src/generators/slice/built-in-plan-foundation-files.d.ts +2 -1
- package/src/generators/slice/built-in-plan-foundation-files.js +34 -10
- package/src/generators/slice/built-in-plan-frontend-files.js +2 -2
- package/src/generators/slice/built-in-plan-infrastructure-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-infrastructure-files.js +180 -522
- package/src/generators/slice/built-in-plan-key-value-cleanup.d.ts +4 -0
- package/src/generators/slice/built-in-plan-key-value-cleanup.js +100 -0
- package/src/generators/slice/built-in-plan-key-value-consumer.d.ts +4 -0
- package/src/generators/slice/built-in-plan-key-value-consumer.js +151 -0
- package/src/generators/slice/built-in-plan-key-value-deletion.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-deletion.js +377 -0
- package/src/generators/slice/built-in-plan-key-value-delivery.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-delivery.js +120 -0
- package/src/generators/slice/built-in-plan-key-value-export.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-export.js +595 -0
- package/src/generators/slice/built-in-plan-key-value-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-files.js +31 -0
- package/src/generators/slice/built-in-plan-key-value-holds.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-holds.js +189 -0
- package/src/generators/slice/built-in-plan-key-value-infrastructure.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-infrastructure.js +102 -0
- package/src/generators/slice/built-in-plan-key-value-performance.d.ts +5 -0
- package/src/generators/slice/built-in-plan-key-value-performance.js +530 -0
- package/src/generators/slice/built-in-plan-key-value-provider.d.ts +4 -0
- package/src/generators/slice/built-in-plan-key-value-provider.js +366 -0
- package/src/generators/slice/built-in-plan-key-value-records.d.ts +3 -0
- package/src/generators/slice/built-in-plan-key-value-records.js +232 -0
- package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.js +103 -0
- package/src/generators/slice/built-in-plan-key-value-retention-job-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-job-files.js +160 -0
- package/src/generators/slice/built-in-plan-key-value-retention-maintenance.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-maintenance.js +329 -0
- package/src/generators/slice/built-in-plan-key-value-retention-worker.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-worker.js +477 -0
- package/src/generators/slice/built-in-plan-key-value-retention.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention.js +184 -0
- package/src/generators/slice/built-in-plan-metadata-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-metadata-files.js +140 -28
- package/src/generators/slice/built-in-plan-native-runtime-authority-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-native-runtime-authority-files.js +36 -19
- package/src/generators/slice/built-in-plan-operations-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-operations-files.js +89 -50
- package/src/generators/slice/built-in-plan-performance-files.js +84 -1010
- package/src/generators/slice/built-in-plan-project-files.js +116 -67
- package/src/generators/slice/built-in-plan-release-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-release-files.js +64 -15
- package/src/generators/slice/built-in-plan-reliability-files.js +54 -28
- package/src/generators/slice/built-in-plan-shared.d.ts +3 -1
- package/src/generators/slice/built-in-plan-shared.js +40 -18
- package/src/generators/slice/built-in-plan-sql-events.d.ts +2 -0
- package/src/generators/slice/built-in-plan-sql-events.js +257 -0
- package/src/generators/slice/built-in-plan-testing-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-testing-files.js +184 -132
- package/src/generators/slice/built-in-plan-testing-persistence-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-testing-persistence-files.js +178 -0
- package/src/generators/slice/built-in-plan-thin-release-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-thin-release-files.js +35 -2
- package/src/generators/slice/built-in-plan-verification-files.js +70 -0
- package/src/generators/slice/built-in-plan-web-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-web-files.js +191 -14
- package/src/generators/slice/built-in-plan-workload-role-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-workload-role-files.js +113 -274
- package/src/generators/slice/built-in-plan.d.ts +9 -1
- package/src/generators/slice/built-in-plan.js +49 -21
- package/src/generators/slice/data-policy-review-seed.d.ts +2 -0
- package/src/generators/slice/data-policy-review-seed.js +22 -0
- package/src/generators/slice/deployed-smoke-readiness.d.ts +2 -0
- package/src/generators/slice/deployed-smoke-readiness.js +214 -0
- package/src/generators/slice/local-source-identity.d.ts +2 -0
- package/src/generators/slice/local-source-identity.js +31 -0
- package/src/generators/slice/persistence-contract.d.ts +14 -0
- package/src/generators/slice/persistence-contract.js +32 -0
- package/src/generators/slice/slice.js +29 -24
- package/src/generators/sql/schema.d.ts +7 -0
- package/src/generators/sql/schema.json +38 -0
- package/src/generators/sql/sql.d.ts +6 -0
- package/src/generators/sql/sql.js +53 -0
- package/src/generators/upgrade/sql-seeded-reconciliation.d.ts +2 -0
- package/src/generators/upgrade/sql-seeded-reconciliation.js +170 -0
- package/src/generators/upgrade/upgrade.d.ts +2 -0
- package/src/generators/upgrade/upgrade.js +136 -9
- package/src/generators/workload/workload.js +51 -2
- package/src/local-dev/browser-verification.d.ts +37 -0
- package/src/local-dev/browser-verification.js +281 -0
- package/src/local-dev/contracts.d.ts +10 -1
- package/src/local-dev/contracts.js +114 -13
- package/src/local-dev/dev-command-core.js +24 -5
- package/src/local-dev/docker-credential-health.d.ts +14 -0
- package/src/local-dev/docker-credential-health.js +94 -0
- package/src/local-dev/environment-inspector.d.ts +7 -0
- package/src/local-dev/environment-inspector.js +42 -15
- package/src/local-dev/environment-state.js +2 -2
- package/src/local-dev/errors.d.ts +1 -1
- package/src/local-dev/errors.js +2 -0
- package/src/local-dev/fleet-client.js +2 -2
- package/src/local-dev/fleet-configuration.js +57 -25
- package/src/local-dev/fleet-inventory.js +46 -18
- package/src/local-dev/fleet-ipc.d.ts +7 -0
- package/src/local-dev/fleet-ipc.js +14 -2
- package/src/local-dev/fleet-launcher.d.ts +6 -0
- package/src/local-dev/fleet-launcher.js +14 -2
- package/src/local-dev/fleet-logs.js +50 -22
- package/src/local-dev/fleet-process.d.ts +20 -1
- package/src/local-dev/fleet-process.js +197 -41
- package/src/local-dev/fleet-runner-evidence.d.ts +5 -0
- package/src/local-dev/fleet-runner-evidence.js +56 -0
- package/src/local-dev/fleet-runtime.js +268 -229
- package/src/local-dev/fleet-session.d.ts +2 -0
- package/src/local-dev/fleet-session.js +28 -11
- package/src/local-dev/fleet-slice-runner-daemon.js +4 -0
- package/src/local-dev/fleet-state.d.ts +5 -2
- package/src/local-dev/fleet-state.js +25 -4
- package/src/local-dev/fleet-supervisor.d.ts +3 -0
- package/src/local-dev/fleet-supervisor.js +66 -11
- package/src/local-dev/fleet-web-start.d.ts +12 -0
- package/src/local-dev/fleet-web-start.js +48 -0
- package/src/local-dev/identity.js +3 -14
- package/src/local-dev/index.d.ts +1 -0
- package/src/local-dev/index.js +1 -0
- package/src/local-dev/native-completion.d.ts +9 -0
- package/src/local-dev/native-completion.js +130 -0
- package/src/local-dev/nx-workspace.d.ts +4 -2
- package/src/local-dev/nx-workspace.js +11 -6
- package/src/local-dev/personal-stage.d.ts +4 -5
- package/src/local-dev/personal-stage.js +6 -54
- package/src/local-dev/port-claims.d.ts +13 -0
- package/src/local-dev/port-claims.js +46 -0
- package/src/local-dev/ports.d.ts +14 -2
- package/src/local-dev/ports.js +78 -3
- package/src/local-dev/private-state.d.ts +6 -0
- package/src/local-dev/private-state.js +126 -34
- package/src/local-dev/process-group.d.ts +4 -0
- package/src/local-dev/process-group.js +29 -0
- package/src/local-dev/process-identity.d.ts +8 -0
- package/src/local-dev/process-identity.js +31 -1
- package/src/local-dev/proxy.js +11 -12
- package/src/local-dev/runtime-paths.js +1 -1
- package/src/local-dev/slice-claim.js +1 -1
- package/src/local-dev/slice-runner.d.ts +15 -6
- package/src/local-dev/slice-runner.js +189 -54
- package/src/local-dev/task-attachment.d.ts +17 -0
- package/src/local-dev/task-attachment.js +73 -0
- package/src/local-dev/task-authority.d.ts +10 -0
- package/src/local-dev/task-authority.js +27 -0
- package/src/local-dev/task-identity.d.ts +33 -0
- package/src/local-dev/task-identity.js +418 -0
- package/src/local-dev/task-native-writer.d.ts +18 -0
- package/src/local-dev/task-native-writer.js +329 -0
- package/src/local-dev/tls.js +3 -1
- package/src/local-dev/web-launch.d.ts +9 -0
- package/src/local-dev/web-launch.js +130 -0
- package/src/ownership/composition.d.ts +2 -1
- package/src/ownership/composition.js +1 -0
- package/src/performance/scenario.d.ts +22 -0
- package/src/performance/scenario.js +100 -92
- package/src/repository-policy/deployment-plan.d.ts +2 -2
- package/src/repository-policy/deployment-plan.js +2 -1
- package/src/repository-policy/policy.js +38 -3
- package/src/schemas/blueprint-v1.json +5 -4
- package/src/schemas/foundation-requirement-v1.json +5 -4
- package/src/schemas/testing-evidence-result-v1.json +1 -1
- package/src/secrets/operator.d.ts +3 -1
- package/src/secrets/operator.js +22 -3
- package/src/sst-environment-source-format.d.ts +3 -0
- package/src/sst-environment-source-format.js +20 -0
- package/src/supply-chain/blueprint-trust-git.js +13 -3
- package/src/supply-chain/build-output.js +15 -0
- package/src/supply-chain/generated-blueprint-trust-command.js +18 -1
- package/src/testing-evidence/fixture-scope.d.ts +2 -1
- package/src/testing-evidence/fixture-scope.js +4 -2
- package/src/testing-evidence/result.d.ts +2 -1
- package/src/testing-evidence/result.js +18 -4
- package/src/workload-roles/standard-slice.js +2 -3
- package/src/api-contract/baseline-update.js.map +0 -1
- package/src/api-contract/compatibility.js.map +0 -1
- package/src/api-contract/contract-graph.js.map +0 -1
- package/src/api-contract/evidence.js.map +0 -1
- package/src/api-contract/index.js.map +0 -1
- package/src/api-contract/model.fixtures.d.ts +0 -2
- package/src/api-contract/model.fixtures.js +0 -81
- package/src/api-contract/model.fixtures.js.map +0 -1
- package/src/api-contract/model.js.map +0 -1
- package/src/api-contract/rollout.js.map +0 -1
- package/src/authorization/compatibility.js.map +0 -1
- package/src/authorization/index.js.map +0 -1
- package/src/aws/child-environment.js.map +0 -1
- package/src/aws/identity.js.map +0 -1
- package/src/aws-authority/index.js.map +0 -1
- package/src/aws-authority/stage-authority.js.map +0 -1
- package/src/blueprints/execution.js.map +0 -1
- package/src/blueprints/external-sandbox.js.map +0 -1
- package/src/blueprints/index.js.map +0 -1
- package/src/blueprints/inputs.js.map +0 -1
- package/src/blueprints/manifest.js.map +0 -1
- package/src/blueprints/resolver.js.map +0 -1
- package/src/blueprints/transitions.js.map +0 -1
- package/src/cli/agent-command.js.map +0 -1
- package/src/cli/agent-runtime.js.map +0 -1
- package/src/cli/blueprint-authority.js.map +0 -1
- package/src/cli/blueprint-command.js.map +0 -1
- package/src/cli/bootstrap.js.map +0 -1
- package/src/cli/dev-command.js.map +0 -1
- package/src/cli/main.js.map +0 -1
- package/src/cli/output.js.map +0 -1
- package/src/cli/personal-secret-runtime.js.map +0 -1
- package/src/cli/secret-terminal.js.map +0 -1
- package/src/container-images.js.map +0 -1
- package/src/data-lifecycle/index.js.map +0 -1
- package/src/data-lifecycle/model.fixtures.d.ts +0 -2
- package/src/data-lifecycle/model.fixtures.js +0 -244
- package/src/data-lifecycle/model.fixtures.js.map +0 -1
- package/src/data-lifecycle/model.js.map +0 -1
- package/src/data-lifecycle/sensitive-artifacts.js.map +0 -1
- package/src/data-lifecycle/validation.js.map +0 -1
- package/src/database-retirement/contract.js.map +0 -1
- package/src/database-retirement/index.js.map +0 -1
- package/src/deterministic-order.js.map +0 -1
- package/src/developer-agent/context.js.map +0 -1
- package/src/developer-agent/generator-planning.js.map +0 -1
- package/src/developer-agent/generator-preview.js.map +0 -1
- package/src/developer-agent/git-inspection.js.map +0 -1
- package/src/developer-agent/handoff.js.map +0 -1
- package/src/developer-agent/index.js.map +0 -1
- package/src/developer-agent/model.js.map +0 -1
- package/src/developer-agent/operation-registry.js.map +0 -1
- package/src/developer-agent/ordering.js.map +0 -1
- package/src/developer-agent/planning.js.map +0 -1
- package/src/developer-agent/preview-changes.js.map +0 -1
- package/src/developer-agent/preview-source.js.map +0 -1
- package/src/developer-agent/validation.js.map +0 -1
- package/src/developer-agent/workspace-inspection.js.map +0 -1
- package/src/developer-agent/workspace-path.js.map +0 -1
- package/src/executors/blueprint-trust/blueprint-trust.js.map +0 -1
- package/src/executors/blueprint-verification/blueprint-verification.js.map +0 -1
- package/src/executors/deploy/deploy.js.map +0 -1
- package/src/executors/deployment-plan/deployment-plan.js.map +0 -1
- package/src/executors/dev/dev.js.map +0 -1
- package/src/executors/dev-doctor/dev-doctor.js.map +0 -1
- package/src/executors/dev-setup/dev-setup.js.map +0 -1
- package/src/executors/developer-context/developer-context.js.map +0 -1
- package/src/executors/fleet/fleet.js.map +0 -1
- package/src/executors/foundation-preflight/foundation-preflight.js.map +0 -1
- package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js.map +0 -1
- package/src/executors/foundation-profile-preflight/production-database-safeguard.js.map +0 -1
- package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js.map +0 -1
- package/src/executors/github-bootstrap/github-bootstrap.js.map +0 -1
- package/src/executors/infrastructure-plan/infrastructure-plan.js.map +0 -1
- package/src/executors/local-deploy/federated-credential-transport.js.map +0 -1
- package/src/executors/local-deploy/fixtures/local-deploy-state-machine.mjs +0 -53
- package/src/executors/local-deploy/local-approval.js.map +0 -1
- package/src/executors/local-deploy/local-deploy.js.map +0 -1
- package/src/executors/refresh/refresh.js.map +0 -1
- package/src/executors/remove/remove.js.map +0 -1
- package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js.map +0 -1
- package/src/executors/secret-guardrails/secret-guardrails.js.map +0 -1
- package/src/executors/secret-initialize/secret-initialize.js.map +0 -1
- package/src/executors/secret-metadata/secret-metadata.js.map +0 -1
- package/src/executors/sst-lifecycle/generated-secret-retirement.js.map +0 -1
- package/src/executors/sst-lifecycle/governed-native-operation.js.map +0 -1
- package/src/executors/sst-lifecycle/logging-preflight.js.map +0 -1
- package/src/executors/sst-lifecycle/run-sst-command.js.map +0 -1
- package/src/executors/sst-lifecycle/secret-preflight.js.map +0 -1
- package/src/executors/sst-lifecycle/sst-environment-source.js.map +0 -1
- package/src/executors/unlock/unlock.js.map +0 -1
- package/src/executors/work-markers/work-markers.js.map +0 -1
- package/src/foundation/discovery.js.map +0 -1
- package/src/foundation/index.js.map +0 -1
- package/src/foundation/local-database-contract.js.map +0 -1
- package/src/foundation/logging-preflight.js.map +0 -1
- package/src/foundation/logging.js.map +0 -1
- package/src/foundation/partial-state.js.map +0 -1
- package/src/foundation/preflight.js.map +0 -1
- package/src/foundation/private-smoke-access.js.map +0 -1
- package/src/foundation/private-smoke-admission.js.map +0 -1
- package/src/foundation/private-smoke-control-plane.js.map +0 -1
- package/src/foundation/provision-database-bindings.js.map +0 -1
- package/src/foundation/provision-logical-database.js.map +0 -1
- package/src/foundation/requirements.js.map +0 -1
- package/src/foundation/router-origin-proof.js.map +0 -1
- package/src/foundation/router-waf-rate-rehearsal.js.map +0 -1
- package/src/foundation/router-waf.js.map +0 -1
- package/src/foundation/stages.js.map +0 -1
- package/src/frontend-security/index.js.map +0 -1
- package/src/frontend-security/security-headers.js.map +0 -1
- package/src/generators/feature-registry.js.map +0 -1
- package/src/generators/formatting.js.map +0 -1
- package/src/generators/function/function.js.map +0 -1
- package/src/generators/job/job.js.map +0 -1
- package/src/generators/logging-registry.js.map +0 -1
- package/src/generators/post-generation-install.js.map +0 -1
- package/src/generators/preset/dependency-overrides.js.map +0 -1
- package/src/generators/preset/generator.js.map +0 -1
- package/src/generators/preset/tool-versions.js.map +0 -1
- package/src/generators/preset/workspace-files-ci-acceptance.js.map +0 -1
- package/src/generators/preset/workspace-files-developer-agent.js.map +0 -1
- package/src/generators/preset/workspace-files-ecr-inspector.js.map +0 -1
- package/src/generators/preset/workspace-files-foundation-workload-roles.js.map +0 -1
- package/src/generators/preset/workspace-files-performance-cost.js.map +0 -1
- package/src/generators/preset/workspace-files-performance-load.js.map +0 -1
- package/src/generators/preset/workspace-files-stage-authority.js.map +0 -1
- package/src/generators/preset/workspace-files-thin-deploy.js.map +0 -1
- package/src/generators/preset/workspace-files-thin-docs.js.map +0 -1
- package/src/generators/preset/workspace-files-thin-release.js.map +0 -1
- package/src/generators/preset/workspace-files-workload-role-baseline.js.map +0 -1
- package/src/generators/preset/workspace-files.js.map +0 -1
- package/src/generators/repository-policy/repository-policy.js.map +0 -1
- package/src/generators/repository-policy/tree-policy.js.map +0 -1
- package/src/generators/service/service.js.map +0 -1
- package/src/generators/slice/built-in-plan-api-contract-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-api-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-async-work-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-authorization-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-background-work-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-deletion-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-lifecycle-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-retention-job-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-retention-launcher-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-rights-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-database-lifecycle-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-foundation-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-frontend-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-infrastructure-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-infrastructure-safety-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-metadata-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-native-runtime-authority-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-operations-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-performance-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-project-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-release-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-reliability-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-secret-runtime-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-shared.js.map +0 -1
- package/src/generators/slice/built-in-plan-testing-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-thin-release-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-verification-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-web-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-workload-role-files.js.map +0 -1
- package/src/generators/slice/built-in-plan.js.map +0 -1
- package/src/generators/slice/slice.js.map +0 -1
- package/src/generators/upgrade/upgrade.js.map +0 -1
- package/src/generators/workload/frozen-container.js.map +0 -1
- package/src/generators/workload/performance-registration.js.map +0 -1
- package/src/generators/workload/reliability-registration.js.map +0 -1
- package/src/generators/workload/service-performance-files.js.map +0 -1
- package/src/generators/workload/workload.js.map +0 -1
- package/src/git-policy/action-pins.js.map +0 -1
- package/src/git-policy/github-bootstrap.js.map +0 -1
- package/src/git-policy/index.js.map +0 -1
- package/src/git-policy/routing.js.map +0 -1
- package/src/index.js.map +0 -1
- package/src/infrastructure-change/convergence.js.map +0 -1
- package/src/infrastructure-change/drift.js.map +0 -1
- package/src/infrastructure-change/index.js.map +0 -1
- package/src/infrastructure-change/local-capability.js.map +0 -1
- package/src/infrastructure-change/model.js.map +0 -1
- package/src/infrastructure-change/normalize.js.map +0 -1
- package/src/infrastructure-change/plan.js.map +0 -1
- package/src/infrastructure-change/risk.js.map +0 -1
- package/src/infrastructure-change/summary.js.map +0 -1
- package/src/local-dev/context-discovery.js.map +0 -1
- package/src/local-dev/contract-validation.js.map +0 -1
- package/src/local-dev/contracts.js.map +0 -1
- package/src/local-dev/control-contract.js.map +0 -1
- package/src/local-dev/dev-command-core.js.map +0 -1
- package/src/local-dev/effects.js.map +0 -1
- package/src/local-dev/environment-inspector.js.map +0 -1
- package/src/local-dev/environment-state.js.map +0 -1
- package/src/local-dev/errors.js.map +0 -1
- package/src/local-dev/fleet-client.js.map +0 -1
- package/src/local-dev/fleet-configuration.js.map +0 -1
- package/src/local-dev/fleet-inventory.js.map +0 -1
- package/src/local-dev/fleet-ipc.js.map +0 -1
- package/src/local-dev/fleet-launcher.js.map +0 -1
- package/src/local-dev/fleet-lock.js.map +0 -1
- package/src/local-dev/fleet-logs.js.map +0 -1
- package/src/local-dev/fleet-process.js.map +0 -1
- package/src/local-dev/fleet-runtime.js.map +0 -1
- package/src/local-dev/fleet-session.js.map +0 -1
- package/src/local-dev/fleet-slice-runner-daemon.js.map +0 -1
- package/src/local-dev/fleet-startup.js.map +0 -1
- package/src/local-dev/fleet-state.js.map +0 -1
- package/src/local-dev/fleet-supervisor-daemon.js.map +0 -1
- package/src/local-dev/fleet-supervisor.js.map +0 -1
- package/src/local-dev/gateway-daemon.js.map +0 -1
- package/src/local-dev/gateway.js.map +0 -1
- package/src/local-dev/identity.js.map +0 -1
- package/src/local-dev/index.js.map +0 -1
- package/src/local-dev/local-context.js.map +0 -1
- package/src/local-dev/nx-workspace.js.map +0 -1
- package/src/local-dev/personal-secret-ledger.js.map +0 -1
- package/src/local-dev/personal-stage.js.map +0 -1
- package/src/local-dev/portless.js.map +0 -1
- package/src/local-dev/ports.js.map +0 -1
- package/src/local-dev/private-state.js.map +0 -1
- package/src/local-dev/process-identity.js.map +0 -1
- package/src/local-dev/proxy.js.map +0 -1
- package/src/local-dev/runtime-paths.js.map +0 -1
- package/src/local-dev/secret-slice-claim.js.map +0 -1
- package/src/local-dev/slice-claim.js.map +0 -1
- package/src/local-dev/slice-runner.js.map +0 -1
- package/src/local-dev/tls.js.map +0 -1
- package/src/ownership/composition.js.map +0 -1
- package/src/ownership/foundation-record.js.map +0 -1
- package/src/ownership/hash.js.map +0 -1
- package/src/ownership/index.js.map +0 -1
- package/src/ownership/json-ownership.js.map +0 -1
- package/src/ownership/record.js.map +0 -1
- package/src/ownership/workspace-record.js.map +0 -1
- package/src/performance/cost-collection.js.map +0 -1
- package/src/performance/cost-controls.js.map +0 -1
- package/src/performance/evidence.js.map +0 -1
- package/src/performance/index.js.map +0 -1
- package/src/performance/load-safety.js.map +0 -1
- package/src/performance/model.js.map +0 -1
- package/src/performance/pr-artifact-budget.js.map +0 -1
- package/src/performance/runner.js.map +0 -1
- package/src/performance/scenario-endpoint.js.map +0 -1
- package/src/performance/scenario.js.map +0 -1
- package/src/performance/test-fixtures.d.ts +0 -7
- package/src/performance/test-fixtures.js +0 -167
- package/src/performance/test-fixtures.js.map +0 -1
- package/src/performance/unit-cost.js.map +0 -1
- package/src/release/index.js.map +0 -1
- package/src/release/lifecycle.js.map +0 -1
- package/src/release/manifest.js.map +0 -1
- package/src/release/rollout.js.map +0 -1
- package/src/release/source-authority.js.map +0 -1
- package/src/repository-policy/aurora-preflight.js.map +0 -1
- package/src/repository-policy/capacity-migration.js.map +0 -1
- package/src/repository-policy/deployment-plan.js.map +0 -1
- package/src/repository-policy/foundation-profile.js.map +0 -1
- package/src/repository-policy/index.js.map +0 -1
- package/src/repository-policy/policy.js.map +0 -1
- package/src/secrets/adapters.js.map +0 -1
- package/src/secrets/aws-control-plane.js.map +0 -1
- package/src/secrets/declaration-verification.js.map +0 -1
- package/src/secrets/generated-aws-control-plane.js.map +0 -1
- package/src/secrets/guardrails.js.map +0 -1
- package/src/secrets/index.js.map +0 -1
- package/src/secrets/metadata.js.map +0 -1
- package/src/secrets/model.js.map +0 -1
- package/src/secrets/operator.js.map +0 -1
- package/src/secrets/permissions.js.map +0 -1
- package/src/secrets/personal-command.js.map +0 -1
- package/src/secrets/personal-ledger.js.map +0 -1
- package/src/secrets/registry.js.map +0 -1
- package/src/secrets/verification.js.map +0 -1
- package/src/secrets/workload-access.js.map +0 -1
- package/src/sst-environment-source-format.js.map +0 -1
- package/src/supply-chain/action-pins.js.map +0 -1
- package/src/supply-chain/blueprint-trust-git.js.map +0 -1
- package/src/supply-chain/blueprint-trust-process.js.map +0 -1
- package/src/supply-chain/blueprint-trust.js.map +0 -1
- package/src/supply-chain/blueprint-verification-authority.js.map +0 -1
- package/src/supply-chain/blueprint-verification-execution.js.map +0 -1
- package/src/supply-chain/build-output-bounds.js.map +0 -1
- package/src/supply-chain/build-output.js.map +0 -1
- package/src/supply-chain/common.js.map +0 -1
- package/src/supply-chain/container-artifact-contract.js.map +0 -1
- package/src/supply-chain/container-release.js.map +0 -1
- package/src/supply-chain/ecr-applicability.js.map +0 -1
- package/src/supply-chain/ecr-scanning.js.map +0 -1
- package/src/supply-chain/ecs-deployed-image.js.map +0 -1
- package/src/supply-chain/errors.js.map +0 -1
- package/src/supply-chain/generated-blueprint-trust-command.js.map +0 -1
- package/src/supply-chain/generated-command.js.map +0 -1
- package/src/supply-chain/generated-container-release-command.js.map +0 -1
- package/src/supply-chain/generated-ecr-collector-command.js.map +0 -1
- package/src/supply-chain/generated-files.js.map +0 -1
- package/src/supply-chain/generated-scanner-command.js.map +0 -1
- package/src/supply-chain/index.js.map +0 -1
- package/src/supply-chain/inventory.js.map +0 -1
- package/src/supply-chain/license-expression.js.map +0 -1
- package/src/supply-chain/pnpm-toolchain-authority.js.map +0 -1
- package/src/supply-chain/policy.js.map +0 -1
- package/src/supply-chain/publication.js.map +0 -1
- package/src/supply-chain/sbom.js.map +0 -1
- package/src/supply-chain/scanner.js.map +0 -1
- package/src/supply-chain/vendored-source.js.map +0 -1
- package/src/testing-evidence/fixture-scope.js.map +0 -1
- package/src/testing-evidence/index.js.map +0 -1
- package/src/testing-evidence/quarantine.js.map +0 -1
- package/src/testing-evidence/result.js.map +0 -1
- package/src/testing-evidence/runner-adapter.js.map +0 -1
- package/src/testing-evidence/sensitive-artifacts.js.map +0 -1
- package/src/validation.js.map +0 -1
- package/src/work-markers/index.js.map +0 -1
- package/src/work-markers/work-markers.js.map +0 -1
- package/src/workload-roles/account-baseline.js.map +0 -1
- package/src/workload-roles/authority.js.map +0 -1
- package/src/workload-roles/aws-adapter.js.map +0 -1
- package/src/workload-roles/aws-internal-handler.js.map +0 -1
- package/src/workload-roles/aws-s3-state.js.map +0 -1
- package/src/workload-roles/aws-sdk-control-plane.js.map +0 -1
- package/src/workload-roles/boundary.js.map +0 -1
- package/src/workload-roles/convergence.js.map +0 -1
- package/src/workload-roles/effective-policy.js.map +0 -1
- package/src/workload-roles/handler.js.map +0 -1
- package/src/workload-roles/history.js.map +0 -1
- package/src/workload-roles/index.js.map +0 -1
- package/src/workload-roles/injection.js.map +0 -1
- package/src/workload-roles/manifest.js.map +0 -1
- package/src/workload-roles/provisioner.js.map +0 -1
- package/src/workload-roles/registration.js.map +0 -1
- package/src/workload-roles/stage-boundary.js.map +0 -1
- package/src/workload-roles/standard-slice.js.map +0 -1
- package/src/workspace-deployment/affected-lifecycle.js.map +0 -1
- package/src/workspace-deployment/identity.js.map +0 -1
- package/src/workspace-deployment/index.js.map +0 -1
- package/src/workspace-deployment/migration-evidence.js.map +0 -1
- package/src/workspace-deployment/migration.js.map +0 -1
- package/src/workspace-deployment/ownership.js.map +0 -1
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderKeyValueDeletionFiles = renderKeyValueDeletionFiles;
|
|
4
|
+
const built_in_plan_shared_js_1 = require("./built-in-plan-shared.js");
|
|
5
|
+
/** Same public deletion journey, backed by native item transactions and durable intents. */
|
|
6
|
+
function renderKeyValueDeletionFiles(name, pascalName, workspace) {
|
|
7
|
+
const prefix = (0, built_in_plan_shared_js_1.lowerCamelIdentifier)(pascalName);
|
|
8
|
+
return {
|
|
9
|
+
'data/record-deletion.ts': `import {
|
|
10
|
+
assertAuthorizationAllowed, beginApprovalExecution, completeApprovalExecution,
|
|
11
|
+
createApprovalRequest, decideApprovalRequest, digestApprovalRequest,
|
|
12
|
+
type ApprovalRequest, type AuthorizationPrincipal,
|
|
13
|
+
} from '@empire-builder-kit/runtime/authorization';
|
|
14
|
+
import type { KeyValueChange } from '@empire-builder-kit/runtime/key-value';
|
|
15
|
+
import { createDataLifecycleDeletionRequest, createDataLifecycleLedgerEntry,
|
|
16
|
+
transitionDataLifecycleDeletion, type DataLifecycleDeletionRequestV1,
|
|
17
|
+
} from '@empire-builder-kit/runtime/data-lifecycle';
|
|
18
|
+
import { buildEventEntry } from '@empire-builder-kit/runtime/events';
|
|
19
|
+
import { RuntimeError } from '@empire-builder-kit/runtime/response';
|
|
20
|
+
import { ${prefix}AuthorizationPolicy } from '../authorization/policy.js';
|
|
21
|
+
import { ${pascalName}DataLifecycleChanged } from '../contracts/events.js';
|
|
22
|
+
import { dispatchEventIntent } from '../async/key-value-delivery.js';
|
|
23
|
+
import { create${pascalName}DataLifecycleChanged } from './lifecycle-event.js';
|
|
24
|
+
import { approvalReference, productRecordReference } from './pseudonymous-reference.js';
|
|
25
|
+
import { canonicalDataRightsNow, dataRightsAuditChange, dataRightsSubjectReference,
|
|
26
|
+
DATA_RIGHTS_POLICY_VERSION, plusDataRightsDays, requireDirectAdmin, requireDirectHuman,
|
|
27
|
+
} from './data-rights-shared.js';
|
|
28
|
+
import { loadActiveRecordHolds } from './record-holds.js';
|
|
29
|
+
import { readProductRecord } from './records.js';
|
|
30
|
+
import { dataStore } from './key-value.js';
|
|
31
|
+
|
|
32
|
+
const ACTION = '${name}:record-delete';
|
|
33
|
+
const COPY = 'event-contract';
|
|
34
|
+
const AUTHORIZATION_POLICY_VERSION = ${prefix}AuthorizationPolicy.version;
|
|
35
|
+
const COMPLETIONS = 'record-deletion-completions';
|
|
36
|
+
interface Workflow {
|
|
37
|
+
kind: 'record-deletion'; schemaVersion: 1;
|
|
38
|
+
approval: ApprovalRequest<typeof ACTION>;
|
|
39
|
+
lifecycle: DataLifecycleDeletionRequestV1;
|
|
40
|
+
}
|
|
41
|
+
interface Command {
|
|
42
|
+
principal: AuthorizationPrincipal; recordId: string; correlationId: string; now?: string;
|
|
43
|
+
}
|
|
44
|
+
interface Execution extends Command { approvalRequestId: string; reasonCode: string }
|
|
45
|
+
const command = (recordId: string, reasonCode: string) => ({ reasonCode, recordId });
|
|
46
|
+
const digestInput = (recordId: string, reasonCode: string) => ({ action: ACTION,
|
|
47
|
+
resource: { id: recordId, type: 'record' }, command: command(recordId, reasonCode) } as const);
|
|
48
|
+
function failure(code: string, message: string, status = 409): never {
|
|
49
|
+
throw new RuntimeError({ code, message, status, retryable: status === 503 || code === 'deletion_conflict' });
|
|
50
|
+
}
|
|
51
|
+
function validateInput(input: Command) {
|
|
52
|
+
requireDirectHuman(input.principal);
|
|
53
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(input.recordId) || !input.correlationId || input.correlationId.length > 128)
|
|
54
|
+
failure('deletion_input_invalid', 'Deletion identity is invalid.', 400);
|
|
55
|
+
}
|
|
56
|
+
function recordAuthority(value: unknown, input: Command) {
|
|
57
|
+
const record = readProductRecord(value, input.recordId);
|
|
58
|
+
if (input.principal.kind !== 'admin' && record.ownerId !== input.principal.actor.id)
|
|
59
|
+
failure('record_not_found', 'The record was not found.', 404);
|
|
60
|
+
}
|
|
61
|
+
async function loadWorkflow(input: { recordId: string; approvalRequestId: string },
|
|
62
|
+
snapshot?: Awaited<ReturnType<ReturnType<typeof dataStore>['get']>>) {
|
|
63
|
+
const row = snapshot ?? await dataStore().get('application', 'record-deletions', input.approvalRequestId);
|
|
64
|
+
const workflow = row?.value as Workflow | undefined;
|
|
65
|
+
if (!row || !workflow || workflow.kind !== 'record-deletion' || workflow.schemaVersion !== 1 ||
|
|
66
|
+
workflow.approval?.id !== input.approvalRequestId || workflow.approval.action !== ACTION ||
|
|
67
|
+
workflow.approval.resource?.id !== input.recordId || workflow.approval.resource.type !== 'record' ||
|
|
68
|
+
workflow.approval.slice !== '${name}' || workflow.approval.schemaVersion !== 1 ||
|
|
69
|
+
!Number.isInteger(workflow.approval.revision) || workflow.approval.revision < 0 ||
|
|
70
|
+
workflow.lifecycle?.kind !== 'data-deletion' || workflow.lifecycle.version !== 1 ||
|
|
71
|
+
workflow.lifecycle.scopeRef !== productRecordReference(input.recordId) ||
|
|
72
|
+
workflow.lifecycle.requestRef !== approvalReference(input.approvalRequestId) ||
|
|
73
|
+
typeof workflow.lifecycle.policyVersion !== 'string' || !/^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$/.test(workflow.lifecycle.policyVersion))
|
|
74
|
+
failure('deletion_workflow_invalid', 'The exact deletion workflow was not found or is malformed.', 404);
|
|
75
|
+
if (workflow.lifecycle.categoryId !== 'product-record' || workflow.lifecycle.primaryLocationId !== 'primary-database' ||
|
|
76
|
+
workflow.lifecycle.approvalRequirement !== 'required' || !Array.isArray(workflow.lifecycle.downstreamCopies) || workflow.lifecycle.downstreamCopies.length !== 1 ||
|
|
77
|
+
workflow.lifecycle.downstreamCopies[0]?.copyId !== COPY)
|
|
78
|
+
failure('deletion_workflow_invalid', 'The deletion workflow has an invalid resource or propagation contract.');
|
|
79
|
+
if (workflow.approval.status === 'executed' &&
|
|
80
|
+
(workflow.approval.execution?.executionId !== 'deletion:' + input.approvalRequestId ||
|
|
81
|
+
!workflow.approval.execution.completedAt || workflow.approval.decision?.outcome !== 'approved' ||
|
|
82
|
+
workflow.lifecycle.status !== 'completed' || workflow.lifecycle.primaryStatus !== 'completed' ||
|
|
83
|
+
workflow.lifecycle.downstreamCopies[0].status !== 'completed'))
|
|
84
|
+
failure('deletion_workflow_invalid', 'The deletion workflow lacks completed execution and propagation evidence.');
|
|
85
|
+
return { workflow, version: row.version };
|
|
86
|
+
}
|
|
87
|
+
async function load(input: Command & { approvalRequestId: string }) {
|
|
88
|
+
const retained = await loadWorkflow(input);
|
|
89
|
+
if (retained.workflow.lifecycle.primaryStatus !== 'completed' &&
|
|
90
|
+
(retained.workflow.approval.policyVersion !== AUTHORIZATION_POLICY_VERSION ||
|
|
91
|
+
retained.workflow.lifecycle.policyVersion !== DATA_RIGHTS_POLICY_VERSION))
|
|
92
|
+
failure('deletion_policy_stale', 'The deletion approval policy has changed.');
|
|
93
|
+
if (input.principal.kind !== 'admin' && retained.workflow.approval.author?.actorId !== input.principal.actor.id)
|
|
94
|
+
failure('deletion_workflow_invalid', 'The exact deletion workflow was not found or is malformed.', 404);
|
|
95
|
+
return retained;
|
|
96
|
+
}
|
|
97
|
+
function requireCommittedExecution(workflow: Workflow, approvalRequestId: string) {
|
|
98
|
+
if (workflow.approval.status !== 'executing' || workflow.lifecycle.primaryStatus !== 'completed' ||
|
|
99
|
+
workflow.approval.decision?.outcome !== 'approved' ||
|
|
100
|
+
workflow.approval.execution?.executionId !== 'deletion:' + approvalRequestId ||
|
|
101
|
+
typeof workflow.approval.policyVersion !== 'string' || !workflow.approval.policyVersion)
|
|
102
|
+
failure('deletion_workflow_invalid', 'Deletion completion lacks its exact approved execution.');
|
|
103
|
+
}
|
|
104
|
+
function workflowChange(workflow: Workflow, expectedVersion: number): KeyValueChange {
|
|
105
|
+
return { action: 'put', collection: 'record-deletions', id: workflow.approval.id, expectedVersion, value: workflow };
|
|
106
|
+
}
|
|
107
|
+
function audit(input: Command & { approvalRequestId: string }, now: string, purposeCode: string) {
|
|
108
|
+
return dataRightsAuditChange({ action: 'deletion', approvalRequestId: input.approvalRequestId,
|
|
109
|
+
principal: input.principal, recordId: input.recordId, correlationId: input.correlationId,
|
|
110
|
+
now, outcome: 'succeeded', purposeCode, summary: 'A bounded record deletion workflow transition was committed.' });
|
|
111
|
+
}
|
|
112
|
+
async function commit(input: Parameters<ReturnType<typeof dataStore>['commit']>[0]) {
|
|
113
|
+
try { await dataStore().commit(input); }
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (error instanceof Error && error.name === 'TransactionCanceledException' &&
|
|
116
|
+
(error as Error & { CancellationReasons?: Array<{ Code?: string }> }).CancellationReasons?.some(reason => reason.Code === 'ConditionalCheckFailed'))
|
|
117
|
+
failure('deletion_conflict', 'The record, hold or approval changed concurrently; retry the exact request.');
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function requestRecordDeletion(input: Command & { reasonCode: string }) {
|
|
123
|
+
validateInput(input);
|
|
124
|
+
const now = canonicalDataRightsNow(input.now);
|
|
125
|
+
const row = await dataStore().get('application', 'records', input.recordId);
|
|
126
|
+
if (!row) failure('record_not_found', 'The record was not found.', 404);
|
|
127
|
+
recordAuthority(row.value, input);
|
|
128
|
+
const approval = createApprovalRequest({ ...digestInput(input.recordId, input.reasonCode),
|
|
129
|
+
author: input.principal, expiresAt: plusDataRightsDays(now, 1), now,
|
|
130
|
+
policyVersion: AUTHORIZATION_POLICY_VERSION, slice: '${name}' });
|
|
131
|
+
const lifecycle = createDataLifecycleDeletionRequest({ approvalRequirement: 'required',
|
|
132
|
+
categoryId: 'product-record', createdAt: now, downstreamCopyIds: [COPY],
|
|
133
|
+
policyVersion: DATA_RIGHTS_POLICY_VERSION, primaryLocationId: 'primary-database',
|
|
134
|
+
requestRef: approvalReference(approval.id), scopeRef: productRecordReference(input.recordId) });
|
|
135
|
+
await commit({ tenantId: 'application', changes: [
|
|
136
|
+
{ action: 'check', collection: 'records', id: input.recordId, expectedVersion: row.version },
|
|
137
|
+
workflowChange({ kind: 'record-deletion', schemaVersion: 1, approval, lifecycle }, 0),
|
|
138
|
+
audit({ ...input, approvalRequestId: approval.id }, now, 'approval-pending'),
|
|
139
|
+
] });
|
|
140
|
+
return { approvalRequestId: approval.id, expiresAt: approval.expiresAt, requestDigest: approval.requestDigest, status: 'pending' as const };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export async function decideRecordDeletion(input: Command & {
|
|
144
|
+
approvalRequestId: string; expectedRequestDigest: string; outcome: string; reasonReference: string;
|
|
145
|
+
}) {
|
|
146
|
+
validateInput(input); requireDirectAdmin(input.principal);
|
|
147
|
+
if (input.outcome !== 'approved' && input.outcome !== 'rejected')
|
|
148
|
+
failure('approval_outcome_invalid', 'The approval outcome is invalid.', 400);
|
|
149
|
+
const { workflow, version } = await load(input);
|
|
150
|
+
const now = canonicalDataRightsNow(input.now);
|
|
151
|
+
const decision = decideApprovalRequest(workflow.approval, { approver: input.principal,
|
|
152
|
+
expectedRequestDigest: input.expectedRequestDigest, now, outcome: input.outcome,
|
|
153
|
+
reasonReference: input.reasonReference, requiredPolicyVersion: AUTHORIZATION_POLICY_VERSION });
|
|
154
|
+
assertAuthorizationAllowed(decision.decision);
|
|
155
|
+
const next = { ...workflow, approval: decision.request,
|
|
156
|
+
lifecycle: decision.request.status === 'rejected'
|
|
157
|
+
? transitionDataLifecycleDeletion(workflow.lifecycle, { at: now, reasonCode: 'approval-rejected', type: 'reject' })
|
|
158
|
+
: workflow.lifecycle };
|
|
159
|
+
await commit({ tenantId: 'application', changes: [workflowChange(next, version), audit(input, now, 'approval-' + input.outcome)] });
|
|
160
|
+
return { approvalRequestId: decision.request.id, status: decision.request.status as 'approved' | 'rejected' };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function prepare(input: Execution) {
|
|
164
|
+
validateInput(input);
|
|
165
|
+
const now = canonicalDataRightsNow(input.now);
|
|
166
|
+
const { workflow, version } = await load(input);
|
|
167
|
+
if (digestApprovalRequest(digestInput(input.recordId, input.reasonCode)) !== workflow.approval.requestDigest)
|
|
168
|
+
failure('deletion_command_mismatch', 'The deletion command differs from the approved request.');
|
|
169
|
+
if (workflow.approval.status === 'executed' && workflow.lifecycle.status === 'completed') return 'completed';
|
|
170
|
+
if (workflow.lifecycle.primaryStatus === 'completed') {
|
|
171
|
+
requireCommittedExecution(workflow, input.approvalRequestId);
|
|
172
|
+
return 'pending';
|
|
173
|
+
}
|
|
174
|
+
const begun = beginApprovalExecution(workflow.approval, { ...digestInput(input.recordId, input.reasonCode),
|
|
175
|
+
executionId: 'deletion:' + input.approvalRequestId, now, requiredPolicyVersion: AUTHORIZATION_POLICY_VERSION });
|
|
176
|
+
assertAuthorizationAllowed(begun.decision);
|
|
177
|
+
const row = await dataStore().get('application', 'records', input.recordId);
|
|
178
|
+
if (!row) failure('record_not_found', 'The record was not found.', 404);
|
|
179
|
+
recordAuthority(row.value, input);
|
|
180
|
+
const holds = await loadActiveRecordHolds(input.recordId, now, 'deletion');
|
|
181
|
+
let lifecycle = transitionDataLifecycleDeletion(workflow.lifecycle, { type: 'verify', at: now,
|
|
182
|
+
authorityRef: dataRightsSubjectReference(input.principal.actor.id),
|
|
183
|
+
approvalRef: approvalReference(input.approvalRequestId), holds: holds.holds });
|
|
184
|
+
if (lifecycle.status === 'held') {
|
|
185
|
+
await commit({ tenantId: 'application', changes: [holds.check, workflowChange({ ...workflow, lifecycle }, version)] });
|
|
186
|
+
return 'held';
|
|
187
|
+
}
|
|
188
|
+
lifecycle = transitionDataLifecycleDeletion(lifecycle, { at: now, type: 'start-primary' });
|
|
189
|
+
lifecycle = transitionDataLifecycleDeletion(lifecycle, { at: now, type: 'complete-primary', outcomeCode: 'deleted' });
|
|
190
|
+
const ledger = createDataLifecycleLedgerEntry({ action: 'deletion', categoryId: 'product-record',
|
|
191
|
+
effectiveAt: now, expiresAt: plusDataRightsDays(now, 400), policyVersion: DATA_RIGHTS_POLICY_VERSION,
|
|
192
|
+
pseudonymousKey: productRecordReference(input.recordId) });
|
|
193
|
+
const entry = buildEventEntry(${pascalName}DataLifecycleChanged,
|
|
194
|
+
create${pascalName}DataLifecycleChanged({ action: 'deletion', occurredAt: now,
|
|
195
|
+
policyVersion: DATA_RIGHTS_POLICY_VERSION, recordId: input.recordId, requestRef: approvalReference(input.approvalRequestId) }),
|
|
196
|
+
{ busName: process.env.EVENT_BUS_NAME ?? '', stage: process.env.EBK_STAGE ?? '',
|
|
197
|
+
context: { correlationId: input.correlationId, tenantId: 'application' },
|
|
198
|
+
eventId: input.approvalRequestId, occurredAt: now }).entry;
|
|
199
|
+
// No event can exist without its primary deletion, ledger and execution claim.
|
|
200
|
+
// The hold-set check covers both a changed hold and a concurrently inserted first hold.
|
|
201
|
+
await commit({ tenantId: 'application', changes: [
|
|
202
|
+
{ action: 'delete', collection: 'records', id: input.recordId, expectedVersion: row.version },
|
|
203
|
+
holds.check,
|
|
204
|
+
workflowChange({ ...workflow, approval: begun.request, lifecycle }, version),
|
|
205
|
+
{ action: 'put', collection: COMPLETIONS, id: input.approvalRequestId, expectedVersion: 0,
|
|
206
|
+
value: { kind: 'record-deletion-completion', schemaVersion: 1, recordId: input.recordId } },
|
|
207
|
+
{ action: 'put', collection: 'data-lifecycle-ledger', id: input.approvalRequestId, expectedVersion: 0, value: ledger },
|
|
208
|
+
audit(input, now, 'primary-deleted'),
|
|
209
|
+
], events: [entry], retainEventsUntilAcknowledged: true });
|
|
210
|
+
return 'pending';
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function finish(input: Execution, published: boolean) {
|
|
214
|
+
return finishRetained(input, await load(input), published);
|
|
215
|
+
}
|
|
216
|
+
async function finishRetained(input: Command & { approvalRequestId: string },
|
|
217
|
+
{ workflow, version }: Awaited<ReturnType<typeof loadWorkflow>>, published: boolean) {
|
|
218
|
+
if (workflow.approval.status === 'executed' && workflow.lifecycle.status === 'completed') return;
|
|
219
|
+
requireCommittedExecution(workflow, input.approvalRequestId);
|
|
220
|
+
const completion = await dataStore().get('application', COMPLETIONS, input.approvalRequestId);
|
|
221
|
+
const pending = completion?.value as { kind?: string; schemaVersion?: number; recordId?: string } | undefined;
|
|
222
|
+
if (!completion || pending?.kind !== 'record-deletion-completion' || pending.schemaVersion !== 1 || pending.recordId !== input.recordId)
|
|
223
|
+
failure('deletion_workflow_invalid', 'Deletion completion lacks its atomic pending-work binding.');
|
|
224
|
+
const now = canonicalDataRightsNow(input.now);
|
|
225
|
+
let lifecycle = workflow.lifecycle;
|
|
226
|
+
if (lifecycle.status === 'failed') lifecycle = transitionDataLifecycleDeletion(lifecycle, { at: now, type: 'retry' });
|
|
227
|
+
lifecycle = transitionDataLifecycleDeletion(lifecycle, { at: now, copyId: COPY, type: 'start-copy' });
|
|
228
|
+
lifecycle = transitionDataLifecycleDeletion(lifecycle, published
|
|
229
|
+
? { at: now, copyId: COPY, type: 'complete-copy', outcomeCode: 'acknowledged' }
|
|
230
|
+
: { at: now, copyId: COPY, type: 'fail-copy', errorCode: 'event-publication-failed' });
|
|
231
|
+
let approval = workflow.approval;
|
|
232
|
+
if (published) {
|
|
233
|
+
const completed = completeApprovalExecution(approval, { executionId: 'deletion:' + input.approvalRequestId,
|
|
234
|
+
now, requiredPolicyVersion: approval.policyVersion });
|
|
235
|
+
assertAuthorizationAllowed(completed.decision); approval = completed.request;
|
|
236
|
+
}
|
|
237
|
+
await commit({ tenantId: 'application', changes: [workflowChange({ ...workflow, approval, lifecycle }, version),
|
|
238
|
+
...(published ? [audit(input, now, 'approval-executed'),
|
|
239
|
+
{ action: 'delete' as const, collection: COMPLETIONS, id: input.approvalRequestId, expectedVersion: completion.version }] : []),
|
|
240
|
+
], ...(published ? { acknowledgeEvents: [input.approvalRequestId] } : {}) });
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Finish an already-authorized, committed primary deletion. This cannot begin
|
|
244
|
+
* a deletion or approve a request; delivery and the exact execution are re-read. */
|
|
245
|
+
export async function completePendingRecordDeletion(approvalRequestId: string, expectedRecordId?: string): Promise<boolean> {
|
|
246
|
+
const row = await dataStore().get('application', 'record-deletions', approvalRequestId);
|
|
247
|
+
if (!row) {
|
|
248
|
+
if (expectedRecordId !== undefined) failure('deletion_workflow_invalid', 'Pending deletion lacks its retained workflow.');
|
|
249
|
+
return true; // Other event kinds do not have deletion workflows.
|
|
250
|
+
}
|
|
251
|
+
const recordId = (row.value as Workflow)?.approval?.resource?.id;
|
|
252
|
+
if (typeof recordId !== 'string' || expectedRecordId !== undefined && recordId !== expectedRecordId)
|
|
253
|
+
failure('deletion_workflow_invalid', 'Deletion completion lacks its exact record identity.');
|
|
254
|
+
const retained = await loadWorkflow({ recordId, approvalRequestId }, row);
|
|
255
|
+
const { workflow } = retained;
|
|
256
|
+
if (workflow.approval.status === 'executed') return true;
|
|
257
|
+
if (expectedRecordId === undefined && (workflow.approval.status !== 'executing' || workflow.lifecycle.primaryStatus !== 'completed')) return false;
|
|
258
|
+
requireCommittedExecution(workflow, approvalRequestId);
|
|
259
|
+
if (await dispatchEventIntent(approvalRequestId) !== 'published') return false;
|
|
260
|
+
const identity = '${name}:event-delivery';
|
|
261
|
+
const principal: AuthorizationPrincipal = { kind: 'agent', actor: { id: identity, type: 'service' },
|
|
262
|
+
subject: { id: identity, type: 'service' }, authority: { mode: 'machine', machineIdentityId: identity },
|
|
263
|
+
audience: identity, roles: [], scopes: [] };
|
|
264
|
+
await finishRetained({ principal, recordId, approvalRequestId, correlationId: 'deletion:' + approvalRequestId }, retained, true);
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** Recover completion even if a worker died after acknowledging publication.
|
|
269
|
+
* Published intents leave the outbox index, so use a separate bounded cursor. */
|
|
270
|
+
export async function reconcileRecordDeletions() {
|
|
271
|
+
const store = dataStore(), id = 'deletion-completion';
|
|
272
|
+
const checkpoint = await store.get('system', 'event-reconciliation', id);
|
|
273
|
+
const afterId = (checkpoint?.value as { afterId?: string | null } | undefined)?.afterId;
|
|
274
|
+
if (checkpoint && afterId !== null && typeof afterId !== 'string') throw new Error('Deletion completion cursor is malformed.');
|
|
275
|
+
const page = await store.list('application', COMPLETIONS, { limit: 25, ...(afterId ? { afterId } : {}) });
|
|
276
|
+
const summary = { completed: 0, pending: 0, failed: 0, deferred: 0, remaining: false };
|
|
277
|
+
for (const row of page.records) {
|
|
278
|
+
try {
|
|
279
|
+
const pending = row.value as { kind?: string; schemaVersion?: number; recordId?: string;
|
|
280
|
+
failure?: { at: string; attempts: number; code: string; nextAttemptAt: string } };
|
|
281
|
+
if (pending.kind !== 'record-deletion-completion' || pending.schemaVersion !== 1 || typeof pending.recordId !== 'string')
|
|
282
|
+
failure('deletion_workflow_invalid', 'Deletion pending-work binding is malformed.');
|
|
283
|
+
if (pending.failure) {
|
|
284
|
+
if (pending.failure.code !== 'deletion-completion-failed' || !Number.isSafeInteger(pending.failure.attempts) ||
|
|
285
|
+
pending.failure.attempts < 1 || pending.failure.attempts > 16 ||
|
|
286
|
+
canonicalDataRightsNow(pending.failure.at) !== pending.failure.at ||
|
|
287
|
+
canonicalDataRightsNow(pending.failure.nextAttemptAt) !== pending.failure.nextAttemptAt ||
|
|
288
|
+
Date.parse(pending.failure.nextAttemptAt) <= Date.parse(pending.failure.at) ||
|
|
289
|
+
Date.parse(pending.failure.nextAttemptAt) - Date.parse(pending.failure.at) > 3_600_000)
|
|
290
|
+
failure('deletion_workflow_invalid', 'Deletion completion retry metadata is malformed.');
|
|
291
|
+
if (Date.parse(pending.failure.nextAttemptAt) > Date.now()) {
|
|
292
|
+
summary.pending++; summary.deferred++; continue;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (await completePendingRecordDeletion(row.id, pending.recordId)) summary.completed++;
|
|
296
|
+
else summary.pending++;
|
|
297
|
+
} catch (error) {
|
|
298
|
+
// A concurrent caller may have atomically completed and removed this work.
|
|
299
|
+
// Re-read only on an optimistic conflict; never resurrect the old row or
|
|
300
|
+
// accept an unrelated/malformed terminal workflow as successful completion.
|
|
301
|
+
if (error instanceof RuntimeError && error.code === 'deletion_conflict') {
|
|
302
|
+
const pending = row.value as { recordId?: string };
|
|
303
|
+
if (typeof pending.recordId === 'string') {
|
|
304
|
+
const latest = await loadWorkflow({ recordId: pending.recordId, approvalRequestId: row.id });
|
|
305
|
+
if (latest.workflow.approval.status === 'executed' &&
|
|
306
|
+
!await store.get('application', COMPLETIONS, row.id)) {
|
|
307
|
+
summary.completed++;
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
summary.failed++;
|
|
313
|
+
// Durable, content-free operational evidence remains on the pending item.
|
|
314
|
+
// A failed conditional write also fails the invocation; never silently skip it.
|
|
315
|
+
const previousAttempts = (row.value as { failure?: { attempts?: number } }).failure?.attempts;
|
|
316
|
+
const attempts = Math.min(Number.isSafeInteger(previousAttempts) && previousAttempts! > 0 ? previousAttempts! + 1 : 1, 16);
|
|
317
|
+
const at = canonicalDataRightsNow();
|
|
318
|
+
const nextAttemptAt = new Date(Date.parse(at) + Math.min(60_000 * 2 ** (attempts - 1), 3_600_000)).toISOString();
|
|
319
|
+
await store.commit({ tenantId: 'application', changes: [{ action: 'put', collection: COMPLETIONS,
|
|
320
|
+
id: row.id, expectedVersion: row.version, value: { ...(row.value as object),
|
|
321
|
+
failure: { code: 'deletion-completion-failed', at, attempts, nextAttemptAt } } }] });
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
await store.commit({ tenantId: 'system', changes: [{ action: 'put', collection: 'event-reconciliation', id,
|
|
325
|
+
expectedVersion: checkpoint?.version ?? 0, value: { afterId: page.nextId ?? null } }] });
|
|
326
|
+
// Page counters are not global health. A one-row strongly consistent probe
|
|
327
|
+
// keeps earlier failed/deferred obligations visible after the cursor moves.
|
|
328
|
+
summary.remaining = (await store.list('application', COMPLETIONS, { limit: 1 })).records.length > 0;
|
|
329
|
+
if (summary.failed) failure('deletion_reconciliation_failed', 'Deletion completion failed; inspect the retained pending-work failure markers and retry reconciliation.', 503);
|
|
330
|
+
return summary;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export async function deleteRecord(input: Execution) {
|
|
334
|
+
const prepared = await prepare(input);
|
|
335
|
+
if (prepared === 'held') failure('data_deletion_held', 'Deletion is blocked by an active hold.');
|
|
336
|
+
if (prepared === 'completed') return { deleted: true, id: input.recordId };
|
|
337
|
+
let delivery: Awaited<ReturnType<typeof dispatchEventIntent>> = 'retry-required';
|
|
338
|
+
try { delivery = await dispatchEventIntent(input.approvalRequestId); } catch { /* durable intent remains retryable */ }
|
|
339
|
+
// The primary transaction is committed. A competing delivery owner is not a
|
|
340
|
+
// failed copy and cannot supply publication/completed-approval evidence.
|
|
341
|
+
if (delivery === 'delivery-pending') return { deleted: true, id: input.recordId, deliveryPending: true };
|
|
342
|
+
const published = delivery === 'published';
|
|
343
|
+
await finish(input, published);
|
|
344
|
+
if (!published) failure('data_deletion_retry_required', 'Deletion propagation requires retry.', 503);
|
|
345
|
+
return { deleted: true, id: input.recordId };
|
|
346
|
+
}
|
|
347
|
+
`,
|
|
348
|
+
'database/deletions.ts': `import type { RuntimeHttpEvent } from '@empire-builder-kit/runtime';
|
|
349
|
+
import type { AuthorizationPrincipal } from '@empire-builder-kit/runtime/authorization';
|
|
350
|
+
import { createContractHttpJsonHandler } from '@empire-builder-kit/runtime/api-server';
|
|
351
|
+
import { ${prefix}RequestRecordDeletion, ${prefix}DecideRecordDeletion, ${prefix}DeleteRecord } from '../api/contracts/protected.js';
|
|
352
|
+
import { authorize${pascalName}DataRightsOperation } from '../authorization/runtime.js';
|
|
353
|
+
import { requestRecordDeletion, decideRecordDeletion, deleteRecord } from '../data/record-deletion.js';
|
|
354
|
+
|
|
355
|
+
export const requestDeletionHandler = createContractHttpJsonHandler<typeof ${prefix}RequestRecordDeletion, RuntimeHttpEvent, AuthorizationPrincipal>({
|
|
356
|
+
component: 'database-records', operation: ${prefix}RequestRecordDeletion, workspace: '${workspace}',
|
|
357
|
+
authorize: authorize${pascalName}DataRightsOperation,
|
|
358
|
+
handle: ({ context, principal, request }) => requestRecordDeletion({ principal, correlationId: context.correlationId,
|
|
359
|
+
recordId: request.path.recordId, reasonCode: request.body.reasonCode }),
|
|
360
|
+
});
|
|
361
|
+
export const decideDeletionHandler = createContractHttpJsonHandler<typeof ${prefix}DecideRecordDeletion, RuntimeHttpEvent, AuthorizationPrincipal>({
|
|
362
|
+
component: 'database-records', operation: ${prefix}DecideRecordDeletion, workspace: '${workspace}',
|
|
363
|
+
authorize: authorize${pascalName}DataRightsOperation,
|
|
364
|
+
handle: ({ context, principal, request }) => decideRecordDeletion({ principal, correlationId: context.correlationId,
|
|
365
|
+
recordId: request.path.recordId, approvalRequestId: request.path.approvalRequestId,
|
|
366
|
+
expectedRequestDigest: request.body.expectedRequestDigest, outcome: request.body.outcome, reasonReference: request.body.reasonReference }),
|
|
367
|
+
});
|
|
368
|
+
export const deleteHandler = createContractHttpJsonHandler<typeof ${prefix}DeleteRecord, RuntimeHttpEvent, AuthorizationPrincipal>({
|
|
369
|
+
component: 'database-records', operation: ${prefix}DeleteRecord, workspace: '${workspace}',
|
|
370
|
+
authorize: authorize${pascalName}DataRightsOperation,
|
|
371
|
+
handle: ({ context, principal, request }) => deleteRecord({ principal, correlationId: context.correlationId,
|
|
372
|
+
recordId: request.path.recordId, approvalRequestId: request.query.approvalRequestId, reasonCode: request.query.reasonCode }),
|
|
373
|
+
});
|
|
374
|
+
`,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
//# sourceMappingURL=built-in-plan-key-value-deletion.js.map
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderKeyValueDeliverySource = renderKeyValueDeliverySource;
|
|
4
|
+
/** Streams are prompt delivery; retained intents and persisted cursors are the recovery path. */
|
|
5
|
+
function renderKeyValueDeliverySource(name) {
|
|
6
|
+
return `import { EventBridgeClient, PutEventsCommand } from '@aws-sdk/client-eventbridge';
|
|
7
|
+
import { DYNAMO_OUTBOX_SHARDS } from '@empire-builder-kit/runtime/key-value';
|
|
8
|
+
import type { PublishableEventEntry } from '@empire-builder-kit/runtime/events';
|
|
9
|
+
import { dataStore } from '../data/key-value.js';
|
|
10
|
+
import { reconcileRecordDeletions } from '../data/record-deletion.js';
|
|
11
|
+
|
|
12
|
+
const client = new EventBridgeClient({ maxAttempts: 2 });
|
|
13
|
+
type Store = ReturnType<typeof dataStore>;
|
|
14
|
+
type Cursor = NonNullable<Awaited<ReturnType<Store['listPending']>>['cursor']>;
|
|
15
|
+
// Only the provider fields consumed below are part of this handler's input.
|
|
16
|
+
// No ambient aws-lambda package is required by the generated application.
|
|
17
|
+
interface DynamoDBStreamEvent {
|
|
18
|
+
Records: Array<{
|
|
19
|
+
eventName?: string; eventSource?: string; eventSourceARN?: string;
|
|
20
|
+
dynamodb?: {
|
|
21
|
+
SequenceNumber?: string;
|
|
22
|
+
Keys?: Record<string, { S?: string }>;
|
|
23
|
+
NewImage?: Record<string, { S?: string }>;
|
|
24
|
+
};
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function publish(entry: PublishableEventEntry): Promise<void> {
|
|
29
|
+
const result = await client.send(new PutEventsCommand({ Entries: [{
|
|
30
|
+
Source: entry.source, DetailType: entry.detailType, Detail: entry.detail,
|
|
31
|
+
EventBusName: entry.eventBusName,
|
|
32
|
+
}] }), { abortSignal: AbortSignal.timeout(25_000) });
|
|
33
|
+
if (result.FailedEntryCount !== 0 || result.Entries?.length !== 1 ||
|
|
34
|
+
result.Entries[0]?.ErrorCode || !result.Entries[0]?.EventId)
|
|
35
|
+
throw new Error('EventBridge did not acknowledge this event intent.');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const delivered = (result: Awaited<ReturnType<Store['dispatch']>>) =>
|
|
39
|
+
result === 'published' || result === 'already-published';
|
|
40
|
+
|
|
41
|
+
/** The API and background workers share the same durable lease/acknowledgement path. */
|
|
42
|
+
export async function dispatchEventIntent(id: string): Promise<'published' | 'delivery-pending' | 'retry-required'> {
|
|
43
|
+
const result = await dataStore().dispatch(id, publish);
|
|
44
|
+
return delivered(result) ? 'published' : result === 'busy' ? 'delivery-pending' : 'retry-required';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function stream(event: DynamoDBStreamEvent) {
|
|
48
|
+
const streamArn = process.env.EBK_KEY_VALUE_STREAM_ARN;
|
|
49
|
+
if (!streamArn || !Array.isArray(event?.Records) || event.Records.length > 100 ||
|
|
50
|
+
event.Records.some((record) => record.eventSource !== 'aws:dynamodb' ||
|
|
51
|
+
record.eventSourceARN !== streamArn ||
|
|
52
|
+
!/^[0-9]{1,128}$/.test(record.dynamodb?.SequenceNumber ?? '')))
|
|
53
|
+
throw new Error('DynamoDB delivery requires the exact configured stream and bounded sequence records.');
|
|
54
|
+
const store = dataStore();
|
|
55
|
+
const batchItemFailures: Array<{ itemIdentifier: string }> = [];
|
|
56
|
+
for (const record of event.Records) {
|
|
57
|
+
const image = record.dynamodb?.NewImage;
|
|
58
|
+
// The provider filter is an efficiency aid, not an authorization boundary.
|
|
59
|
+
if (record.eventName !== 'INSERT' || image?.kind?.S !== 'outbox' || image.state?.S !== 'pending') continue;
|
|
60
|
+
try {
|
|
61
|
+
const id = image.sk?.S;
|
|
62
|
+
const scope = process.env.EBK_STAGE + '/${name}';
|
|
63
|
+
if (!id || image.scope?.S !== scope || record.dynamodb?.Keys?.sk?.S !== id ||
|
|
64
|
+
record.dynamodb.Keys.pk?.S !== image.pk?.S || !image.pk?.S?.startsWith(scope + '/outbox/'))
|
|
65
|
+
throw new Error('Stream intent identity differs from its table scope.');
|
|
66
|
+
// Re-read and validate the retained intent; never publish the untrusted stream image.
|
|
67
|
+
const result = await store.dispatch(id, publish);
|
|
68
|
+
// Another owner may be publishing the committed intent. Acknowledge this
|
|
69
|
+
// notification, not publication; the retained intent and sweep recover a
|
|
70
|
+
// dead owner after lease expiry.
|
|
71
|
+
if (!delivered(result) && result !== 'busy')
|
|
72
|
+
throw new Error('Durable intent needs another delivery attempt.');
|
|
73
|
+
} catch {
|
|
74
|
+
batchItemFailures.push({ itemIdentifier: record.dynamodb!.SequenceNumber! });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { batchItemFailures };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function reconcile() {
|
|
81
|
+
const store = dataStore();
|
|
82
|
+
const summary = { kind: 'ebk-key-value-reconciliation', delivered: 0, deliveryPending: 0, retryRequired: 0, rejected: 0, shards: 0 };
|
|
83
|
+
// One page per shard per run bounds work and prevents a poison-filled first
|
|
84
|
+
// page from starving the remainder forever. Wrap after the final page.
|
|
85
|
+
for (let shard = 0; shard < DYNAMO_OUTBOX_SHARDS; shard++) {
|
|
86
|
+
const id = 'shard-' + shard;
|
|
87
|
+
const previous = await store.get('system', 'event-reconciliation', id);
|
|
88
|
+
const checkpoint = previous?.value as { cursor: Cursor | null } | undefined;
|
|
89
|
+
if (previous && (!checkpoint || typeof checkpoint !== 'object' || !Object.prototype.hasOwnProperty.call(checkpoint, 'cursor')))
|
|
90
|
+
throw new Error('Retained reconciliation checkpoint is malformed.');
|
|
91
|
+
const page = await store.listPending({ shard, limit: 25, cursor: checkpoint?.cursor ?? undefined });
|
|
92
|
+
summary.rejected += page.rejected.length;
|
|
93
|
+
if (page.rejected.length) console.warn(JSON.stringify({
|
|
94
|
+
kind: 'ebk-key-value-invalid-intents', shard, records: page.rejected,
|
|
95
|
+
}));
|
|
96
|
+
for (const intent of page.events) {
|
|
97
|
+
try {
|
|
98
|
+
const result = await store.dispatch(intent.id, publish);
|
|
99
|
+
if (delivered(result)) summary.delivered++;
|
|
100
|
+
else if (result === 'busy') summary.deliveryPending++;
|
|
101
|
+
else summary.retryRequired++;
|
|
102
|
+
} catch {
|
|
103
|
+
// Retain the row and advance the page. A later sweep retries it.
|
|
104
|
+
summary.retryRequired++;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
await store.commit({ tenantId: 'system', changes: [{
|
|
108
|
+
action: 'put', collection: 'event-reconciliation', id,
|
|
109
|
+
expectedVersion: previous?.version ?? 0, value: { cursor: page.cursor ?? null },
|
|
110
|
+
}] });
|
|
111
|
+
summary.shards++;
|
|
112
|
+
}
|
|
113
|
+
const deletionCompletion = await reconcileRecordDeletions();
|
|
114
|
+
console.info(JSON.stringify({ ...summary, deletionCompletion }));
|
|
115
|
+
return { ...summary, deletionCompletion,
|
|
116
|
+
outcome: summary.rejected || summary.retryRequired || summary.deliveryPending || deletionCompletion.remaining || deletionCompletion.pending || deletionCompletion.failed ? 'partial' : 'completed' };
|
|
117
|
+
}
|
|
118
|
+
`;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=built-in-plan-key-value-delivery.js.map
|