@empire-builder-kit/nx 1.0.0-beta.9 → 1.0.0-rc.2
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/NOTICE +6 -0
- package/README.md +113 -3
- package/executors.json +30 -0
- package/package.json +84 -6
- package/src/api-contract/baseline-update.d.ts +82 -0
- package/src/api-contract/baseline-update.js +225 -0
- package/src/api-contract/baseline-update.js.map +1 -0
- package/src/api-contract/compatibility.d.ts +30 -0
- package/src/api-contract/compatibility.js +643 -0
- package/src/api-contract/compatibility.js.map +1 -0
- package/src/api-contract/contract-graph.d.ts +35 -0
- package/src/api-contract/contract-graph.js +141 -0
- package/src/api-contract/contract-graph.js.map +1 -0
- package/src/api-contract/evidence.d.ts +23 -0
- package/src/api-contract/evidence.js +87 -0
- package/src/api-contract/evidence.js.map +1 -0
- package/src/api-contract/index.d.ts +6 -0
- package/src/api-contract/index.js +10 -0
- package/src/api-contract/index.js.map +1 -0
- package/src/api-contract/model.d.ts +144 -0
- package/src/api-contract/model.fixtures.d.ts +2 -0
- package/src/api-contract/model.fixtures.js +81 -0
- package/src/api-contract/model.fixtures.js.map +1 -0
- package/src/api-contract/model.js +993 -0
- package/src/api-contract/model.js.map +1 -0
- package/src/api-contract/rollout.d.ts +32 -0
- package/src/api-contract/rollout.js +88 -0
- package/src/api-contract/rollout.js.map +1 -0
- package/src/authorization/compatibility.d.ts +62 -0
- package/src/authorization/compatibility.js +175 -0
- package/src/authorization/compatibility.js.map +1 -0
- package/src/authorization/index.d.ts +1 -0
- package/src/authorization/index.js +5 -0
- package/src/authorization/index.js.map +1 -0
- package/src/blueprints/execution.d.ts +25 -1
- package/src/blueprints/execution.js +246 -83
- package/src/blueprints/execution.js.map +1 -1
- package/src/blueprints/external-sandbox.d.ts +53 -0
- package/src/blueprints/external-sandbox.js +1182 -0
- package/src/blueprints/external-sandbox.js.map +1 -0
- package/src/blueprints/manifest.d.ts +42 -2
- package/src/blueprints/manifest.js +82 -27
- package/src/blueprints/manifest.js.map +1 -1
- package/src/blueprints/resolver.d.ts +14 -5
- package/src/blueprints/resolver.js +228 -68
- package/src/blueprints/resolver.js.map +1 -1
- package/src/cli/agent-command.d.ts +30 -0
- package/src/cli/agent-command.js +204 -0
- package/src/cli/agent-command.js.map +1 -0
- package/src/cli/agent-runtime.d.ts +25 -0
- package/src/cli/agent-runtime.js +488 -0
- package/src/cli/agent-runtime.js.map +1 -0
- package/src/cli/blueprint-authority.d.ts +9 -0
- package/src/cli/blueprint-authority.js +43 -0
- package/src/cli/blueprint-authority.js.map +1 -0
- package/src/cli/blueprint-command.d.ts +24 -0
- package/src/cli/blueprint-command.js +63 -0
- package/src/cli/blueprint-command.js.map +1 -0
- package/src/cli/bootstrap.d.ts +31 -0
- package/src/cli/bootstrap.js +165 -0
- package/src/cli/bootstrap.js.map +1 -0
- package/src/cli/dev-command.d.ts +92 -0
- package/src/cli/dev-command.js +561 -0
- package/src/cli/dev-command.js.map +1 -0
- package/src/cli/main.d.ts +131 -0
- package/src/cli/main.js +1199 -0
- package/src/cli/main.js.map +1 -0
- package/src/cli/output.d.ts +19 -0
- package/src/cli/output.js +51 -0
- package/src/cli/output.js.map +1 -0
- package/src/cli/personal-secret-runtime.d.ts +81 -0
- package/src/cli/personal-secret-runtime.js +567 -0
- package/src/cli/personal-secret-runtime.js.map +1 -0
- package/src/cli/secret-terminal.d.ts +12 -0
- package/src/cli/secret-terminal.js +128 -0
- package/src/cli/secret-terminal.js.map +1 -0
- package/src/container-images.d.ts +1 -0
- package/src/container-images.js +5 -0
- package/src/container-images.js.map +1 -0
- package/src/data-lifecycle/index.d.ts +3 -0
- package/src/data-lifecycle/index.js +7 -0
- package/src/data-lifecycle/index.js.map +1 -0
- package/src/data-lifecycle/model.d.ts +172 -0
- package/src/data-lifecycle/model.fixtures.d.ts +2 -0
- package/src/data-lifecycle/model.fixtures.js +244 -0
- package/src/data-lifecycle/model.fixtures.js.map +1 -0
- package/src/data-lifecycle/model.js +95 -0
- package/src/data-lifecycle/model.js.map +1 -0
- package/src/data-lifecycle/sensitive-artifacts.d.ts +28 -0
- package/src/data-lifecycle/sensitive-artifacts.js +146 -0
- package/src/data-lifecycle/sensitive-artifacts.js.map +1 -0
- package/src/data-lifecycle/validation.d.ts +32 -0
- package/src/data-lifecycle/validation.js +837 -0
- package/src/data-lifecycle/validation.js.map +1 -0
- package/src/database-retirement/contract.d.ts +68 -0
- package/src/database-retirement/contract.js +166 -0
- package/src/database-retirement/contract.js.map +1 -0
- package/src/database-retirement/index.d.ts +1 -0
- package/src/database-retirement/index.js +5 -0
- package/src/database-retirement/index.js.map +1 -0
- package/src/deterministic-order.d.ts +2 -0
- package/src/deterministic-order.js +7 -0
- package/src/deterministic-order.js.map +1 -0
- package/src/developer-agent/context.d.ts +27 -0
- package/src/developer-agent/context.js +497 -0
- package/src/developer-agent/context.js.map +1 -0
- package/src/developer-agent/generator-planning.d.ts +34 -0
- package/src/developer-agent/generator-planning.js +645 -0
- package/src/developer-agent/generator-planning.js.map +1 -0
- package/src/developer-agent/generator-preview.d.ts +8 -0
- package/src/developer-agent/generator-preview.js +650 -0
- package/src/developer-agent/generator-preview.js.map +1 -0
- package/src/developer-agent/git-inspection.d.ts +108 -0
- package/src/developer-agent/git-inspection.js +815 -0
- package/src/developer-agent/git-inspection.js.map +1 -0
- package/src/developer-agent/handoff.d.ts +43 -0
- package/src/developer-agent/handoff.js +698 -0
- package/src/developer-agent/handoff.js.map +1 -0
- package/src/developer-agent/index.d.ts +10 -0
- package/src/developer-agent/index.js +14 -0
- package/src/developer-agent/index.js.map +1 -0
- package/src/developer-agent/model.d.ts +481 -0
- package/src/developer-agent/model.js +143 -0
- package/src/developer-agent/model.js.map +1 -0
- package/src/developer-agent/operation-registry.d.ts +45 -0
- package/src/developer-agent/operation-registry.js +1255 -0
- package/src/developer-agent/operation-registry.js.map +1 -0
- package/src/developer-agent/ordering.d.ts +9 -0
- package/src/developer-agent/ordering.js +15 -0
- package/src/developer-agent/ordering.js.map +1 -0
- package/src/developer-agent/planning.d.ts +18 -0
- package/src/developer-agent/planning.js +447 -0
- package/src/developer-agent/planning.js.map +1 -0
- package/src/developer-agent/preview-changes.d.ts +26 -0
- package/src/developer-agent/preview-changes.js +203 -0
- package/src/developer-agent/preview-changes.js.map +1 -0
- package/src/developer-agent/preview-source.d.ts +2 -0
- package/src/developer-agent/preview-source.js +16 -0
- package/src/developer-agent/preview-source.js.map +1 -0
- package/src/developer-agent/validation.d.ts +18 -0
- package/src/developer-agent/validation.js +2617 -0
- package/src/developer-agent/validation.js.map +1 -0
- package/src/developer-agent/workspace-inspection.d.ts +20 -0
- package/src/developer-agent/workspace-inspection.js +1134 -0
- package/src/developer-agent/workspace-inspection.js.map +1 -0
- package/src/developer-agent/workspace-path.d.ts +6 -0
- package/src/developer-agent/workspace-path.js +22 -0
- package/src/developer-agent/workspace-path.js.map +1 -0
- package/src/executors/blueprint-trust/blueprint-trust.d.ts +29 -0
- package/src/executors/blueprint-trust/blueprint-trust.js +115 -0
- package/src/executors/blueprint-trust/blueprint-trust.js.map +1 -0
- package/src/executors/blueprint-trust/schema.json +27 -0
- package/src/executors/blueprint-verification/blueprint-verification.d.ts +13 -0
- package/src/executors/blueprint-verification/blueprint-verification.js +110 -0
- package/src/executors/blueprint-verification/blueprint-verification.js.map +1 -0
- package/src/executors/blueprint-verification/schema.json +44 -0
- package/src/executors/deployment-plan/deployment-plan.js +1 -0
- package/src/executors/deployment-plan/deployment-plan.js.map +1 -1
- package/src/executors/dev-doctor/dev-doctor.d.ts +19 -5
- package/src/executors/dev-doctor/dev-doctor.js +186 -32
- package/src/executors/dev-doctor/dev-doctor.js.map +1 -1
- package/src/executors/dev-doctor/schema.json +1 -6
- package/src/executors/dev-setup/dev-setup.d.ts +7 -1
- package/src/executors/dev-setup/dev-setup.js +14 -3
- package/src/executors/dev-setup/dev-setup.js.map +1 -1
- package/src/executors/developer-context/developer-context.d.ts +38 -0
- package/src/executors/developer-context/developer-context.js +78 -0
- package/src/executors/developer-context/developer-context.js.map +1 -0
- package/src/executors/developer-context/schema.json +87 -0
- package/src/executors/fleet/fleet.d.ts +12 -43
- package/src/executors/fleet/fleet.js +120 -500
- package/src/executors/fleet/fleet.js.map +1 -1
- package/src/executors/fleet/schema.json +64 -14
- package/src/executors/github-bootstrap/github-bootstrap.d.ts +1 -0
- package/src/executors/github-bootstrap/github-bootstrap.js +1 -0
- package/src/executors/github-bootstrap/github-bootstrap.js.map +1 -1
- package/src/executors/github-bootstrap/schema.json +5 -0
- package/src/executors/infrastructure-plan/infrastructure-plan.d.ts +144 -0
- package/src/executors/infrastructure-plan/infrastructure-plan.js +3320 -0
- package/src/executors/infrastructure-plan/infrastructure-plan.js.map +1 -0
- package/src/executors/infrastructure-plan/schema.json +78 -0
- package/src/executors/secret-guardrails/schema.json +6 -0
- package/src/executors/secret-guardrails/secret-guardrails.d.ts +3 -0
- package/src/executors/secret-guardrails/secret-guardrails.js +44 -0
- package/src/executors/secret-guardrails/secret-guardrails.js.map +1 -0
- package/src/executors/secret-metadata/schema.json +31 -0
- package/src/executors/secret-metadata/secret-metadata.d.ts +32 -0
- package/src/executors/secret-metadata/secret-metadata.js +131 -0
- package/src/executors/secret-metadata/secret-metadata.js.map +1 -0
- package/src/executors/sst-lifecycle/logging-preflight.d.ts +17 -0
- package/src/executors/sst-lifecycle/logging-preflight.js +196 -0
- package/src/executors/sst-lifecycle/logging-preflight.js.map +1 -0
- package/src/executors/sst-lifecycle/run-sst-command.d.ts +23 -3
- package/src/executors/sst-lifecycle/run-sst-command.js +113 -10
- package/src/executors/sst-lifecycle/run-sst-command.js.map +1 -1
- package/src/executors/sst-lifecycle/schema.json +9 -5
- package/src/executors/sst-lifecycle/secret-preflight.d.ts +25 -0
- package/src/executors/sst-lifecycle/secret-preflight.js +119 -0
- package/src/executors/sst-lifecycle/secret-preflight.js.map +1 -0
- package/src/foundation/discovery.d.ts +17 -1
- package/src/foundation/discovery.js +16 -1
- package/src/foundation/discovery.js.map +1 -1
- package/src/foundation/index.d.ts +3 -0
- package/src/foundation/index.js +3 -0
- package/src/foundation/index.js.map +1 -1
- package/src/foundation/local-database-contract.d.ts +1 -0
- package/src/foundation/local-database-contract.js +3 -0
- package/src/foundation/local-database-contract.js.map +1 -1
- package/src/foundation/logging-preflight.d.ts +30 -0
- package/src/foundation/logging-preflight.js +138 -0
- package/src/foundation/logging-preflight.js.map +1 -0
- package/src/foundation/logging.d.ts +70 -0
- package/src/foundation/logging.js +133 -0
- package/src/foundation/logging.js.map +1 -0
- package/src/foundation/preflight.d.ts +9 -0
- package/src/foundation/preflight.js +58 -2
- package/src/foundation/preflight.js.map +1 -1
- package/src/foundation/provision-database-bindings.d.ts +9 -1
- package/src/foundation/provision-database-bindings.js +25 -5
- package/src/foundation/provision-database-bindings.js.map +1 -1
- package/src/foundation/provision-logical-database.d.ts +10 -2
- package/src/foundation/provision-logical-database.js +68 -113
- package/src/foundation/provision-logical-database.js.map +1 -1
- package/src/foundation/router-origin-proof.d.ts +2 -0
- package/src/foundation/router-origin-proof.js +25 -0
- package/src/foundation/router-origin-proof.js.map +1 -0
- package/src/foundation/stages.d.ts +1 -0
- package/src/foundation/stages.js +9 -1
- package/src/foundation/stages.js.map +1 -1
- package/src/frontend-security/index.d.ts +1 -0
- package/src/frontend-security/index.js +5 -0
- package/src/frontend-security/index.js.map +1 -0
- package/src/frontend-security/security-headers.d.ts +50 -0
- package/src/frontend-security/security-headers.js +418 -0
- package/src/frontend-security/security-headers.js.map +1 -0
- package/src/generators/feature-registry.d.ts +13 -0
- package/src/generators/feature-registry.js +73 -0
- package/src/generators/feature-registry.js.map +1 -0
- package/src/generators/function/function.d.ts +1 -1
- package/src/generators/function/function.js +51 -6
- package/src/generators/function/function.js.map +1 -1
- package/src/generators/function/schema.d.ts +1 -0
- package/src/generators/function/schema.json +6 -1
- package/src/generators/logging-registry.d.ts +13 -0
- package/src/generators/logging-registry.js +62 -0
- package/src/generators/logging-registry.js.map +1 -0
- package/src/generators/post-generation-install.d.ts +67 -0
- package/src/generators/post-generation-install.js +3308 -0
- package/src/generators/post-generation-install.js.map +1 -0
- package/src/generators/preset/generator.js +3 -0
- package/src/generators/preset/generator.js.map +1 -1
- package/src/generators/preset/tool-versions.d.ts +18 -0
- package/src/generators/preset/tool-versions.js +22 -0
- package/src/generators/preset/tool-versions.js.map +1 -0
- package/src/generators/preset/workspace-files-developer-agent.d.ts +14 -0
- package/src/generators/preset/workspace-files-developer-agent.js +511 -0
- package/src/generators/preset/workspace-files-developer-agent.js.map +1 -0
- package/src/generators/preset/workspace-files-foundation-workload-roles.d.ts +6 -0
- package/src/generators/preset/workspace-files-foundation-workload-roles.js +406 -0
- package/src/generators/preset/workspace-files-foundation-workload-roles.js.map +1 -0
- package/src/generators/preset/workspace-files-performance-cost.d.ts +66 -0
- package/src/generators/preset/workspace-files-performance-cost.js +866 -0
- package/src/generators/preset/workspace-files-performance-cost.js.map +1 -0
- package/src/generators/preset/workspace-files-performance-load.d.ts +73 -0
- package/src/generators/preset/workspace-files-performance-load.js +801 -0
- package/src/generators/preset/workspace-files-performance-load.js.map +1 -0
- package/src/generators/preset/workspace-files-workload-role-baseline.d.ts +9 -0
- package/src/generators/preset/workspace-files-workload-role-baseline.js +1102 -0
- package/src/generators/preset/workspace-files-workload-role-baseline.js.map +1 -0
- package/src/generators/preset/workspace-files.d.ts +14 -1
- package/src/generators/preset/workspace-files.js +8189 -245
- package/src/generators/preset/workspace-files.js.map +1 -1
- package/src/generators/repository-policy/repository-policy.js +3 -47
- package/src/generators/repository-policy/repository-policy.js.map +1 -1
- package/src/generators/slice/built-in-plan-api-contract-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-api-contract-files.js +2656 -0
- package/src/generators/slice/built-in-plan-api-contract-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-api-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-api-files.js +1186 -0
- package/src/generators/slice/built-in-plan-api-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-async-work-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-async-work-files.js +7903 -0
- package/src/generators/slice/built-in-plan-async-work-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-authorization-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-authorization-files.js +2314 -0
- package/src/generators/slice/built-in-plan-authorization-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +559 -0
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-data-deletion-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-data-deletion-files.js +1101 -0
- package/src/generators/slice/built-in-plan-data-deletion-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-data-export-infrastructure-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js +249 -0
- package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-data-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-data-files.js +1530 -0
- package/src/generators/slice/built-in-plan-data-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-data-lifecycle-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-data-lifecycle-files.js +750 -0
- package/src/generators/slice/built-in-plan-data-lifecycle-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js +828 -0
- package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-data-retention-job-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-data-retention-job-files.js +2604 -0
- package/src/generators/slice/built-in-plan-data-retention-job-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-data-retention-launcher-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-data-retention-launcher-files.js +144 -0
- package/src/generators/slice/built-in-plan-data-retention-launcher-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-data-rights-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-data-rights-files.js +4628 -0
- package/src/generators/slice/built-in-plan-data-rights-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-database-lifecycle-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-database-lifecycle-files.js +1901 -0
- package/src/generators/slice/built-in-plan-database-lifecycle-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js +485 -0
- package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-foundation-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-foundation-files.js +184 -0
- package/src/generators/slice/built-in-plan-foundation-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-frontend-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-frontend-files.js +2233 -0
- package/src/generators/slice/built-in-plan-frontend-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-infrastructure-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-infrastructure-files.js +3038 -0
- package/src/generators/slice/built-in-plan-infrastructure-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-infrastructure-safety-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-infrastructure-safety-files.js +312 -0
- package/src/generators/slice/built-in-plan-infrastructure-safety-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-metadata-files.d.ts +3 -0
- package/src/generators/slice/built-in-plan-metadata-files.js +219 -0
- package/src/generators/slice/built-in-plan-metadata-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-operations-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-operations-files.js +630 -0
- package/src/generators/slice/built-in-plan-operations-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-performance-files.d.ts +3 -0
- package/src/generators/slice/built-in-plan-performance-files.js +3293 -0
- package/src/generators/slice/built-in-plan-performance-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-project-files.d.ts +3 -0
- package/src/generators/slice/built-in-plan-project-files.js +979 -0
- package/src/generators/slice/built-in-plan-project-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-release-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-release-files.js +6387 -0
- package/src/generators/slice/built-in-plan-release-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-reliability-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-reliability-files.js +4499 -0
- package/src/generators/slice/built-in-plan-reliability-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-secret-runtime-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-secret-runtime-files.js +1004 -0
- package/src/generators/slice/built-in-plan-secret-runtime-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-shared.d.ts +53 -0
- package/src/generators/slice/built-in-plan-shared.js +113 -0
- package/src/generators/slice/built-in-plan-shared.js.map +1 -0
- package/src/generators/slice/built-in-plan-testing-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-testing-files.js +2356 -0
- package/src/generators/slice/built-in-plan-testing-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-verification-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-verification-files.js +358 -0
- package/src/generators/slice/built-in-plan-verification-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-web-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-web-files.js +430 -0
- package/src/generators/slice/built-in-plan-web-files.js.map +1 -0
- package/src/generators/slice/built-in-plan-workload-role-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-workload-role-files.js +2356 -0
- package/src/generators/slice/built-in-plan-workload-role-files.js.map +1 -0
- package/src/generators/slice/built-in-plan.d.ts +8 -0
- package/src/generators/slice/built-in-plan.js +264 -0
- package/src/generators/slice/built-in-plan.js.map +1 -0
- package/src/generators/slice/schema.json +1 -1
- package/src/generators/slice/slice.d.ts +1 -22
- package/src/generators/slice/slice.js +185 -1510
- package/src/generators/slice/slice.js.map +1 -1
- package/src/generators/upgrade/schema.d.ts +2 -0
- package/src/generators/upgrade/schema.json +11 -1
- package/src/generators/upgrade/upgrade.d.ts +51 -1
- package/src/generators/upgrade/upgrade.js +820 -82
- package/src/generators/upgrade/upgrade.js.map +1 -1
- package/src/generators/workload/frozen-container.d.ts +8 -0
- package/src/generators/workload/frozen-container.js +36 -0
- package/src/generators/workload/frozen-container.js.map +1 -0
- package/src/generators/workload/performance-registration.d.ts +32 -0
- package/src/generators/workload/performance-registration.js +226 -0
- package/src/generators/workload/performance-registration.js.map +1 -0
- package/src/generators/workload/reliability-registration.d.ts +16 -0
- package/src/generators/workload/reliability-registration.js +150 -0
- package/src/generators/workload/reliability-registration.js.map +1 -0
- package/src/generators/workload/schema.d.ts +1 -0
- package/src/generators/workload/schema.json +6 -1
- package/src/generators/workload/service-performance-files.d.ts +4 -0
- package/src/generators/workload/service-performance-files.js +1846 -0
- package/src/generators/workload/service-performance-files.js.map +1 -0
- package/src/generators/workload/workload.d.ts +3 -2
- package/src/generators/workload/workload.js +1083 -18
- package/src/generators/workload/workload.js.map +1 -1
- package/src/git-policy/action-pins.d.ts +10 -0
- package/src/git-policy/action-pins.js +10 -0
- package/src/git-policy/action-pins.js.map +1 -1
- package/src/git-policy/github-bootstrap.d.ts +14 -1
- package/src/git-policy/github-bootstrap.js +274 -46
- package/src/git-policy/github-bootstrap.js.map +1 -1
- package/src/index.d.ts +2 -0
- package/src/index.js +2 -0
- package/src/index.js.map +1 -1
- package/src/infrastructure-change/convergence.d.ts +13 -0
- package/src/infrastructure-change/convergence.js +85 -0
- package/src/infrastructure-change/convergence.js.map +1 -0
- package/src/infrastructure-change/drift.d.ts +13 -0
- package/src/infrastructure-change/drift.js +112 -0
- package/src/infrastructure-change/drift.js.map +1 -0
- package/src/infrastructure-change/index.d.ts +7 -0
- package/src/infrastructure-change/index.js +11 -0
- package/src/infrastructure-change/index.js.map +1 -0
- package/src/infrastructure-change/model.d.ts +253 -0
- package/src/infrastructure-change/model.js +28 -0
- package/src/infrastructure-change/model.js.map +1 -0
- package/src/infrastructure-change/normalize.d.ts +12 -0
- package/src/infrastructure-change/normalize.js +245 -0
- package/src/infrastructure-change/normalize.js.map +1 -0
- package/src/infrastructure-change/plan.d.ts +40 -0
- package/src/infrastructure-change/plan.js +361 -0
- package/src/infrastructure-change/plan.js.map +1 -0
- package/src/infrastructure-change/risk.d.ts +7 -0
- package/src/infrastructure-change/risk.js +269 -0
- package/src/infrastructure-change/risk.js.map +1 -0
- package/src/infrastructure-change/summary.d.ts +20 -0
- package/src/infrastructure-change/summary.js +130 -0
- package/src/infrastructure-change/summary.js.map +1 -0
- package/src/local-dev/context-discovery.d.ts +2 -0
- package/src/local-dev/context-discovery.js +59 -0
- package/src/local-dev/context-discovery.js.map +1 -0
- package/src/local-dev/contract-validation.d.ts +15 -0
- package/src/local-dev/contract-validation.js +84 -0
- package/src/local-dev/contract-validation.js.map +1 -0
- package/src/local-dev/contracts.d.ts +10 -1
- package/src/local-dev/contracts.js +57 -5
- package/src/local-dev/contracts.js.map +1 -1
- package/src/local-dev/control-contract.d.ts +67 -0
- package/src/local-dev/control-contract.js +180 -0
- package/src/local-dev/control-contract.js.map +1 -0
- package/src/local-dev/dev-command-core.d.ts +60 -0
- package/src/local-dev/dev-command-core.js +89 -0
- package/src/local-dev/dev-command-core.js.map +1 -0
- package/src/local-dev/effects.d.ts +16 -0
- package/src/local-dev/effects.js +63 -0
- package/src/local-dev/effects.js.map +1 -0
- package/src/local-dev/environment-inspector.d.ts +125 -0
- package/src/local-dev/environment-inspector.js +687 -0
- package/src/local-dev/environment-inspector.js.map +1 -0
- package/src/local-dev/environment-state.d.ts +113 -0
- package/src/local-dev/environment-state.js +463 -0
- package/src/local-dev/environment-state.js.map +1 -0
- package/src/local-dev/errors.d.ts +6 -0
- package/src/local-dev/errors.js +41 -0
- package/src/local-dev/errors.js.map +1 -0
- package/src/local-dev/fleet-client.d.ts +33 -0
- package/src/local-dev/fleet-client.js +156 -0
- package/src/local-dev/fleet-client.js.map +1 -0
- package/src/local-dev/fleet-configuration.d.ts +13 -0
- package/src/local-dev/fleet-configuration.js +71 -0
- package/src/local-dev/fleet-configuration.js.map +1 -0
- package/src/local-dev/fleet-inventory.d.ts +56 -0
- package/src/local-dev/fleet-inventory.js +469 -0
- package/src/local-dev/fleet-inventory.js.map +1 -0
- package/src/local-dev/fleet-ipc.d.ts +28 -0
- package/src/local-dev/fleet-ipc.js +267 -0
- package/src/local-dev/fleet-ipc.js.map +1 -0
- package/src/local-dev/fleet-launcher.d.ts +53 -0
- package/src/local-dev/fleet-launcher.js +379 -0
- package/src/local-dev/fleet-launcher.js.map +1 -0
- package/src/local-dev/fleet-lock.d.ts +41 -0
- package/src/local-dev/fleet-lock.js +135 -0
- package/src/local-dev/fleet-lock.js.map +1 -0
- package/src/local-dev/fleet-logs.d.ts +66 -0
- package/src/local-dev/fleet-logs.js +542 -0
- package/src/local-dev/fleet-logs.js.map +1 -0
- package/src/local-dev/fleet-process.d.ts +50 -0
- package/src/local-dev/fleet-process.js +289 -0
- package/src/local-dev/fleet-process.js.map +1 -0
- package/src/local-dev/fleet-runtime.d.ts +22 -0
- package/src/local-dev/fleet-runtime.js +368 -0
- package/src/local-dev/fleet-runtime.js.map +1 -0
- package/src/local-dev/fleet-session.d.ts +70 -0
- package/src/local-dev/fleet-session.js +301 -0
- package/src/local-dev/fleet-session.js.map +1 -0
- package/src/local-dev/fleet-slice-runner-daemon.d.ts +1 -0
- package/src/local-dev/fleet-slice-runner-daemon.js +21 -0
- package/src/local-dev/fleet-slice-runner-daemon.js.map +1 -0
- package/src/local-dev/fleet-startup.d.ts +22 -0
- package/src/local-dev/fleet-startup.js +114 -0
- package/src/local-dev/fleet-startup.js.map +1 -0
- package/src/local-dev/fleet-state.d.ts +62 -0
- package/src/local-dev/fleet-state.js +297 -0
- package/src/local-dev/fleet-state.js.map +1 -0
- package/src/local-dev/fleet-supervisor-daemon.d.ts +1 -0
- package/src/local-dev/fleet-supervisor-daemon.js +41 -0
- package/src/local-dev/fleet-supervisor-daemon.js.map +1 -0
- package/src/local-dev/fleet-supervisor.d.ts +52 -0
- package/src/local-dev/fleet-supervisor.js +384 -0
- package/src/local-dev/fleet-supervisor.js.map +1 -0
- package/src/local-dev/gateway.js +1 -1
- package/src/local-dev/gateway.js.map +1 -1
- package/src/local-dev/identity.js +4 -6
- package/src/local-dev/identity.js.map +1 -1
- package/src/local-dev/index.d.ts +1 -0
- package/src/local-dev/index.js +1 -0
- package/src/local-dev/index.js.map +1 -1
- package/src/local-dev/local-context.d.ts +62 -0
- package/src/local-dev/local-context.js +299 -0
- package/src/local-dev/local-context.js.map +1 -0
- package/src/local-dev/nx-workspace.d.ts +45 -0
- package/src/local-dev/nx-workspace.js +228 -0
- package/src/local-dev/nx-workspace.js.map +1 -0
- package/src/local-dev/personal-secret-ledger.d.ts +75 -0
- package/src/local-dev/personal-secret-ledger.js +391 -0
- package/src/local-dev/personal-secret-ledger.js.map +1 -0
- package/src/local-dev/personal-stage.d.ts +18 -0
- package/src/local-dev/personal-stage.js +129 -0
- package/src/local-dev/personal-stage.js.map +1 -0
- package/src/local-dev/private-state.d.ts +17 -0
- package/src/local-dev/private-state.js +254 -0
- package/src/local-dev/private-state.js.map +1 -0
- package/src/local-dev/process-identity.d.ts +9 -0
- package/src/local-dev/process-identity.js +79 -0
- package/src/local-dev/process-identity.js.map +1 -0
- package/src/local-dev/runtime-paths.d.ts +17 -0
- package/src/local-dev/runtime-paths.js +33 -0
- package/src/local-dev/runtime-paths.js.map +1 -0
- package/src/local-dev/secret-slice-claim.d.ts +17 -0
- package/src/local-dev/secret-slice-claim.js +75 -0
- package/src/local-dev/secret-slice-claim.js.map +1 -0
- package/src/local-dev/slice-claim.d.ts +36 -0
- package/src/local-dev/slice-claim.js +199 -0
- package/src/local-dev/slice-claim.js.map +1 -0
- package/src/local-dev/slice-runner.d.ts +121 -0
- package/src/local-dev/slice-runner.js +715 -0
- package/src/local-dev/slice-runner.js.map +1 -0
- package/src/local-dev/tls.js +51 -17
- package/src/local-dev/tls.js.map +1 -1
- package/src/ownership/composition.d.ts +5 -2
- package/src/ownership/composition.js +26 -3
- package/src/ownership/composition.js.map +1 -1
- package/src/ownership/hash.d.ts +1 -1
- package/src/ownership/hash.js +3 -1
- package/src/ownership/hash.js.map +1 -1
- package/src/ownership/record.d.ts +32 -1
- package/src/ownership/record.js +139 -2
- package/src/ownership/record.js.map +1 -1
- package/src/performance/cost-collection.d.ts +225 -0
- package/src/performance/cost-collection.js +825 -0
- package/src/performance/cost-collection.js.map +1 -0
- package/src/performance/cost-controls.d.ts +131 -0
- package/src/performance/cost-controls.js +336 -0
- package/src/performance/cost-controls.js.map +1 -0
- package/src/performance/evidence.d.ts +140 -0
- package/src/performance/evidence.js +599 -0
- package/src/performance/evidence.js.map +1 -0
- package/src/performance/index.d.ts +10 -0
- package/src/performance/index.js +14 -0
- package/src/performance/index.js.map +1 -0
- package/src/performance/load-safety.d.ts +84 -0
- package/src/performance/load-safety.js +346 -0
- package/src/performance/load-safety.js.map +1 -0
- package/src/performance/model.d.ts +129 -0
- package/src/performance/model.js +452 -0
- package/src/performance/model.js.map +1 -0
- package/src/performance/pr-artifact-budget.d.ts +42 -0
- package/src/performance/pr-artifact-budget.js +151 -0
- package/src/performance/pr-artifact-budget.js.map +1 -0
- package/src/performance/runner.d.ts +75 -0
- package/src/performance/runner.js +227 -0
- package/src/performance/runner.js.map +1 -0
- package/src/performance/scenario-endpoint.d.ts +1 -0
- package/src/performance/scenario-endpoint.js +5 -0
- package/src/performance/scenario-endpoint.js.map +1 -0
- package/src/performance/scenario.d.ts +193 -0
- package/src/performance/scenario.js +736 -0
- package/src/performance/scenario.js.map +1 -0
- package/src/performance/test-fixtures.d.ts +7 -0
- package/src/performance/test-fixtures.js +169 -0
- package/src/performance/test-fixtures.js.map +1 -0
- package/src/performance/unit-cost.d.ts +105 -0
- package/src/performance/unit-cost.js +364 -0
- package/src/performance/unit-cost.js.map +1 -0
- package/src/release/index.d.ts +4 -0
- package/src/release/index.js +8 -0
- package/src/release/index.js.map +1 -0
- package/src/release/lifecycle.d.ts +132 -0
- package/src/release/lifecycle.js +411 -0
- package/src/release/lifecycle.js.map +1 -0
- package/src/release/manifest.d.ts +148 -0
- package/src/release/manifest.js +503 -0
- package/src/release/manifest.js.map +1 -0
- package/src/release/rollout.d.ts +170 -0
- package/src/release/rollout.js +578 -0
- package/src/release/rollout.js.map +1 -0
- package/src/release/source-authority.d.ts +19 -0
- package/src/release/source-authority.js +195 -0
- package/src/release/source-authority.js.map +1 -0
- package/src/repository-policy/deployment-plan.d.ts +2 -1
- package/src/repository-policy/deployment-plan.js +2 -0
- package/src/repository-policy/deployment-plan.js.map +1 -1
- package/src/repository-policy/policy.d.ts +1 -1
- package/src/repository-policy/policy.js +29 -25
- package/src/repository-policy/policy.js.map +1 -1
- package/src/schemas/aggregate-count-evidence-v1.json +135 -0
- package/src/schemas/api-contract-baseline-v1.json +674 -0
- package/src/schemas/api-contract-compatibility-report-v1.json +126 -0
- package/src/schemas/aws-billing-actual-evidence-v1.json +407 -0
- package/src/schemas/blueprint-v1.json +18 -1
- package/src/schemas/data-lifecycle-policy-v1.json +517 -0
- package/src/schemas/generated-ownership-v2.json +22 -0
- package/src/schemas/performance-policy-v1.json +335 -0
- package/src/schemas/repository-policy-v1.json +1 -0
- package/src/schemas/testing-evidence-quarantine-v1.json +168 -0
- package/src/schemas/testing-evidence-result-v1.json +319 -0
- package/src/secrets/adapters.d.ts +49 -0
- package/src/secrets/adapters.js +184 -0
- package/src/secrets/adapters.js.map +1 -0
- package/src/secrets/aws-control-plane.d.ts +20 -0
- package/src/secrets/aws-control-plane.js +255 -0
- package/src/secrets/aws-control-plane.js.map +1 -0
- package/src/secrets/declaration-verification.d.ts +11 -0
- package/src/secrets/declaration-verification.js +68 -0
- package/src/secrets/declaration-verification.js.map +1 -0
- package/src/secrets/guardrails.d.ts +26 -0
- package/src/secrets/guardrails.js +328 -0
- package/src/secrets/guardrails.js.map +1 -0
- package/src/secrets/index.d.ts +12 -0
- package/src/secrets/index.js +16 -0
- package/src/secrets/index.js.map +1 -0
- package/src/secrets/metadata.d.ts +48 -0
- package/src/secrets/metadata.js +171 -0
- package/src/secrets/metadata.js.map +1 -0
- package/src/secrets/model.d.ts +49 -0
- package/src/secrets/model.js +125 -0
- package/src/secrets/model.js.map +1 -0
- package/src/secrets/operator.d.ts +101 -0
- package/src/secrets/operator.js +229 -0
- package/src/secrets/operator.js.map +1 -0
- package/src/secrets/permissions.d.ts +34 -0
- package/src/secrets/permissions.js +86 -0
- package/src/secrets/permissions.js.map +1 -0
- package/src/secrets/personal-command.d.ts +83 -0
- package/src/secrets/personal-command.js +197 -0
- package/src/secrets/personal-command.js.map +1 -0
- package/src/secrets/personal-ledger.d.ts +21 -0
- package/src/secrets/personal-ledger.js +135 -0
- package/src/secrets/personal-ledger.js.map +1 -0
- package/src/secrets/registry.d.ts +9 -0
- package/src/secrets/registry.js +86 -0
- package/src/secrets/registry.js.map +1 -0
- package/src/secrets/verification.d.ts +18 -0
- package/src/secrets/verification.js +139 -0
- package/src/secrets/verification.js.map +1 -0
- package/src/secrets/workload-access.d.ts +46 -0
- package/src/secrets/workload-access.js +163 -0
- package/src/secrets/workload-access.js.map +1 -0
- package/src/supply-chain/action-pins.d.ts +26 -0
- package/src/supply-chain/action-pins.js +208 -0
- package/src/supply-chain/action-pins.js.map +1 -0
- package/src/supply-chain/blueprint-trust-git.d.ts +130 -0
- package/src/supply-chain/blueprint-trust-git.js +1581 -0
- package/src/supply-chain/blueprint-trust-git.js.map +1 -0
- package/src/supply-chain/blueprint-trust-process.d.ts +4 -0
- package/src/supply-chain/blueprint-trust-process.js +88 -0
- package/src/supply-chain/blueprint-trust-process.js.map +1 -0
- package/src/supply-chain/blueprint-trust.d.ts +354 -0
- package/src/supply-chain/blueprint-trust.js +1558 -0
- package/src/supply-chain/blueprint-trust.js.map +1 -0
- package/src/supply-chain/blueprint-verification-authority.d.ts +62 -0
- package/src/supply-chain/blueprint-verification-authority.js +588 -0
- package/src/supply-chain/blueprint-verification-authority.js.map +1 -0
- package/src/supply-chain/blueprint-verification-execution.d.ts +68 -0
- package/src/supply-chain/blueprint-verification-execution.js +624 -0
- package/src/supply-chain/blueprint-verification-execution.js.map +1 -0
- package/src/supply-chain/build-output.d.ts +35 -0
- package/src/supply-chain/build-output.js +398 -0
- package/src/supply-chain/build-output.js.map +1 -0
- package/src/supply-chain/common.d.ts +21 -0
- package/src/supply-chain/common.js +107 -0
- package/src/supply-chain/common.js.map +1 -0
- package/src/supply-chain/container-artifact-contract.d.ts +23 -0
- package/src/supply-chain/container-artifact-contract.js +208 -0
- package/src/supply-chain/container-artifact-contract.js.map +1 -0
- package/src/supply-chain/container-release.d.ts +119 -0
- package/src/supply-chain/container-release.js +496 -0
- package/src/supply-chain/container-release.js.map +1 -0
- package/src/supply-chain/ecr-applicability.d.ts +19 -0
- package/src/supply-chain/ecr-applicability.js +103 -0
- package/src/supply-chain/ecr-applicability.js.map +1 -0
- package/src/supply-chain/ecr-scanning.d.ts +83 -0
- package/src/supply-chain/ecr-scanning.js +321 -0
- package/src/supply-chain/ecr-scanning.js.map +1 -0
- package/src/supply-chain/ecs-deployed-image.d.ts +85 -0
- package/src/supply-chain/ecs-deployed-image.js +300 -0
- package/src/supply-chain/ecs-deployed-image.js.map +1 -0
- package/src/supply-chain/errors.d.ts +6 -0
- package/src/supply-chain/errors.js +15 -0
- package/src/supply-chain/errors.js.map +1 -0
- package/src/supply-chain/generated-blueprint-trust-command.d.ts +4 -0
- package/src/supply-chain/generated-blueprint-trust-command.js +1362 -0
- package/src/supply-chain/generated-blueprint-trust-command.js.map +1 -0
- package/src/supply-chain/generated-command.d.ts +1 -0
- package/src/supply-chain/generated-command.js +687 -0
- package/src/supply-chain/generated-command.js.map +1 -0
- package/src/supply-chain/generated-container-release-command.d.ts +16 -0
- package/src/supply-chain/generated-container-release-command.js +2087 -0
- package/src/supply-chain/generated-container-release-command.js.map +1 -0
- package/src/supply-chain/generated-ecr-collector-command.d.ts +21 -0
- package/src/supply-chain/generated-ecr-collector-command.js +1338 -0
- package/src/supply-chain/generated-ecr-collector-command.js.map +1 -0
- package/src/supply-chain/generated-files.d.ts +13 -0
- package/src/supply-chain/generated-files.js +401 -0
- package/src/supply-chain/generated-files.js.map +1 -0
- package/src/supply-chain/generated-scanner-command.d.ts +1 -0
- package/src/supply-chain/generated-scanner-command.js +718 -0
- package/src/supply-chain/generated-scanner-command.js.map +1 -0
- package/src/supply-chain/index.d.ts +28 -0
- package/src/supply-chain/index.js +35 -0
- package/src/supply-chain/index.js.map +1 -0
- package/src/supply-chain/inventory.d.ts +79 -0
- package/src/supply-chain/inventory.js +717 -0
- package/src/supply-chain/inventory.js.map +1 -0
- package/src/supply-chain/license-expression.d.ts +7 -0
- package/src/supply-chain/license-expression.js +128 -0
- package/src/supply-chain/license-expression.js.map +1 -0
- package/src/supply-chain/pnpm-toolchain-authority.d.ts +31 -0
- package/src/supply-chain/pnpm-toolchain-authority.js +316 -0
- package/src/supply-chain/pnpm-toolchain-authority.js.map +1 -0
- package/src/supply-chain/policy.d.ts +105 -0
- package/src/supply-chain/policy.js +301 -0
- package/src/supply-chain/policy.js.map +1 -0
- package/src/supply-chain/publication.d.ts +48 -0
- package/src/supply-chain/publication.js +85 -0
- package/src/supply-chain/publication.js.map +1 -0
- package/src/supply-chain/sbom.d.ts +228 -0
- package/src/supply-chain/sbom.js +826 -0
- package/src/supply-chain/sbom.js.map +1 -0
- package/src/supply-chain/scanner.d.ts +77 -0
- package/src/supply-chain/scanner.js +502 -0
- package/src/supply-chain/scanner.js.map +1 -0
- package/src/supply-chain/vendored-source.d.ts +95 -0
- package/src/supply-chain/vendored-source.js +315 -0
- package/src/supply-chain/vendored-source.js.map +1 -0
- package/src/testing-evidence/fixture-scope.d.ts +52 -0
- package/src/testing-evidence/fixture-scope.js +140 -0
- package/src/testing-evidence/fixture-scope.js.map +1 -0
- package/src/testing-evidence/index.d.ts +5 -0
- package/src/testing-evidence/index.js +9 -0
- package/src/testing-evidence/index.js.map +1 -0
- package/src/testing-evidence/quarantine.d.ts +48 -0
- package/src/testing-evidence/quarantine.js +253 -0
- package/src/testing-evidence/quarantine.js.map +1 -0
- package/src/testing-evidence/result.d.ts +108 -0
- package/src/testing-evidence/result.js +571 -0
- package/src/testing-evidence/result.js.map +1 -0
- package/src/testing-evidence/runner-adapter.d.ts +51 -0
- package/src/testing-evidence/runner-adapter.js +288 -0
- package/src/testing-evidence/runner-adapter.js.map +1 -0
- package/src/testing-evidence/sensitive-artifacts.d.ts +13 -0
- package/src/testing-evidence/sensitive-artifacts.js +93 -0
- package/src/testing-evidence/sensitive-artifacts.js.map +1 -0
- package/src/work-markers/work-markers.js +4 -3
- package/src/work-markers/work-markers.js.map +1 -1
- package/src/workload-roles/account-baseline.d.ts +132 -0
- package/src/workload-roles/account-baseline.js +743 -0
- package/src/workload-roles/account-baseline.js.map +1 -0
- package/src/workload-roles/authority.d.ts +38 -0
- package/src/workload-roles/authority.js +156 -0
- package/src/workload-roles/authority.js.map +1 -0
- package/src/workload-roles/aws-adapter.d.ts +99 -0
- package/src/workload-roles/aws-adapter.js +416 -0
- package/src/workload-roles/aws-adapter.js.map +1 -0
- package/src/workload-roles/aws-internal-handler.d.ts +27 -0
- package/src/workload-roles/aws-internal-handler.js +58 -0
- package/src/workload-roles/aws-internal-handler.js.map +1 -0
- package/src/workload-roles/aws-s3-state.d.ts +52 -0
- package/src/workload-roles/aws-s3-state.js +164 -0
- package/src/workload-roles/aws-s3-state.js.map +1 -0
- package/src/workload-roles/aws-sdk-control-plane.d.ts +12 -0
- package/src/workload-roles/aws-sdk-control-plane.js +291 -0
- package/src/workload-roles/aws-sdk-control-plane.js.map +1 -0
- package/src/workload-roles/boundary.d.ts +33 -0
- package/src/workload-roles/boundary.js +130 -0
- package/src/workload-roles/boundary.js.map +1 -0
- package/src/workload-roles/convergence.d.ts +66 -0
- package/src/workload-roles/convergence.js +186 -0
- package/src/workload-roles/convergence.js.map +1 -0
- package/src/workload-roles/effective-policy.d.ts +51 -0
- package/src/workload-roles/effective-policy.js +183 -0
- package/src/workload-roles/effective-policy.js.map +1 -0
- package/src/workload-roles/handler.d.ts +43 -0
- package/src/workload-roles/handler.js +130 -0
- package/src/workload-roles/handler.js.map +1 -0
- package/src/workload-roles/history.d.ts +47 -0
- package/src/workload-roles/history.js +324 -0
- package/src/workload-roles/history.js.map +1 -0
- package/src/workload-roles/index.d.ts +17 -0
- package/src/workload-roles/index.js +21 -0
- package/src/workload-roles/index.js.map +1 -0
- package/src/workload-roles/injection.d.ts +119 -0
- package/src/workload-roles/injection.js +495 -0
- package/src/workload-roles/injection.js.map +1 -0
- package/src/workload-roles/manifest.d.ts +54 -0
- package/src/workload-roles/manifest.js +343 -0
- package/src/workload-roles/manifest.js.map +1 -0
- package/src/workload-roles/provisioner.d.ts +70 -0
- package/src/workload-roles/provisioner.js +231 -0
- package/src/workload-roles/provisioner.js.map +1 -0
- package/src/workload-roles/registration.d.ts +101 -0
- package/src/workload-roles/registration.js +457 -0
- package/src/workload-roles/registration.js.map +1 -0
- package/src/workload-roles/stage-boundary.d.ts +31 -0
- package/src/workload-roles/stage-boundary.js +208 -0
- package/src/workload-roles/stage-boundary.js.map +1 -0
- package/src/workload-roles/standard-slice.d.ts +54 -0
- package/src/workload-roles/standard-slice.js +252 -0
- package/src/workload-roles/standard-slice.js.map +1 -0
- package/migrations/standard-pre-v1-to-v1.md +0 -11
- package/src/foundation/aurora-logical-database.d.ts +0 -76
- package/src/foundation/aurora-logical-database.js +0 -419
- package/src/foundation/aurora-logical-database.js.map +0 -1
|
@@ -0,0 +1,3308 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.postGenerationInstallFeaturePrerequisite = postGenerationInstallFeaturePrerequisite;
|
|
4
|
+
exports.postGenerationInstallMarkerPath = postGenerationInstallMarkerPath;
|
|
5
|
+
exports.capturePostGenerationInstallState = capturePostGenerationInstallState;
|
|
6
|
+
exports.postGenerationInstallPnpmInvocation = postGenerationInstallPnpmInvocation;
|
|
7
|
+
exports.assertPostGenerationInstallWorkspaceAvailable = assertPostGenerationInstallWorkspaceAvailable;
|
|
8
|
+
exports.resumePendingPostGenerationInstall = resumePendingPostGenerationInstall;
|
|
9
|
+
exports.createPostGenerationInstallCallback = createPostGenerationInstallCallback;
|
|
10
|
+
const canonical_json_1 = require("@empire-builder-kit/runtime/canonical-json");
|
|
11
|
+
const node_child_process_1 = require("node:child_process");
|
|
12
|
+
const node_crypto_1 = require("node:crypto");
|
|
13
|
+
const node_fs_1 = require("node:fs");
|
|
14
|
+
const node_os_1 = require("node:os");
|
|
15
|
+
const node_path_1 = require("node:path");
|
|
16
|
+
const semver_1 = require("semver");
|
|
17
|
+
const yaml_1 = require("yaml");
|
|
18
|
+
const pnpm_toolchain_authority_js_1 = require("../supply-chain/pnpm-toolchain-authority.js");
|
|
19
|
+
const INSTALL_RECORD_KIND = 'ebk-post-generation-install';
|
|
20
|
+
const INSTALL_RECORD_SCHEMA_VERSION = 4;
|
|
21
|
+
const INSTALL_RECORD_DIRECTORY = '.ebk/agent/post-generation-installs';
|
|
22
|
+
const INSTALL_LEASE_PATH = '.ebk/agent/post-generation-install-lease.json';
|
|
23
|
+
const INSTALL_BATCH_CONFLICT_PATH = '.ebk/agent/post-generation-install-batch-conflict.json';
|
|
24
|
+
const INSTALL_COMPLETION_PATH = '.ebk/agent/post-generation-install-completion.json';
|
|
25
|
+
const INSTALL_ACTIVE_PATH = '.ebk/agent/post-generation-install-active.json';
|
|
26
|
+
const POST_GENERATION_STORE_DIRECTORY = '.ebk/cache/pnpm-store';
|
|
27
|
+
const INSTALL_BATCH_CONFLICT_KIND = 'ebk-post-generation-install-batch-conflict';
|
|
28
|
+
const INSTALL_LEASE_KIND = 'ebk-post-generation-install-lease';
|
|
29
|
+
const INSTALL_LEASE_SCHEMA_VERSION = 1;
|
|
30
|
+
const INSTALL_RESERVATION_KIND = 'ebk-post-generation-install-reservation';
|
|
31
|
+
const INSTALL_RESERVATION_SCHEMA_VERSION = 1;
|
|
32
|
+
const INSTALL_ACTIVE_KIND = 'ebk-post-generation-install-active';
|
|
33
|
+
const INSTALL_ACTIVE_SCHEMA_VERSION = 1;
|
|
34
|
+
const LOCKFILE_PATH = 'pnpm-lock.yaml';
|
|
35
|
+
const MAX_NON_FROZEN_ATTEMPTS = 2;
|
|
36
|
+
const MAX_FROZEN_ATTEMPTS = 2;
|
|
37
|
+
const MAX_RECORD_BYTES = 256 * 1024;
|
|
38
|
+
const MAX_LEASE_BYTES = 16 * 1024;
|
|
39
|
+
const INSTALL_TIMEOUT_MS = 10 * 60 * 1000;
|
|
40
|
+
const DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/;
|
|
41
|
+
const GENERATION_PATTERN = /^[0-9a-f]{8}-[0-9a-f-]{27,}$/;
|
|
42
|
+
const ALLOWED_NPM_CREDENTIAL_ENVIRONMENT = new Set([
|
|
43
|
+
'CODEARTIFACT_AUTH_TOKEN',
|
|
44
|
+
'GITHUB_TOKEN',
|
|
45
|
+
'NODE_AUTH_TOKEN',
|
|
46
|
+
'NPM_TOKEN',
|
|
47
|
+
]);
|
|
48
|
+
const PHASE_CONTROLLED_PNPM_SETTINGS = new Set([
|
|
49
|
+
'blockexoticsubdeps',
|
|
50
|
+
'cachedir',
|
|
51
|
+
'configdir',
|
|
52
|
+
'dev',
|
|
53
|
+
'deployallfiles',
|
|
54
|
+
'dir',
|
|
55
|
+
'enablemodulesdir',
|
|
56
|
+
'enableglobalvirtualstore',
|
|
57
|
+
'enableprepostscripts',
|
|
58
|
+
'filter',
|
|
59
|
+
'filterprod',
|
|
60
|
+
'fixlockfile',
|
|
61
|
+
'frozenlockfile',
|
|
62
|
+
'gitbranchlockfile',
|
|
63
|
+
'gitbranchlockfilepattern',
|
|
64
|
+
'global',
|
|
65
|
+
'globaldir',
|
|
66
|
+
'globalvirtualstoredir',
|
|
67
|
+
'include',
|
|
68
|
+
'injectworkspacepackages',
|
|
69
|
+
'ignoredepscripts',
|
|
70
|
+
'ignorepnpmfile',
|
|
71
|
+
'ignorescripts',
|
|
72
|
+
'ignoreworkspace',
|
|
73
|
+
'ignoredbuiltdependencies',
|
|
74
|
+
'ignoredoptionaldependencies',
|
|
75
|
+
'lockfile',
|
|
76
|
+
'lockfiledir',
|
|
77
|
+
'lockfileonly',
|
|
78
|
+
'mergegitbranchlockfiles',
|
|
79
|
+
'modulesdir',
|
|
80
|
+
'neverbuiltdependencies',
|
|
81
|
+
'nodelinker',
|
|
82
|
+
'nodeoptions',
|
|
83
|
+
'nolockfile',
|
|
84
|
+
'omit',
|
|
85
|
+
'only',
|
|
86
|
+
'optimisticrepeatinstall',
|
|
87
|
+
'optional',
|
|
88
|
+
'packagelock',
|
|
89
|
+
'packagelockonly',
|
|
90
|
+
'packageimportmethod',
|
|
91
|
+
'preferfrozenlockfile',
|
|
92
|
+
'production',
|
|
93
|
+
'recursiveinstall',
|
|
94
|
+
'resolutiononly',
|
|
95
|
+
'scriptshell',
|
|
96
|
+
'sharedworkspacelockfile',
|
|
97
|
+
'sideeffectscache',
|
|
98
|
+
'sideeffectscachereadonly',
|
|
99
|
+
'shrinkwrap',
|
|
100
|
+
'shrinkwraponly',
|
|
101
|
+
'symlink',
|
|
102
|
+
'storedir',
|
|
103
|
+
'statedir',
|
|
104
|
+
'usestoreserver',
|
|
105
|
+
'uselockfile',
|
|
106
|
+
'verifystoreintegrity',
|
|
107
|
+
'virtualstoredir',
|
|
108
|
+
'virtualstoredirmaxlength',
|
|
109
|
+
]);
|
|
110
|
+
const UNSUPPORTED_EXECUTABLE_PNPM_SETTINGS = new Set([
|
|
111
|
+
'configdependencies',
|
|
112
|
+
'globalpnpmfile',
|
|
113
|
+
'onlybuiltdependenciesfile',
|
|
114
|
+
'pnpmfile',
|
|
115
|
+
]);
|
|
116
|
+
const ALLOWED_STRUCTURED_PNPM_SETTINGS = new Set([
|
|
117
|
+
'allowbuilds',
|
|
118
|
+
'alloweddeprecatedversions',
|
|
119
|
+
'autoinstallpeers',
|
|
120
|
+
'catalog',
|
|
121
|
+
'catalogmode',
|
|
122
|
+
'catalogs',
|
|
123
|
+
'cleanupunusedcatalogs',
|
|
124
|
+
'dedupedirectdeps',
|
|
125
|
+
'dedupeinjecteddeps',
|
|
126
|
+
'enginestrict',
|
|
127
|
+
'excludelinksfromlockfile',
|
|
128
|
+
'ignoredbuiltdependencies',
|
|
129
|
+
'linkworkspacepackages',
|
|
130
|
+
'minimumreleaseage',
|
|
131
|
+
'minimumreleaseageexclude',
|
|
132
|
+
'neverbuiltdependencies',
|
|
133
|
+
'onlybuiltdependencies',
|
|
134
|
+
'overrides',
|
|
135
|
+
'packageextensions',
|
|
136
|
+
'packages',
|
|
137
|
+
'patcheddependencies',
|
|
138
|
+
'peerdependencyrules',
|
|
139
|
+
'preferworkspacepackages',
|
|
140
|
+
'resolutionmode',
|
|
141
|
+
'resolvepeersfromworkspaceroot',
|
|
142
|
+
'saveworkspaceprotocol',
|
|
143
|
+
'strictdepbuilds',
|
|
144
|
+
'strictpeerdependencies',
|
|
145
|
+
'supportedarchitectures',
|
|
146
|
+
'trustpolicy',
|
|
147
|
+
'trustpolicyexclude',
|
|
148
|
+
'trustpolicyignoreafter',
|
|
149
|
+
]);
|
|
150
|
+
const ALLOWED_NPMRC_RESOLUTION_SETTINGS = new Set([
|
|
151
|
+
'alwaysauth',
|
|
152
|
+
'autoinstallpeers',
|
|
153
|
+
'cafile',
|
|
154
|
+
'certfile',
|
|
155
|
+
'dedupeinjecteddeps',
|
|
156
|
+
'dedupepeerdependents',
|
|
157
|
+
'fetchretries',
|
|
158
|
+
'fetchretryfactor',
|
|
159
|
+
'fetchretrymaxtimeout',
|
|
160
|
+
'fetchretrymintimeout',
|
|
161
|
+
'fetchtimeout',
|
|
162
|
+
'httpsproxy',
|
|
163
|
+
'keyfile',
|
|
164
|
+
'linkworkspacepackages',
|
|
165
|
+
'maxsockets',
|
|
166
|
+
'minimumreleaseage',
|
|
167
|
+
'minimumreleaseageexclude',
|
|
168
|
+
'networkconcurrency',
|
|
169
|
+
'noproxy',
|
|
170
|
+
'preferworkspacepackages',
|
|
171
|
+
'proxy',
|
|
172
|
+
'registry',
|
|
173
|
+
'resolutionmode',
|
|
174
|
+
'resolvepeersfromworkspaceroot',
|
|
175
|
+
'saveworkspaceprotocol',
|
|
176
|
+
'strictpeerdependencies',
|
|
177
|
+
'strictssl',
|
|
178
|
+
'trustpolicy',
|
|
179
|
+
'trustpolicyexclude',
|
|
180
|
+
'trustpolicyignoreafter',
|
|
181
|
+
]);
|
|
182
|
+
class SimulatedProtocolCrash extends Error {
|
|
183
|
+
constructor(checkpoint) {
|
|
184
|
+
super(`Simulated post-generation install crash ${checkpoint}.`);
|
|
185
|
+
this.name = 'SimulatedProtocolCrash';
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function sha256(contents) {
|
|
189
|
+
return `sha256:${(0, node_crypto_1.createHash)('sha256').update(contents).digest('hex')}`;
|
|
190
|
+
}
|
|
191
|
+
function literalCompare(left, right) {
|
|
192
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
193
|
+
}
|
|
194
|
+
function normalizedWorkspacePath(path, label) {
|
|
195
|
+
if (path.length === 0 ||
|
|
196
|
+
path.startsWith('/') ||
|
|
197
|
+
path.includes('\\') ||
|
|
198
|
+
path.includes('\0') ||
|
|
199
|
+
path.split('/').some((part) => part === '' || part === '.' || part === '..')) {
|
|
200
|
+
throw new Error(`${label} must be a canonical workspace-relative path.`);
|
|
201
|
+
}
|
|
202
|
+
return path;
|
|
203
|
+
}
|
|
204
|
+
function postGenerationInstallFeaturePrerequisite(path, selectedFeature) {
|
|
205
|
+
normalizedWorkspacePath(path, 'Feature-registry prerequisite path');
|
|
206
|
+
if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(selectedFeature)) {
|
|
207
|
+
throw new Error('Post-generation install prerequisite feature must be lowercase kebab-case.');
|
|
208
|
+
}
|
|
209
|
+
return { kind: 'registered-slice-feature', path, selectedFeature };
|
|
210
|
+
}
|
|
211
|
+
function semanticPrerequisiteKey(prerequisite) {
|
|
212
|
+
return `${prerequisite.kind}:${prerequisite.path}:${prerequisite.selectedFeature}`;
|
|
213
|
+
}
|
|
214
|
+
function assertOperation(operation) {
|
|
215
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(operation.key)) {
|
|
216
|
+
throw new Error('Post-generation install key must be lowercase kebab-case.');
|
|
217
|
+
}
|
|
218
|
+
if (!/^[a-z0-9]+(?::[a-z0-9-]+)+$/.test(operation.id)) {
|
|
219
|
+
throw new Error('Post-generation install operation id is invalid.');
|
|
220
|
+
}
|
|
221
|
+
normalizedWorkspacePath(operation.sourceRoot, 'Install source root');
|
|
222
|
+
if (operation.resumeArguments.length === 0 ||
|
|
223
|
+
operation.resumeArguments.length > 32 ||
|
|
224
|
+
operation.resumeArguments.some((argument) => typeof argument !== 'string' ||
|
|
225
|
+
argument.length === 0 ||
|
|
226
|
+
argument.length > 16 * 1024)) {
|
|
227
|
+
throw new Error('Post-generation resume arguments are invalid or unbounded.');
|
|
228
|
+
}
|
|
229
|
+
if (Buffer.byteLength((0, canonical_json_1.canonicalJson)(operation.request)) > 48 * 1024) {
|
|
230
|
+
throw new Error('Post-generation install request exceeds 48 KiB.');
|
|
231
|
+
}
|
|
232
|
+
const prerequisites = operation.semanticPrerequisites ?? [];
|
|
233
|
+
if (prerequisites.length > 16) {
|
|
234
|
+
throw new Error('Post-generation install has too many semantic prerequisites.');
|
|
235
|
+
}
|
|
236
|
+
let previous = '';
|
|
237
|
+
for (const prerequisite of prerequisites) {
|
|
238
|
+
if (!isRecord(prerequisite) ||
|
|
239
|
+
prerequisite.kind !== 'registered-slice-feature' ||
|
|
240
|
+
Object.keys(prerequisite).sort().join(',') !== 'kind,path,selectedFeature') {
|
|
241
|
+
throw new Error('Post-generation install semantic prerequisite is invalid.');
|
|
242
|
+
}
|
|
243
|
+
postGenerationInstallFeaturePrerequisite(prerequisite.path, prerequisite.selectedFeature);
|
|
244
|
+
const key = semanticPrerequisiteKey(prerequisite);
|
|
245
|
+
if (key <= previous) {
|
|
246
|
+
throw new Error('Post-generation install semantic prerequisites must be unique and sorted.');
|
|
247
|
+
}
|
|
248
|
+
previous = key;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function operationFingerprint(operation) {
|
|
252
|
+
return sha256((0, canonical_json_1.canonicalJson)(operation));
|
|
253
|
+
}
|
|
254
|
+
function expectedFingerprint(operation) {
|
|
255
|
+
return operationFingerprint({
|
|
256
|
+
generator: operation.generator,
|
|
257
|
+
id: operation.id,
|
|
258
|
+
request: operation.request,
|
|
259
|
+
resumeArguments: operation.resumeArguments,
|
|
260
|
+
semanticPrerequisites: operation.semanticPrerequisites ?? [],
|
|
261
|
+
sourceRoot: operation.sourceRoot,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
function postGenerationInstallMarkerPath(key) {
|
|
265
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(key)) {
|
|
266
|
+
throw new Error('Post-generation install key must be lowercase kebab-case.');
|
|
267
|
+
}
|
|
268
|
+
return `${INSTALL_RECORD_DIRECTORY}/${key}.json`;
|
|
269
|
+
}
|
|
270
|
+
function workspaceRealRoot(workspaceRoot) {
|
|
271
|
+
const absolute = (0, node_path_1.resolve)(workspaceRoot);
|
|
272
|
+
const metadata = (0, node_fs_1.lstatSync)(absolute);
|
|
273
|
+
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
274
|
+
throw new Error('Post-generation install workspace must be a real directory.');
|
|
275
|
+
}
|
|
276
|
+
return (0, node_fs_1.realpathSync)(absolute);
|
|
277
|
+
}
|
|
278
|
+
function assertSafeExistingDirectoryChain(workspaceRoot, relativeDirectory) {
|
|
279
|
+
// Threat boundary: this rejects aliases that exist when the operation is
|
|
280
|
+
// planned. It does not claim kernel-level swap resistance against a
|
|
281
|
+
// same-user process that can replace path components afterward.
|
|
282
|
+
const root = workspaceRealRoot(workspaceRoot);
|
|
283
|
+
let current = root;
|
|
284
|
+
for (const part of normalizedWorkspacePath(relativeDirectory, 'Post-generation install directory').split('/')) {
|
|
285
|
+
current = (0, node_path_1.join)(current, part);
|
|
286
|
+
let metadata;
|
|
287
|
+
try {
|
|
288
|
+
metadata = (0, node_fs_1.lstatSync)(current);
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
if (error.code === 'ENOENT')
|
|
292
|
+
return;
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
295
|
+
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
296
|
+
throw new Error(`Post-generation install directory is not a real directory at ${relativeDirectory}.`);
|
|
297
|
+
}
|
|
298
|
+
const target = (0, node_fs_1.realpathSync)(current);
|
|
299
|
+
const fromRoot = (0, node_path_1.relative)(root, target);
|
|
300
|
+
if (target !== current ||
|
|
301
|
+
fromRoot === '..' ||
|
|
302
|
+
fromRoot.startsWith(`..${node_path_1.sep}`)) {
|
|
303
|
+
throw new Error(`Post-generation install directory escapes the workspace at ${relativeDirectory}.`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function ensureSafeDirectory(workspaceRoot, relativeDirectory) {
|
|
308
|
+
// Recovery repeats the pre-existing-alias checks while materializing the
|
|
309
|
+
// private state path; the same-user post-check swap limitation still applies.
|
|
310
|
+
const root = workspaceRealRoot(workspaceRoot);
|
|
311
|
+
let current = root;
|
|
312
|
+
for (const part of normalizedWorkspacePath(relativeDirectory, 'Post-generation install directory').split('/')) {
|
|
313
|
+
current = (0, node_path_1.join)(current, part);
|
|
314
|
+
try {
|
|
315
|
+
(0, node_fs_1.mkdirSync)(current, { mode: 0o700 });
|
|
316
|
+
(0, node_fs_1.chmodSync)(current, 0o700);
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
if (error.code !== 'EEXIST')
|
|
320
|
+
throw error;
|
|
321
|
+
}
|
|
322
|
+
const metadata = (0, node_fs_1.lstatSync)(current);
|
|
323
|
+
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
324
|
+
throw new Error(`Post-generation install directory is not a real directory at ${relativeDirectory}.`);
|
|
325
|
+
}
|
|
326
|
+
const target = (0, node_fs_1.realpathSync)(current);
|
|
327
|
+
const fromRoot = (0, node_path_1.relative)(root, target);
|
|
328
|
+
if (target !== current ||
|
|
329
|
+
fromRoot === '..' ||
|
|
330
|
+
fromRoot.startsWith(`..${node_path_1.sep}`)) {
|
|
331
|
+
throw new Error(`Post-generation install directory escapes the workspace at ${relativeDirectory}.`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return current;
|
|
335
|
+
}
|
|
336
|
+
function confinedAbsolutePath(workspaceRoot, relativePath) {
|
|
337
|
+
const root = workspaceRealRoot(workspaceRoot);
|
|
338
|
+
const absolute = (0, node_path_1.resolve)(root, normalizedWorkspacePath(relativePath, 'Post-generation install path'));
|
|
339
|
+
const fromRoot = (0, node_path_1.relative)(root, absolute);
|
|
340
|
+
if (fromRoot === '' || fromRoot === '..' || fromRoot.startsWith(`..${node_path_1.sep}`)) {
|
|
341
|
+
throw new Error('Post-generation install path escapes the workspace.');
|
|
342
|
+
}
|
|
343
|
+
return absolute;
|
|
344
|
+
}
|
|
345
|
+
function assertSafeRegularFile(absolute, label) {
|
|
346
|
+
const metadata = (0, node_fs_1.lstatSync)(absolute);
|
|
347
|
+
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.nlink !== 1) {
|
|
348
|
+
throw new Error(`${label} must be one regular, non-linked file.`);
|
|
349
|
+
}
|
|
350
|
+
if ((0, node_fs_1.realpathSync)(absolute) !== absolute) {
|
|
351
|
+
throw new Error(`${label} must not traverse an aliased path.`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
function writeExclusiveFile(absolute, contents) {
|
|
355
|
+
const noFollow = typeof node_fs_1.constants.O_NOFOLLOW === 'number' ? node_fs_1.constants.O_NOFOLLOW : 0;
|
|
356
|
+
const descriptor = (0, node_fs_1.openSync)(absolute, node_fs_1.constants.O_CREAT | node_fs_1.constants.O_EXCL | node_fs_1.constants.O_WRONLY | noFollow, 0o600);
|
|
357
|
+
try {
|
|
358
|
+
(0, node_fs_1.writeFileSync)(descriptor, contents, 'utf8');
|
|
359
|
+
}
|
|
360
|
+
finally {
|
|
361
|
+
(0, node_fs_1.closeSync)(descriptor);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function treeInstallAuthorityReader(tree) {
|
|
365
|
+
const mode = (path) => {
|
|
366
|
+
const absolute = (0, node_path_1.resolve)(tree.root, path);
|
|
367
|
+
try {
|
|
368
|
+
const observed = (0, node_fs_1.lstatSync)(absolute);
|
|
369
|
+
if (observed.isFile() && !observed.isSymbolicLink()) {
|
|
370
|
+
return observed.mode & 0o777;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
if (error.code !== 'ENOENT')
|
|
375
|
+
throw error;
|
|
376
|
+
}
|
|
377
|
+
return tree.read(path) === null ? null : 0o666 & ~process.umask();
|
|
378
|
+
};
|
|
379
|
+
return {
|
|
380
|
+
children: (path) => tree.children(path).sort(literalCompare),
|
|
381
|
+
kind(path) {
|
|
382
|
+
if (tree.read(path) !== null)
|
|
383
|
+
return 'file';
|
|
384
|
+
return tree.children(path).length > 0 ? 'directory' : 'missing';
|
|
385
|
+
},
|
|
386
|
+
mode,
|
|
387
|
+
read: (path) => tree.read(path),
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function diskInstallAuthorityReader(workspaceRoot) {
|
|
391
|
+
const root = workspaceRealRoot(workspaceRoot);
|
|
392
|
+
const absolute = (path) => {
|
|
393
|
+
if (path === '')
|
|
394
|
+
return root;
|
|
395
|
+
const candidate = (0, node_path_1.resolve)(root, normalizedWorkspacePath(path, 'Install-authority path'));
|
|
396
|
+
const fromRoot = (0, node_path_1.relative)(root, candidate);
|
|
397
|
+
if (fromRoot === '..' || fromRoot.startsWith(`..${node_path_1.sep}`)) {
|
|
398
|
+
throw new Error('Install-authority path escapes the workspace.');
|
|
399
|
+
}
|
|
400
|
+
return candidate;
|
|
401
|
+
};
|
|
402
|
+
const metadata = (path) => {
|
|
403
|
+
const candidate = absolute(path);
|
|
404
|
+
try {
|
|
405
|
+
const observed = (0, node_fs_1.lstatSync)(candidate);
|
|
406
|
+
if (observed.isSymbolicLink()) {
|
|
407
|
+
throw new Error(`Install-authority path is a symlink at ${path}.`);
|
|
408
|
+
}
|
|
409
|
+
if ((0, node_fs_1.realpathSync)(candidate) !== candidate) {
|
|
410
|
+
throw new Error(`Install-authority path is aliased at ${path}.`);
|
|
411
|
+
}
|
|
412
|
+
return observed;
|
|
413
|
+
}
|
|
414
|
+
catch (error) {
|
|
415
|
+
if (error.code === 'ENOENT')
|
|
416
|
+
return undefined;
|
|
417
|
+
throw error;
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
return {
|
|
421
|
+
children(path) {
|
|
422
|
+
const observed = metadata(path);
|
|
423
|
+
if (!observed)
|
|
424
|
+
return [];
|
|
425
|
+
if (!observed.isDirectory())
|
|
426
|
+
return [];
|
|
427
|
+
return (0, node_fs_1.readdirSync)(absolute(path), { withFileTypes: true })
|
|
428
|
+
.map((entry) => {
|
|
429
|
+
if (entry.isSymbolicLink()) {
|
|
430
|
+
throw new Error(`Install-authority directory contains a symlink at ${node_path_1.posix.join(path, entry.name)}.`);
|
|
431
|
+
}
|
|
432
|
+
return entry.name;
|
|
433
|
+
})
|
|
434
|
+
.sort(literalCompare);
|
|
435
|
+
},
|
|
436
|
+
kind(path) {
|
|
437
|
+
const observed = metadata(path);
|
|
438
|
+
if (!observed)
|
|
439
|
+
return 'missing';
|
|
440
|
+
if (observed.isDirectory())
|
|
441
|
+
return 'directory';
|
|
442
|
+
if (observed.isFile() && observed.nlink === 1)
|
|
443
|
+
return 'file';
|
|
444
|
+
throw new Error(`Install-authority path must be one regular file or real directory at ${path}.`);
|
|
445
|
+
},
|
|
446
|
+
mode(path) {
|
|
447
|
+
const observed = metadata(path);
|
|
448
|
+
if (!observed || !observed.isFile() || observed.nlink !== 1)
|
|
449
|
+
return null;
|
|
450
|
+
return observed.mode & 0o777;
|
|
451
|
+
},
|
|
452
|
+
read(path) {
|
|
453
|
+
const observed = metadata(path);
|
|
454
|
+
if (!observed)
|
|
455
|
+
return null;
|
|
456
|
+
if (observed.isDirectory())
|
|
457
|
+
return null;
|
|
458
|
+
if (!observed.isFile() || observed.nlink !== 1) {
|
|
459
|
+
throw new Error(`Install-authority path must be one regular file at ${path}.`);
|
|
460
|
+
}
|
|
461
|
+
return (0, node_fs_1.readFileSync)(absolute(path));
|
|
462
|
+
},
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
function parseBoundedJson(source, label) {
|
|
466
|
+
try {
|
|
467
|
+
return JSON.parse(source.toString('utf8'));
|
|
468
|
+
}
|
|
469
|
+
catch {
|
|
470
|
+
throw new Error(`${label} must be valid JSON.`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
function parseBoundedYaml(source, label) {
|
|
474
|
+
try {
|
|
475
|
+
const document = (0, yaml_1.parseDocument)(source.toString('utf8'), {
|
|
476
|
+
prettyErrors: false,
|
|
477
|
+
uniqueKeys: true,
|
|
478
|
+
});
|
|
479
|
+
if (document.errors.length > 0)
|
|
480
|
+
throw document.errors[0];
|
|
481
|
+
const pending = [document.contents];
|
|
482
|
+
const visited = new Set();
|
|
483
|
+
while (pending.length > 0) {
|
|
484
|
+
const node = pending.pop();
|
|
485
|
+
if (!node || typeof node !== 'object' || visited.has(node))
|
|
486
|
+
continue;
|
|
487
|
+
visited.add(node);
|
|
488
|
+
if ((0, yaml_1.isAlias)(node) ||
|
|
489
|
+
('anchor' in node &&
|
|
490
|
+
typeof node.anchor === 'string')) {
|
|
491
|
+
throw new Error(`${label} cannot contain YAML aliases or anchors.`);
|
|
492
|
+
}
|
|
493
|
+
if ((0, yaml_1.isPair)(node)) {
|
|
494
|
+
if ((0, yaml_1.isScalar)(node.key) && node.key.value === '<<') {
|
|
495
|
+
throw new Error(`${label} cannot contain YAML merge keys.`);
|
|
496
|
+
}
|
|
497
|
+
pending.push(node.key, node.value);
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
if ('items' in node && Array.isArray(node.items)) {
|
|
501
|
+
pending.push(...node.items);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return document.toJS({ maxAliasCount: 0 });
|
|
505
|
+
}
|
|
506
|
+
catch {
|
|
507
|
+
throw new Error(`${label} must be valid, explicit YAML without aliases, anchors, or merge keys.`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
function workspacePatternSegments(pattern) {
|
|
511
|
+
let normalized = pattern.trim();
|
|
512
|
+
if (normalized.startsWith('./'))
|
|
513
|
+
normalized = normalized.slice(2);
|
|
514
|
+
while (normalized.endsWith('/'))
|
|
515
|
+
normalized = normalized.slice(0, -1);
|
|
516
|
+
if (normalized.length === 0 ||
|
|
517
|
+
normalized.startsWith('/') ||
|
|
518
|
+
normalized.includes('\\') ||
|
|
519
|
+
['[', ']', '{', '}', '(', ')'].some((character) => normalized.includes(character)) ||
|
|
520
|
+
normalized.split('/').some((part) => part === '' || part === '..')) {
|
|
521
|
+
throw new Error(`Post-generation install does not support unsafe pnpm workspace pattern ${pattern}.`);
|
|
522
|
+
}
|
|
523
|
+
return normalized.split('/');
|
|
524
|
+
}
|
|
525
|
+
function workspaceSegmentMatches(pattern, candidate) {
|
|
526
|
+
if (pattern === '**')
|
|
527
|
+
return true;
|
|
528
|
+
const expression = pattern
|
|
529
|
+
.split('')
|
|
530
|
+
.map((character) => {
|
|
531
|
+
if (character === '*')
|
|
532
|
+
return '[^/]*';
|
|
533
|
+
if (character === '?')
|
|
534
|
+
return '[^/]';
|
|
535
|
+
return character.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
536
|
+
})
|
|
537
|
+
.join('');
|
|
538
|
+
return new RegExp(`^${expression}$`, 'u').test(candidate);
|
|
539
|
+
}
|
|
540
|
+
function workspaceDirectoriesForPattern(reader, pattern) {
|
|
541
|
+
const segments = workspacePatternSegments(pattern);
|
|
542
|
+
const results = new Set();
|
|
543
|
+
const visited = new Set();
|
|
544
|
+
const walk = (directory, index) => {
|
|
545
|
+
const visitKey = `${index}:${directory}`;
|
|
546
|
+
if (visited.has(visitKey))
|
|
547
|
+
return;
|
|
548
|
+
visited.add(visitKey);
|
|
549
|
+
if (visited.size > pnpm_toolchain_authority_js_1.PNPM_TOOLCHAIN_AUTHORITY_MAX_ENTRIES * 8) {
|
|
550
|
+
throw new Error('Post-generation install workspace-pattern traversal is unbounded.');
|
|
551
|
+
}
|
|
552
|
+
if (index === segments.length) {
|
|
553
|
+
results.add(directory);
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const segment = segments[index];
|
|
557
|
+
if (segment === '**') {
|
|
558
|
+
walk(directory, index + 1);
|
|
559
|
+
for (const child of reader.children(directory)) {
|
|
560
|
+
if (['.ebk', '.git', 'node_modules'].includes(child))
|
|
561
|
+
continue;
|
|
562
|
+
const path = directory ? `${directory}/${child}` : child;
|
|
563
|
+
if (reader.kind(path) === 'directory')
|
|
564
|
+
walk(path, index);
|
|
565
|
+
}
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
for (const child of reader.children(directory)) {
|
|
569
|
+
if (['.ebk', '.git', 'node_modules'].includes(child) ||
|
|
570
|
+
!workspaceSegmentMatches(segment, child)) {
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
const path = directory ? `${directory}/${child}` : child;
|
|
574
|
+
if (reader.kind(path) === 'directory')
|
|
575
|
+
walk(path, index + 1);
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
walk('', 0);
|
|
579
|
+
return results;
|
|
580
|
+
}
|
|
581
|
+
function exactWorkspaceManifestPaths(reader, workspace) {
|
|
582
|
+
const patterns = isRecord(workspace) ? workspace.packages : undefined;
|
|
583
|
+
if (!Array.isArray(patterns) ||
|
|
584
|
+
patterns.length > 256 ||
|
|
585
|
+
patterns.some((pattern) => typeof pattern !== 'string')) {
|
|
586
|
+
throw new Error('Post-generation install requires bounded pnpm workspace package patterns.');
|
|
587
|
+
}
|
|
588
|
+
const included = new Set();
|
|
589
|
+
const excluded = new Set();
|
|
590
|
+
for (const pattern of patterns) {
|
|
591
|
+
const negative = pattern.startsWith('!');
|
|
592
|
+
const directories = workspaceDirectoriesForPattern(reader, negative ? pattern.slice(1) : pattern);
|
|
593
|
+
for (const directory of directories) {
|
|
594
|
+
if (!directory)
|
|
595
|
+
continue;
|
|
596
|
+
if (reader.kind(`${directory}/.npmrc`) !== 'missing') {
|
|
597
|
+
throw new Error(`Post-generation install does not accept package-local .npmrc at ${directory}/.npmrc.`);
|
|
598
|
+
}
|
|
599
|
+
for (const alternate of ['package.json5', 'package.yaml']) {
|
|
600
|
+
const alternatePath = `${directory}/${alternate}`;
|
|
601
|
+
if (reader.kind(alternatePath) !== 'missing') {
|
|
602
|
+
throw new Error(`Post-generation install accepts only package.json workspace manifests; found ${alternatePath}.`);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
const manifest = `${directory}/package.json`;
|
|
606
|
+
if (reader.kind(manifest) !== 'file')
|
|
607
|
+
continue;
|
|
608
|
+
(negative ? excluded : included).add(manifest);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return [...included]
|
|
612
|
+
.filter((path) => !excluded.has(path))
|
|
613
|
+
.sort(literalCompare);
|
|
614
|
+
}
|
|
615
|
+
function normalizedLiteralReferencedPath(baseDirectory, reference, label) {
|
|
616
|
+
if (reference.includes('%') ||
|
|
617
|
+
reference.startsWith('~') ||
|
|
618
|
+
/^[A-Za-z]:/u.test(reference)) {
|
|
619
|
+
throw new Error(`${label} contains an ambiguous encoded or home-relative path.`);
|
|
620
|
+
}
|
|
621
|
+
const normalized = node_path_1.posix.normalize(node_path_1.posix.join(baseDirectory, reference));
|
|
622
|
+
if (node_path_1.posix.isAbsolute(reference) ||
|
|
623
|
+
reference.includes('\\') ||
|
|
624
|
+
normalized === '.' ||
|
|
625
|
+
normalized === '.ebk' ||
|
|
626
|
+
normalized.startsWith('.ebk/') ||
|
|
627
|
+
normalized === '..' ||
|
|
628
|
+
normalized.startsWith('../') ||
|
|
629
|
+
normalized.includes('/../')) {
|
|
630
|
+
throw new Error(`${label} must remain inside the workspace.`);
|
|
631
|
+
}
|
|
632
|
+
return normalizedWorkspacePath(normalized, label);
|
|
633
|
+
}
|
|
634
|
+
function dependencyReferences(manifest, baseDirectory) {
|
|
635
|
+
if (!isRecord(manifest))
|
|
636
|
+
return [];
|
|
637
|
+
const references = new Set();
|
|
638
|
+
for (const key of [
|
|
639
|
+
'dependencies',
|
|
640
|
+
'devDependencies',
|
|
641
|
+
'optionalDependencies',
|
|
642
|
+
'peerDependencies',
|
|
643
|
+
]) {
|
|
644
|
+
const dependencies = manifest[key];
|
|
645
|
+
if (!isRecord(dependencies))
|
|
646
|
+
continue;
|
|
647
|
+
for (const specification of Object.values(dependencies)) {
|
|
648
|
+
if (typeof specification !== 'string')
|
|
649
|
+
continue;
|
|
650
|
+
assertSupportedDependencySpecification(specification, `Dependency specification in ${key}`);
|
|
651
|
+
const prefix = specification.startsWith('file:')
|
|
652
|
+
? 'file:'
|
|
653
|
+
: specification.startsWith('link:')
|
|
654
|
+
? 'link:'
|
|
655
|
+
: undefined;
|
|
656
|
+
if (!prefix)
|
|
657
|
+
continue;
|
|
658
|
+
references.add(normalizedLiteralReferencedPath(baseDirectory, specification.slice(prefix.length), 'Local dependency input'));
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return [...references].sort(literalCompare);
|
|
662
|
+
}
|
|
663
|
+
function collectNestedLocalReferences(value, baseDirectory, references) {
|
|
664
|
+
if (typeof value === 'string') {
|
|
665
|
+
assertSupportedDependencySpecification(value, 'Nested pnpm resolution specification');
|
|
666
|
+
const prefix = value.startsWith('file:')
|
|
667
|
+
? 'file:'
|
|
668
|
+
: value.startsWith('link:')
|
|
669
|
+
? 'link:'
|
|
670
|
+
: undefined;
|
|
671
|
+
if (prefix) {
|
|
672
|
+
references.add(normalizedLiteralReferencedPath(baseDirectory, value.slice(prefix.length), 'Local pnpm configuration input'));
|
|
673
|
+
}
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (Array.isArray(value)) {
|
|
677
|
+
for (const nested of value) {
|
|
678
|
+
collectNestedLocalReferences(nested, baseDirectory, references);
|
|
679
|
+
}
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
if (isRecord(value)) {
|
|
683
|
+
for (const nested of Object.values(value)) {
|
|
684
|
+
collectNestedLocalReferences(nested, baseDirectory, references);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
function assertNoExternalDependencyAuthority(specification, label) {
|
|
689
|
+
if (/^(?:bitbucket:|git(?:\+|:|@)|github:|gitlab:|https?:|ssh:)/iu.test(specification) ||
|
|
690
|
+
/\.git(?:#|$)/iu.test(specification)) {
|
|
691
|
+
throw new Error(`${label} cannot use VCS, URL, or other external executable resolution authority.`);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
function assertSupportedDependencySpecification(specification, label) {
|
|
695
|
+
assertNoExternalDependencyAuthority(specification, label);
|
|
696
|
+
if (/^(?:file:|link:)/u.test(specification)) {
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
if (specification.startsWith('workspace:')) {
|
|
700
|
+
const selector = specification.slice('workspace:'.length);
|
|
701
|
+
if (!['*', '^', '~'].includes(selector) &&
|
|
702
|
+
(0, semver_1.validRange)(selector, { loose: false }) === null) {
|
|
703
|
+
throw new Error(`${label} cannot use path or alias forms of the workspace protocol.`);
|
|
704
|
+
}
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
if (/^catalog:[a-z0-9._-]*$/iu.test(specification))
|
|
708
|
+
return;
|
|
709
|
+
if (specification.startsWith('npm:')) {
|
|
710
|
+
const alias = specification.slice('npm:'.length);
|
|
711
|
+
if (!/^(?:@[^/\s]+\/[^@\s]+|[^@/\s]+)@[^\s/]+$/u.test(alias)) {
|
|
712
|
+
throw new Error(`${label} contains an unsupported npm alias.`);
|
|
713
|
+
}
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
if (/^(?:\.{1,2}[\\/]|~[\\/]|[\\/]|[A-Za-z]:[\\/])/u.test(specification) ||
|
|
717
|
+
/^[A-Za-z]:/u.test(specification) ||
|
|
718
|
+
specification.includes('\\') ||
|
|
719
|
+
/\.(?:tar|tar\.gz|tgz)(?:#.*)?$/iu.test(specification) ||
|
|
720
|
+
specification.includes('/')) {
|
|
721
|
+
throw new Error(`${label} must use a registry, workspace, catalog, or explicitly bound file/link specification.`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
function localReferencesInLockValue(value, baseDirectory, label) {
|
|
725
|
+
const references = new Set();
|
|
726
|
+
for (const match of value.matchAll(/(?:^|[@(])(?:file|link):([^),\s'"}]+)/gu)) {
|
|
727
|
+
references.add(normalizedLiteralReferencedPath(baseDirectory, match[1] ?? '', `${label} local target`));
|
|
728
|
+
}
|
|
729
|
+
return [...references].sort(literalCompare);
|
|
730
|
+
}
|
|
731
|
+
function importerBaseDirectory(importer) {
|
|
732
|
+
if (importer === '.')
|
|
733
|
+
return '';
|
|
734
|
+
return normalizedLiteralReferencedPath('', importer, 'Lockfile importer path');
|
|
735
|
+
}
|
|
736
|
+
function manifestDependencySpecifications(manifest, label) {
|
|
737
|
+
const specifications = new Map();
|
|
738
|
+
if (!isRecord(manifest))
|
|
739
|
+
return specifications;
|
|
740
|
+
for (const section of [
|
|
741
|
+
'dependencies',
|
|
742
|
+
'devDependencies',
|
|
743
|
+
'optionalDependencies',
|
|
744
|
+
]) {
|
|
745
|
+
const dependencies = manifest[section];
|
|
746
|
+
if (!isRecord(dependencies))
|
|
747
|
+
continue;
|
|
748
|
+
for (const [name, specification] of Object.entries(dependencies)) {
|
|
749
|
+
if (typeof specification !== 'string') {
|
|
750
|
+
throw new Error(`Post-generation install ${label} dependency ${name} must use one string specification.`);
|
|
751
|
+
}
|
|
752
|
+
assertSupportedDependencySpecification(specification, `${label} dependency ${name}`);
|
|
753
|
+
const previous = specifications.get(name);
|
|
754
|
+
if (previous !== undefined && previous !== specification) {
|
|
755
|
+
throw new Error(`Post-generation install ${label} dependency ${name} has ambiguous specifications.`);
|
|
756
|
+
}
|
|
757
|
+
specifications.set(name, specification);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return specifications;
|
|
761
|
+
}
|
|
762
|
+
function assertWorkspaceDependencyTargets(reader, manifestPaths) {
|
|
763
|
+
const workspaceTargets = new Map();
|
|
764
|
+
const manifests = new Map();
|
|
765
|
+
for (const manifestPath of manifestPaths) {
|
|
766
|
+
const source = reader.read(manifestPath);
|
|
767
|
+
if (source === null) {
|
|
768
|
+
throw new Error(`Install-authority manifest vanished at ${manifestPath}.`);
|
|
769
|
+
}
|
|
770
|
+
const manifest = parseBoundedJson(source, `Install-authority manifest ${manifestPath}`);
|
|
771
|
+
manifests.set(manifestPath, manifest);
|
|
772
|
+
if (manifestPath === 'package.json' || !isRecord(manifest))
|
|
773
|
+
continue;
|
|
774
|
+
if (typeof manifest.name !== 'string' || manifest.name.length === 0) {
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
const targets = workspaceTargets.get(manifest.name) ?? [];
|
|
778
|
+
targets.push(manifestPath);
|
|
779
|
+
workspaceTargets.set(manifest.name, targets);
|
|
780
|
+
}
|
|
781
|
+
for (const [manifestPath, manifest] of manifests) {
|
|
782
|
+
for (const [dependency, specification] of manifestDependencySpecifications(manifest, manifestPath)) {
|
|
783
|
+
if (!specification.startsWith('workspace:'))
|
|
784
|
+
continue;
|
|
785
|
+
const targets = workspaceTargets.get(dependency) ?? [];
|
|
786
|
+
if (targets.length !== 1) {
|
|
787
|
+
throw new Error(`Post-generation install workspace dependency ${dependency} in ${manifestPath} must resolve to exactly one declared workspace package.`);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
function assertLockfileImporterSemantics(lockfile, reader, workspaceManifestPaths, references, workspaceLockEntries, workspaceResolutionEntries) {
|
|
793
|
+
const importers = lockfile.importers;
|
|
794
|
+
if (importers === undefined)
|
|
795
|
+
return;
|
|
796
|
+
if (!isRecord(importers)) {
|
|
797
|
+
throw new Error('Post-generation install lockfile importers are invalid.');
|
|
798
|
+
}
|
|
799
|
+
for (const [importer, importerValue] of Object.entries(importers)) {
|
|
800
|
+
const baseDirectory = importerBaseDirectory(importer);
|
|
801
|
+
const manifestPath = baseDirectory
|
|
802
|
+
? `${baseDirectory}/package.json`
|
|
803
|
+
: 'package.json';
|
|
804
|
+
const manifestSource = reader.read(manifestPath);
|
|
805
|
+
if (manifestSource === null || reader.kind(manifestPath) !== 'file') {
|
|
806
|
+
throw new Error(`Post-generation install lockfile importer ${importer} has no bound package.json.`);
|
|
807
|
+
}
|
|
808
|
+
const manifest = parseBoundedJson(manifestSource, `Post-generation install lockfile importer ${manifestPath}`);
|
|
809
|
+
const manifestSpecifications = manifestDependencySpecifications(manifest, manifestPath);
|
|
810
|
+
if (!isRecord(importerValue)) {
|
|
811
|
+
throw new Error(`Post-generation install lockfile importer ${importer} is invalid.`);
|
|
812
|
+
}
|
|
813
|
+
for (const section of [
|
|
814
|
+
'dependencies',
|
|
815
|
+
'devDependencies',
|
|
816
|
+
'optionalDependencies',
|
|
817
|
+
]) {
|
|
818
|
+
const dependencies = importerValue[section];
|
|
819
|
+
if (dependencies === undefined)
|
|
820
|
+
continue;
|
|
821
|
+
if (!isRecord(dependencies)) {
|
|
822
|
+
throw new Error(`Post-generation install lockfile importer ${importer} ${section} is invalid.`);
|
|
823
|
+
}
|
|
824
|
+
for (const [name, lockEntry] of Object.entries(dependencies)) {
|
|
825
|
+
if (!isRecord(lockEntry) || typeof lockEntry.specifier !== 'string') {
|
|
826
|
+
throw new Error(`Post-generation install lockfile importer ${importer} dependency ${name} is invalid.`);
|
|
827
|
+
}
|
|
828
|
+
const manifestSpecification = manifestSpecifications.get(name);
|
|
829
|
+
if (manifestSpecification === undefined ||
|
|
830
|
+
lockEntry.specifier !== manifestSpecification) {
|
|
831
|
+
throw new Error(`Post-generation install lockfile importer ${importer} dependency ${name} does not match its manifest authority.`);
|
|
832
|
+
}
|
|
833
|
+
const resolvedReferences = new Set();
|
|
834
|
+
if (typeof lockEntry.version === 'string') {
|
|
835
|
+
for (const reference of localReferencesInLockValue(lockEntry.version, baseDirectory, `Lockfile importer ${importer} dependency ${name} version`)) {
|
|
836
|
+
resolvedReferences.add(reference);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
if (isRecord(lockEntry.resolution)) {
|
|
840
|
+
if (typeof lockEntry.resolution.directory === 'string') {
|
|
841
|
+
resolvedReferences.add(normalizedLiteralReferencedPath('', lockEntry.resolution.directory, `Lockfile importer ${importer} dependency ${name} directory`));
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
if (/^(?:file:|link:)/u.test(manifestSpecification)) {
|
|
845
|
+
const expected = normalizedLiteralReferencedPath(baseDirectory, manifestSpecification.slice(manifestSpecification.indexOf(':') + 1), `Manifest dependency ${name}`);
|
|
846
|
+
if (resolvedReferences.size === 0 ||
|
|
847
|
+
[...resolvedReferences].some((reference) => reference !== expected)) {
|
|
848
|
+
throw new Error(`Post-generation install lockfile redirects local dependency ${name} away from its manifest target.`);
|
|
849
|
+
}
|
|
850
|
+
for (const reference of resolvedReferences) {
|
|
851
|
+
references.add(reference);
|
|
852
|
+
}
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
855
|
+
if (manifestSpecification.startsWith('workspace:')) {
|
|
856
|
+
if (Object.prototype.hasOwnProperty.call(lockEntry, 'version') &&
|
|
857
|
+
typeof lockEntry.version !== 'string') {
|
|
858
|
+
throw new Error(`Post-generation install lockfile workspace dependency ${name} version is invalid.`);
|
|
859
|
+
}
|
|
860
|
+
if (lockEntry.resolution !== undefined &&
|
|
861
|
+
!isRecord(lockEntry.resolution)) {
|
|
862
|
+
throw new Error(`Post-generation install lockfile workspace dependency ${name} resolution is invalid.`);
|
|
863
|
+
}
|
|
864
|
+
if (isRecord(lockEntry.resolution) &&
|
|
865
|
+
Object.prototype.hasOwnProperty.call(lockEntry.resolution, 'directory') &&
|
|
866
|
+
typeof lockEntry.resolution.directory !== 'string') {
|
|
867
|
+
throw new Error(`Post-generation install lockfile workspace dependency ${name} resolution directory is invalid.`);
|
|
868
|
+
}
|
|
869
|
+
if (resolvedReferences.size !== 1) {
|
|
870
|
+
throw new Error(`Post-generation install lockfile workspace dependency ${name} lacks one exact local target.`);
|
|
871
|
+
}
|
|
872
|
+
const [target] = resolvedReferences;
|
|
873
|
+
const targetManifestPath = `${target}/package.json`;
|
|
874
|
+
if (!workspaceManifestPaths.has(targetManifestPath)) {
|
|
875
|
+
throw new Error(`Post-generation install lockfile workspace dependency ${name} targets an undeclared workspace package.`);
|
|
876
|
+
}
|
|
877
|
+
const targetManifestSource = reader.read(targetManifestPath);
|
|
878
|
+
const targetManifest = targetManifestSource
|
|
879
|
+
? parseBoundedJson(targetManifestSource, `Post-generation install workspace target ${targetManifestPath}`)
|
|
880
|
+
: undefined;
|
|
881
|
+
if (!isRecord(targetManifest) || targetManifest.name !== name) {
|
|
882
|
+
throw new Error(`Post-generation install lockfile workspace dependency ${name} targets a different package identity.`);
|
|
883
|
+
}
|
|
884
|
+
// Workspace links bind the already-digested target manifest. They
|
|
885
|
+
// are not file/tarball inputs and must not recursively absorb source,
|
|
886
|
+
// generated ownership, or local agent state into install authority.
|
|
887
|
+
workspaceLockEntries.add(lockEntry);
|
|
888
|
+
if (isRecord(lockEntry.resolution)) {
|
|
889
|
+
workspaceResolutionEntries.add(lockEntry.resolution);
|
|
890
|
+
}
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
if (resolvedReferences.size > 0) {
|
|
894
|
+
throw new Error(`Post-generation install lockfile redirects registry dependency ${name} to local input.`);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
function lockfileLocalReferences(source, reader, workspaceManifestPaths = new Set()) {
|
|
901
|
+
const lockfile = parseBoundedYaml(source, 'Post-generation install pnpm-lock.yaml');
|
|
902
|
+
if (!isRecord(lockfile) ||
|
|
903
|
+
!['9', '9.0', 9].includes(lockfile.lockfileVersion)) {
|
|
904
|
+
throw new Error('Post-generation install requires pnpm lockfile schema 9 authority.');
|
|
905
|
+
}
|
|
906
|
+
const references = new Set();
|
|
907
|
+
const workspaceLockEntries = new WeakSet();
|
|
908
|
+
const workspaceResolutionEntries = new WeakSet();
|
|
909
|
+
if (reader) {
|
|
910
|
+
assertLockfileImporterSemantics(lockfile, reader, workspaceManifestPaths, references, workspaceLockEntries, workspaceResolutionEntries);
|
|
911
|
+
}
|
|
912
|
+
const inspectString = (value, label, baseDirectory, suppressLocalReference = false) => {
|
|
913
|
+
if (/(?:^|[@(])(?:bitbucket:|git(?:\+|:)|github:|gitlab:|ssh:)/iu.test(value) ||
|
|
914
|
+
/https?:\/\/[^\s'"}]*\.git(?:[#?\s'"}]|$)/iu.test(value)) {
|
|
915
|
+
throw new Error('Post-generation install lockfile contains unsupported VCS or external executable authority.');
|
|
916
|
+
}
|
|
917
|
+
if (!suppressLocalReference) {
|
|
918
|
+
for (const reference of localReferencesInLockValue(value, baseDirectory, label)) {
|
|
919
|
+
references.add(reference);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
const visit = (value, label, baseDirectory, suppressLocalReference = false) => {
|
|
924
|
+
if (typeof value === 'string') {
|
|
925
|
+
inspectString(value, label, baseDirectory, suppressLocalReference);
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
if (Array.isArray(value)) {
|
|
929
|
+
value.forEach((nested, index) => visit(nested, `${label}[${index}]`, baseDirectory, suppressLocalReference));
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
if (!isRecord(value))
|
|
933
|
+
return;
|
|
934
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
935
|
+
const normalizedKey = key.toLowerCase().replace(/[^a-z0-9]/gu, '');
|
|
936
|
+
const resolutionMetadata = /(?:^|\.)resolution$/u.test(label);
|
|
937
|
+
const suppressNestedLocalReference = typeof nested === 'string' &&
|
|
938
|
+
((key === 'version' && workspaceLockEntries.has(value)) ||
|
|
939
|
+
(key === 'directory' && workspaceResolutionEntries.has(value)));
|
|
940
|
+
inspectString(key, `${label} key`, baseDirectory);
|
|
941
|
+
if (resolutionMetadata &&
|
|
942
|
+
(['commit', 'gitcommittish', 'gitrange', 'repo', 'repository'].includes(normalizedKey) ||
|
|
943
|
+
(normalizedKey === 'type' &&
|
|
944
|
+
typeof nested === 'string' &&
|
|
945
|
+
/^(?:git|ssh|vcs)$/iu.test(nested)))) {
|
|
946
|
+
throw new Error('Post-generation install lockfile contains unsupported VCS authority fields.');
|
|
947
|
+
}
|
|
948
|
+
if (resolutionMetadata &&
|
|
949
|
+
normalizedKey === 'directory' &&
|
|
950
|
+
!suppressNestedLocalReference) {
|
|
951
|
+
if (typeof nested !== 'string') {
|
|
952
|
+
throw new Error('Post-generation install lockfile local directory target is invalid.');
|
|
953
|
+
}
|
|
954
|
+
references.add(normalizedLiteralReferencedPath('', nested, 'Lockfile local directory target'));
|
|
955
|
+
}
|
|
956
|
+
if (normalizedKey === 'specifier' && typeof nested === 'string') {
|
|
957
|
+
assertSupportedDependencySpecification(nested, 'Lockfile dependency specification');
|
|
958
|
+
}
|
|
959
|
+
if (label === 'pnpm-lock.yaml' && key === 'importers') {
|
|
960
|
+
if (!isRecord(nested)) {
|
|
961
|
+
throw new Error('Post-generation install lockfile importers are invalid.');
|
|
962
|
+
}
|
|
963
|
+
for (const [importer, importerValue] of Object.entries(nested)) {
|
|
964
|
+
visit(importerValue, `${label}.importers.${importer}`, importerBaseDirectory(importer));
|
|
965
|
+
}
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
visit(nested, `${label}.${key}`, baseDirectory, suppressNestedLocalReference);
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
visit(lockfile, 'pnpm-lock.yaml', '');
|
|
972
|
+
return [...references].sort(literalCompare);
|
|
973
|
+
}
|
|
974
|
+
function assertNoExoticLockfileAuthority(source, reader, workspaceManifestPaths) {
|
|
975
|
+
lockfileLocalReferences(source, reader, workspaceManifestPaths);
|
|
976
|
+
}
|
|
977
|
+
function assertNoInjectedWorkspaceDependencies(manifest, label) {
|
|
978
|
+
if (!isRecord(manifest) || !isRecord(manifest.dependenciesMeta))
|
|
979
|
+
return;
|
|
980
|
+
for (const [dependency, metadata] of Object.entries(manifest.dependenciesMeta)) {
|
|
981
|
+
if (isRecord(metadata) && metadata.injected === true) {
|
|
982
|
+
throw new Error(`Post-generation install does not accept injected workspace dependency ${dependency} in ${label}.`);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
function npmrcResolutionSemantics(source) {
|
|
987
|
+
const semantics = {};
|
|
988
|
+
const referencedFiles = new Set();
|
|
989
|
+
const secretKey = /(?:^|:)(?:_auth|_authtoken|_password|username|password|token|otp|email)$/iu;
|
|
990
|
+
for (const sourceLine of source.toString('utf8').split(/\r?\n/u)) {
|
|
991
|
+
const line = sourceLine.trim();
|
|
992
|
+
if (!line || line.startsWith('#') || line.startsWith(';'))
|
|
993
|
+
continue;
|
|
994
|
+
const separator = line.indexOf('=');
|
|
995
|
+
if (separator <= 0) {
|
|
996
|
+
throw new Error('Post-generation install .npmrc must use bounded key=value semantics.');
|
|
997
|
+
}
|
|
998
|
+
const key = line.slice(0, separator).trim().toLowerCase();
|
|
999
|
+
let value = line.slice(separator + 1).trim();
|
|
1000
|
+
if (/\$\{[^}]+\}/u.test(key)) {
|
|
1001
|
+
throw new Error('Post-generation install .npmrc keys cannot interpolate ambient environment variables.');
|
|
1002
|
+
}
|
|
1003
|
+
if (secretKey.test(key)) {
|
|
1004
|
+
const credential = /^\$\{([A-Z][A-Z0-9_]*)\}$/u.exec(value);
|
|
1005
|
+
if (!credential) {
|
|
1006
|
+
throw new Error('Post-generation install .npmrc credentials must use one explicit credential environment reference.');
|
|
1007
|
+
}
|
|
1008
|
+
const environmentName = credential[1] ?? '';
|
|
1009
|
+
if (!ALLOWED_NPM_CREDENTIAL_ENVIRONMENT.has(environmentName)) {
|
|
1010
|
+
throw new Error('Post-generation install .npmrc credentials may interpolate only the explicit credential environment allowlist.');
|
|
1011
|
+
}
|
|
1012
|
+
// Only the allowlisted placeholder shape and normalized registry/auth
|
|
1013
|
+
// target enter the authority digest. Credential values never do.
|
|
1014
|
+
semantics[key] = `credential-environment:${environmentName}`;
|
|
1015
|
+
continue;
|
|
1016
|
+
}
|
|
1017
|
+
if (/^(?:globalconfig|userconfig)$/u.test(key)) {
|
|
1018
|
+
throw new Error('Post-generation install .npmrc cannot redirect user or global configuration.');
|
|
1019
|
+
}
|
|
1020
|
+
if (/\$\{[^}]+\}/u.test(value)) {
|
|
1021
|
+
throw new Error('Post-generation install non-secret .npmrc resolution semantics cannot interpolate ambient environment variables.');
|
|
1022
|
+
}
|
|
1023
|
+
const normalizedKey = key.replace(/[^a-z0-9]/gu, '');
|
|
1024
|
+
if (UNSUPPORTED_EXECUTABLE_PNPM_SETTINGS.has(normalizedKey)) {
|
|
1025
|
+
throw new Error(`Post-generation install does not accept executable or externally loaded pnpm setting ${key}.`);
|
|
1026
|
+
}
|
|
1027
|
+
if (PHASE_CONTROLLED_PNPM_SETTINGS.has(normalizedKey)) {
|
|
1028
|
+
throw new Error(`Post-generation install controls pnpm ${key} per protocol phase; .npmrc cannot override it.`);
|
|
1029
|
+
}
|
|
1030
|
+
if (!ALLOWED_NPMRC_RESOLUTION_SETTINGS.has(normalizedKey) &&
|
|
1031
|
+
!key.endsWith(':registry') &&
|
|
1032
|
+
!/(?:^|:)always-auth$/u.test(key)) {
|
|
1033
|
+
throw new Error(`Post-generation install .npmrc setting ${key} is outside the explicit resolution-setting allowlist.`);
|
|
1034
|
+
}
|
|
1035
|
+
if (/^(?:cafile|certfile|keyfile|pnpmfile)$/u.test(key) && value) {
|
|
1036
|
+
referencedFiles.add(value);
|
|
1037
|
+
}
|
|
1038
|
+
try {
|
|
1039
|
+
const url = new URL(value);
|
|
1040
|
+
url.username = '';
|
|
1041
|
+
url.password = '';
|
|
1042
|
+
for (const parameter of [...url.searchParams.keys()]) {
|
|
1043
|
+
if (/(?:auth|key|password|secret|token)/iu.test(parameter)) {
|
|
1044
|
+
url.searchParams.delete(parameter);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
value = url.toString();
|
|
1048
|
+
}
|
|
1049
|
+
catch {
|
|
1050
|
+
// Non-URL npm configuration is retained only in the canonical digest.
|
|
1051
|
+
}
|
|
1052
|
+
semantics[key] = value;
|
|
1053
|
+
}
|
|
1054
|
+
return {
|
|
1055
|
+
referencedFiles: [...referencedFiles].sort(literalCompare),
|
|
1056
|
+
semantics,
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
function assertPhaseControlledPnpmSettings(value, label) {
|
|
1060
|
+
if (!isRecord(value))
|
|
1061
|
+
return;
|
|
1062
|
+
for (const key of Object.keys(value)) {
|
|
1063
|
+
const normalizedKey = key.toLowerCase().replace(/[^a-z0-9]/gu, '');
|
|
1064
|
+
if (key === 'storeDir' &&
|
|
1065
|
+
normalizedKey === 'storedir' &&
|
|
1066
|
+
label === 'pnpm-workspace.yaml settings' &&
|
|
1067
|
+
value[key] === POST_GENERATION_STORE_DIRECTORY) {
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
if (PHASE_CONTROLLED_PNPM_SETTINGS.has(normalizedKey)) {
|
|
1071
|
+
throw new Error(`Post-generation install controls pnpm ${key} per protocol phase; ${label} cannot override it.`);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
function assertRetainedPnpmStore(value) {
|
|
1076
|
+
if (!isRecord(value) ||
|
|
1077
|
+
!Object.prototype.hasOwnProperty.call(value, 'storeDir') ||
|
|
1078
|
+
value.storeDir !== POST_GENERATION_STORE_DIRECTORY) {
|
|
1079
|
+
throw new Error(`Post-generation install requires pnpm storeDir ${POST_GENERATION_STORE_DIRECTORY}.`);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
function assertSupportedPnpmSettings(value, label) {
|
|
1083
|
+
if (!isRecord(value))
|
|
1084
|
+
return;
|
|
1085
|
+
for (const key of Object.keys(value)) {
|
|
1086
|
+
const normalizedKey = key.toLowerCase().replace(/[^a-z0-9]/gu, '');
|
|
1087
|
+
if (UNSUPPORTED_EXECUTABLE_PNPM_SETTINGS.has(normalizedKey)) {
|
|
1088
|
+
throw new Error(`Post-generation install does not accept executable or externally loaded pnpm setting ${key} in ${label}.`);
|
|
1089
|
+
}
|
|
1090
|
+
if (!PHASE_CONTROLLED_PNPM_SETTINGS.has(normalizedKey) &&
|
|
1091
|
+
!ALLOWED_STRUCTURED_PNPM_SETTINGS.has(normalizedKey)) {
|
|
1092
|
+
throw new Error(`Post-generation install pnpm setting ${key} in ${label} is outside the explicit declarative allowlist.`);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
function assertNoAmbientInterpolation(value, label) {
|
|
1097
|
+
if (typeof value === 'string') {
|
|
1098
|
+
if (/\$\{[^}]+\}/u.test(value)) {
|
|
1099
|
+
throw new Error(`Post-generation install ${label} cannot interpolate ambient environment variables.`);
|
|
1100
|
+
}
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
if (Array.isArray(value)) {
|
|
1104
|
+
for (const nested of value)
|
|
1105
|
+
assertNoAmbientInterpolation(nested, label);
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
if (isRecord(value)) {
|
|
1109
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
1110
|
+
if (/\$\{[^}]+\}/u.test(key)) {
|
|
1111
|
+
throw new Error(`Post-generation install ${label} keys cannot interpolate ambient environment variables.`);
|
|
1112
|
+
}
|
|
1113
|
+
assertNoAmbientInterpolation(nested, label);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
function buildInstallAuthority(reader) {
|
|
1118
|
+
const entries = new Map();
|
|
1119
|
+
let observedBytes = 0;
|
|
1120
|
+
const addDigest = (kind, path, contents, mode = null) => {
|
|
1121
|
+
const bytes = Buffer.byteLength(contents);
|
|
1122
|
+
if (bytes > pnpm_toolchain_authority_js_1.PNPM_TOOLCHAIN_AUTHORITY_MAX_FILE_BYTES) {
|
|
1123
|
+
throw new Error(`Install-authority input exceeds 8 MiB at ${path}.`);
|
|
1124
|
+
}
|
|
1125
|
+
observedBytes += bytes;
|
|
1126
|
+
if (observedBytes > pnpm_toolchain_authority_js_1.PNPM_TOOLCHAIN_AUTHORITY_MAX_TOTAL_BYTES) {
|
|
1127
|
+
throw new Error('Post-generation install authority exceeds 64 MiB.');
|
|
1128
|
+
}
|
|
1129
|
+
if (mode !== null &&
|
|
1130
|
+
(!Number.isInteger(mode) || mode < 0 || mode > 0o777)) {
|
|
1131
|
+
throw new Error(`Install-authority input has invalid mode at ${path}.`);
|
|
1132
|
+
}
|
|
1133
|
+
const entry = { digest: sha256(contents), kind, mode, path };
|
|
1134
|
+
const key = `${kind}:${path}`;
|
|
1135
|
+
const previous = entries.get(key);
|
|
1136
|
+
if (previous && previous.digest !== entry.digest) {
|
|
1137
|
+
throw new Error(`Install-authority input is ambiguous at ${path}.`);
|
|
1138
|
+
}
|
|
1139
|
+
entries.set(key, entry);
|
|
1140
|
+
if (entries.size > pnpm_toolchain_authority_js_1.PNPM_TOOLCHAIN_AUTHORITY_MAX_ENTRIES) {
|
|
1141
|
+
throw new Error('Post-generation install authority exceeds 4096 entries.');
|
|
1142
|
+
}
|
|
1143
|
+
};
|
|
1144
|
+
const addFile = (kind, path) => {
|
|
1145
|
+
normalizedWorkspacePath(path, 'Install-authority input');
|
|
1146
|
+
const contents = reader.read(path);
|
|
1147
|
+
if (contents === null || reader.kind(path) !== 'file') {
|
|
1148
|
+
throw new Error(`Install-authority input is missing at ${path}.`);
|
|
1149
|
+
}
|
|
1150
|
+
const mode = reader.mode(path);
|
|
1151
|
+
if (mode === null) {
|
|
1152
|
+
throw new Error(`Install-authority input mode is missing at ${path}.`);
|
|
1153
|
+
}
|
|
1154
|
+
addDigest(kind, path, contents, mode);
|
|
1155
|
+
return contents;
|
|
1156
|
+
};
|
|
1157
|
+
const rootManifestSource = addFile('root-manifest', 'package.json');
|
|
1158
|
+
for (const alternate of ['package.json5', 'package.yaml']) {
|
|
1159
|
+
if (reader.kind(alternate) !== 'missing') {
|
|
1160
|
+
throw new Error(`Post-generation install accepts only package.json root manifests; found ${alternate}.`);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
const rootManifest = parseBoundedJson(rootManifestSource, 'Post-generation install root package.json');
|
|
1164
|
+
(0, pnpm_toolchain_authority_js_1.parsePnpmPackageManagerAuthority)(rootManifestSource.toString('utf8'));
|
|
1165
|
+
const workspaceSource = addFile('workspace-config', 'pnpm-workspace.yaml');
|
|
1166
|
+
const workspace = parseBoundedYaml(workspaceSource, 'Post-generation install pnpm-workspace.yaml');
|
|
1167
|
+
assertPhaseControlledPnpmSettings(isRecord(rootManifest) ? rootManifest.pnpm : undefined, 'root package.json pnpm settings');
|
|
1168
|
+
assertPhaseControlledPnpmSettings(workspace, 'pnpm-workspace.yaml settings');
|
|
1169
|
+
assertNoAmbientInterpolation(isRecord(rootManifest) ? rootManifest.pnpm : undefined, 'root package.json pnpm settings');
|
|
1170
|
+
assertNoAmbientInterpolation(workspace, 'pnpm-workspace.yaml settings');
|
|
1171
|
+
assertSupportedPnpmSettings(isRecord(rootManifest) ? rootManifest.pnpm : undefined, 'root package.json pnpm settings');
|
|
1172
|
+
assertSupportedPnpmSettings(workspace, 'pnpm-workspace.yaml settings');
|
|
1173
|
+
assertRetainedPnpmStore(workspace);
|
|
1174
|
+
const manifests = ['package.json'];
|
|
1175
|
+
for (const manifestPath of exactWorkspaceManifestPaths(reader, workspace)) {
|
|
1176
|
+
addFile('workspace-manifest', manifestPath);
|
|
1177
|
+
manifests.push(manifestPath);
|
|
1178
|
+
}
|
|
1179
|
+
assertWorkspaceDependencyTargets(reader, manifests);
|
|
1180
|
+
const localReferences = new Set();
|
|
1181
|
+
const lockfileSource = reader.read(LOCKFILE_PATH);
|
|
1182
|
+
if (lockfileSource === null || reader.kind(LOCKFILE_PATH) !== 'file') {
|
|
1183
|
+
throw new Error('Install-authority lockfile is missing.');
|
|
1184
|
+
}
|
|
1185
|
+
for (const reference of lockfileLocalReferences(lockfileSource, reader, new Set(manifests))) {
|
|
1186
|
+
localReferences.add(reference);
|
|
1187
|
+
}
|
|
1188
|
+
for (const manifestPath of manifests) {
|
|
1189
|
+
const source = reader.read(manifestPath);
|
|
1190
|
+
if (source === null) {
|
|
1191
|
+
throw new Error(`Install-authority manifest vanished at ${manifestPath}.`);
|
|
1192
|
+
}
|
|
1193
|
+
const manifest = parseBoundedJson(source, `Install-authority manifest ${manifestPath}`);
|
|
1194
|
+
assertNoInjectedWorkspaceDependencies(manifest, manifestPath);
|
|
1195
|
+
for (const reference of dependencyReferences(manifest, node_path_1.posix.dirname(manifestPath) === '.' ? '' : node_path_1.posix.dirname(manifestPath))) {
|
|
1196
|
+
localReferences.add(reference);
|
|
1197
|
+
}
|
|
1198
|
+
if (manifestPath === 'package.json' && isRecord(manifest)) {
|
|
1199
|
+
if (isRecord(manifest.pnpm)) {
|
|
1200
|
+
for (const key of [
|
|
1201
|
+
'catalog',
|
|
1202
|
+
'catalogs',
|
|
1203
|
+
'overrides',
|
|
1204
|
+
'packageExtensions',
|
|
1205
|
+
]) {
|
|
1206
|
+
collectNestedLocalReferences(manifest.pnpm[key], '', localReferences);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
collectNestedLocalReferences(manifest.resolutions, '', localReferences);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
if (isRecord(workspace)) {
|
|
1213
|
+
for (const key of [
|
|
1214
|
+
'catalog',
|
|
1215
|
+
'catalogs',
|
|
1216
|
+
'overrides',
|
|
1217
|
+
'packageExtensions',
|
|
1218
|
+
]) {
|
|
1219
|
+
collectNestedLocalReferences(workspace[key], '', localReferences);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
for (const candidate of ['.pnpmfile.cjs', '.pnpmfile.mjs']) {
|
|
1223
|
+
if (reader.kind(candidate) !== 'missing') {
|
|
1224
|
+
throw new Error(`Post-generation install does not accept executable pnpm hooks at ${candidate}; use declarative, bound resolution configuration.`);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
if (isRecord(workspace) && typeof workspace.pnpmfile === 'string') {
|
|
1228
|
+
throw new Error('Post-generation install does not accept executable pnpmfile hooks in pnpm-workspace.yaml.');
|
|
1229
|
+
}
|
|
1230
|
+
const npmrcSource = reader.read('.npmrc');
|
|
1231
|
+
if (npmrcSource !== null) {
|
|
1232
|
+
const npmrc = npmrcResolutionSemantics(npmrcSource);
|
|
1233
|
+
addDigest('npmrc-resolution', '.npmrc', (0, canonical_json_1.canonicalJson)(npmrc.semantics));
|
|
1234
|
+
for (const referenced of npmrc.referencedFiles) {
|
|
1235
|
+
const path = normalizedLiteralReferencedPath('', referenced, '.npmrc referenced input');
|
|
1236
|
+
addFile('npmrc-input', path);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
const patches = new Set();
|
|
1240
|
+
const collectPatchValues = (value) => {
|
|
1241
|
+
if (!isRecord(value))
|
|
1242
|
+
return;
|
|
1243
|
+
for (const patch of Object.values(value)) {
|
|
1244
|
+
if (typeof patch === 'string')
|
|
1245
|
+
patches.add(patch);
|
|
1246
|
+
else if (isRecord(patch) && typeof patch.path === 'string') {
|
|
1247
|
+
patches.add(patch.path);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
};
|
|
1251
|
+
if (isRecord(rootManifest) && isRecord(rootManifest.pnpm)) {
|
|
1252
|
+
collectPatchValues(rootManifest.pnpm.patchedDependencies);
|
|
1253
|
+
}
|
|
1254
|
+
if (isRecord(workspace))
|
|
1255
|
+
collectPatchValues(workspace.patchedDependencies);
|
|
1256
|
+
for (const patch of [...patches].sort(literalCompare)) {
|
|
1257
|
+
addFile('patch', normalizedLiteralReferencedPath('', patch, 'Referenced patch path'));
|
|
1258
|
+
}
|
|
1259
|
+
const localVisited = new Set();
|
|
1260
|
+
const queuedLocalReferences = new Set(localReferences);
|
|
1261
|
+
const pendingLocalReferences = [...localReferences].sort(literalCompare);
|
|
1262
|
+
const enqueueLocalReference = (reference) => {
|
|
1263
|
+
if (queuedLocalReferences.has(reference))
|
|
1264
|
+
return;
|
|
1265
|
+
queuedLocalReferences.add(reference);
|
|
1266
|
+
pendingLocalReferences.push(reference);
|
|
1267
|
+
if (pendingLocalReferences.length > pnpm_toolchain_authority_js_1.PNPM_TOOLCHAIN_AUTHORITY_MAX_ENTRIES) {
|
|
1268
|
+
throw new Error('Local install-authority dependency graph is unbounded.');
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
const addLocalInput = (path, depth) => {
|
|
1272
|
+
if (depth > 64 || localVisited.size > pnpm_toolchain_authority_js_1.PNPM_TOOLCHAIN_AUTHORITY_MAX_ENTRIES * 4) {
|
|
1273
|
+
throw new Error('Local install-authority input traversal is unbounded.');
|
|
1274
|
+
}
|
|
1275
|
+
if (localVisited.has(path))
|
|
1276
|
+
return;
|
|
1277
|
+
localVisited.add(path);
|
|
1278
|
+
const kind = reader.kind(path);
|
|
1279
|
+
if (kind === 'missing') {
|
|
1280
|
+
throw new Error(`Local install-authority input is missing at ${path}.`);
|
|
1281
|
+
}
|
|
1282
|
+
if (kind === 'file') {
|
|
1283
|
+
addFile('local-input', path);
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
if (depth === 0) {
|
|
1287
|
+
for (const unsupported of ['.npmrc', 'package.json5', 'package.yaml']) {
|
|
1288
|
+
if (reader.kind(`${path}/${unsupported}`) !== 'missing') {
|
|
1289
|
+
throw new Error(`Local install-authority input ${path} contains unsupported ${unsupported} authority.`);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
const manifestPath = `${path}/package.json`;
|
|
1293
|
+
const manifestSource = reader.read(manifestPath);
|
|
1294
|
+
if (manifestSource !== null && reader.kind(manifestPath) === 'file') {
|
|
1295
|
+
const manifest = parseBoundedJson(manifestSource, `Local install-authority manifest ${manifestPath}`);
|
|
1296
|
+
assertNoInjectedWorkspaceDependencies(manifest, manifestPath);
|
|
1297
|
+
for (const reference of dependencyReferences(manifest, path)) {
|
|
1298
|
+
enqueueLocalReference(reference);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
for (const child of reader.children(path)) {
|
|
1303
|
+
if (child === '.ebk') {
|
|
1304
|
+
throw new Error(`Local install-authority input ${path || '.'} contains reserved .ebk state.`);
|
|
1305
|
+
}
|
|
1306
|
+
if (['.git', 'node_modules'].includes(child))
|
|
1307
|
+
continue;
|
|
1308
|
+
addLocalInput(`${path}/${child}`, depth + 1);
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
for (let index = 0; index < pendingLocalReferences.length; index += 1) {
|
|
1312
|
+
const reference = pendingLocalReferences[index];
|
|
1313
|
+
if (reference)
|
|
1314
|
+
addLocalInput(reference, 0);
|
|
1315
|
+
}
|
|
1316
|
+
const canonicalEntries = [...entries.values()].sort((left, right) => literalCompare(`${left.kind}:${left.path}`, `${right.kind}:${right.path}`));
|
|
1317
|
+
return {
|
|
1318
|
+
digest: sha256((0, canonical_json_1.canonicalJson)(canonicalEntries)),
|
|
1319
|
+
entries: canonicalEntries,
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
function bindSemanticPrerequisites(reader, prerequisites) {
|
|
1323
|
+
return prerequisites.map((prerequisite) => {
|
|
1324
|
+
const source = reader.read(prerequisite.path);
|
|
1325
|
+
if (source === null || reader.kind(prerequisite.path) !== 'file') {
|
|
1326
|
+
throw new Error(`Post-generation install semantic prerequisite is missing at ${prerequisite.path}.`);
|
|
1327
|
+
}
|
|
1328
|
+
const registry = parseBoundedJson(source, `Post-generation install feature registry ${prerequisite.path}`);
|
|
1329
|
+
const features = isRecord(registry) ? registry.features : undefined;
|
|
1330
|
+
if (!isRecord(registry) ||
|
|
1331
|
+
registry.schemaVersion !== 1 ||
|
|
1332
|
+
!Array.isArray(features) ||
|
|
1333
|
+
!features.some((feature) => isRecord(feature) && feature.id === prerequisite.selectedFeature)) {
|
|
1334
|
+
throw new Error(`Post-generation install feature ${prerequisite.selectedFeature} is not registered at ${prerequisite.path}.`);
|
|
1335
|
+
}
|
|
1336
|
+
return { ...prerequisite, digest: sha256(source) };
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
function installAuthorityMatches(workspaceRoot, expected) {
|
|
1340
|
+
try {
|
|
1341
|
+
const observed = buildInstallAuthority(diskInstallAuthorityReader(workspaceRoot));
|
|
1342
|
+
return (0, canonical_json_1.canonicalJson)(observed) === (0, canonical_json_1.canonicalJson)(expected);
|
|
1343
|
+
}
|
|
1344
|
+
catch {
|
|
1345
|
+
return false;
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
function semanticPrerequisitesMatch(workspaceRoot, record) {
|
|
1349
|
+
try {
|
|
1350
|
+
const descriptors = record.semanticPrerequisites.map(({ kind, path, selectedFeature }) => ({ kind, path, selectedFeature }));
|
|
1351
|
+
const observed = bindSemanticPrerequisites(diskInstallAuthorityReader(workspaceRoot), descriptors);
|
|
1352
|
+
return ((0, canonical_json_1.canonicalJson)(observed) === (0, canonical_json_1.canonicalJson)(record.semanticPrerequisites));
|
|
1353
|
+
}
|
|
1354
|
+
catch {
|
|
1355
|
+
return false;
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
function treeChangeSnapshot(tree) {
|
|
1359
|
+
return new Map(tree
|
|
1360
|
+
.listChanges()
|
|
1361
|
+
.filter((change) => !change.path.startsWith(`${INSTALL_RECORD_DIRECTORY}/`) &&
|
|
1362
|
+
change.path !== INSTALL_LEASE_PATH &&
|
|
1363
|
+
change.path !== INSTALL_ACTIVE_PATH &&
|
|
1364
|
+
change.path !== INSTALL_COMPLETION_PATH &&
|
|
1365
|
+
change.path !== INSTALL_BATCH_CONFLICT_PATH)
|
|
1366
|
+
.map((change) => [
|
|
1367
|
+
change.path,
|
|
1368
|
+
`${change.type}:${change.content === null ? 'null' : sha256(change.content)}`,
|
|
1369
|
+
]));
|
|
1370
|
+
}
|
|
1371
|
+
function collectTreeFiles(tree, path, files) {
|
|
1372
|
+
const contents = tree.read(path);
|
|
1373
|
+
if (contents !== null) {
|
|
1374
|
+
files.set(path, contents);
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
for (const child of tree.children(path)) {
|
|
1378
|
+
collectTreeFiles(tree, path ? `${path}/${child}` : child, files);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
function capturePostGenerationInstallState(tree, scope, operation) {
|
|
1382
|
+
assertOperation(operation);
|
|
1383
|
+
const markerPath = postGenerationInstallMarkerPath(operation.key);
|
|
1384
|
+
reconcilePostGenerationInstallWorkspaceState(tree);
|
|
1385
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.resolve)(tree.root))) {
|
|
1386
|
+
assertNoRetainedBatchConflict(tree.root);
|
|
1387
|
+
}
|
|
1388
|
+
const existing = pendingMarkerPaths(tree);
|
|
1389
|
+
if (existing.length > 0 || tree.exists(markerPath)) {
|
|
1390
|
+
throw new Error(`Post-generation install is already pending at ${existing[0] ?? markerPath}.`);
|
|
1391
|
+
}
|
|
1392
|
+
const lockfile = tree.read(LOCKFILE_PATH);
|
|
1393
|
+
if (lockfile === null) {
|
|
1394
|
+
throw new Error(`${LOCKFILE_PATH} is required for resumable post-generation installation.`);
|
|
1395
|
+
}
|
|
1396
|
+
const authorityReader = treeInstallAuthorityReader(tree);
|
|
1397
|
+
const workspaceSource = authorityReader.read('pnpm-workspace.yaml');
|
|
1398
|
+
const workspaceManifestPaths = workspaceSource
|
|
1399
|
+
? new Set(exactWorkspaceManifestPaths(authorityReader, parseBoundedYaml(workspaceSource, 'Post-generation install pnpm-workspace.yaml')))
|
|
1400
|
+
: new Set();
|
|
1401
|
+
assertNoExoticLockfileAuthority(lockfile, authorityReader, workspaceManifestPaths);
|
|
1402
|
+
const packageSource = tree.read('package.json', 'utf8');
|
|
1403
|
+
if (packageSource === null) {
|
|
1404
|
+
throw new Error('Post-generation install requires a root package.json with an exact pnpm packageManager authority.');
|
|
1405
|
+
}
|
|
1406
|
+
const packageManagerDescriptor = (0, pnpm_toolchain_authority_js_1.parsePnpmPackageManagerAuthority)(packageSource);
|
|
1407
|
+
const packageManager = {
|
|
1408
|
+
...packageManagerDescriptor,
|
|
1409
|
+
toolchain: (0, pnpm_toolchain_authority_js_1.resolvePnpmToolchainAuthority)(packageManagerDescriptor),
|
|
1410
|
+
};
|
|
1411
|
+
const contents = new Map();
|
|
1412
|
+
for (const path of [
|
|
1413
|
+
...new Set([...(scope.exactPaths ?? []), ...scope.roots]),
|
|
1414
|
+
]) {
|
|
1415
|
+
collectTreeFiles(tree, normalizedWorkspacePath(path, 'Install-planned path'), contents);
|
|
1416
|
+
}
|
|
1417
|
+
for (const path of scope.requiredPaths) {
|
|
1418
|
+
normalizedWorkspacePath(path, 'Required install-planned path');
|
|
1419
|
+
}
|
|
1420
|
+
const reservation = {
|
|
1421
|
+
generation: (0, node_crypto_1.randomUUID)(),
|
|
1422
|
+
kind: INSTALL_RESERVATION_KIND,
|
|
1423
|
+
operationFingerprint: expectedFingerprint(operation),
|
|
1424
|
+
operationId: operation.id,
|
|
1425
|
+
schemaVersion: INSTALL_RESERVATION_SCHEMA_VERSION,
|
|
1426
|
+
};
|
|
1427
|
+
// Queue the marker before any operation-owned source. Nx retains the first
|
|
1428
|
+
// insertion position when the reservation is finalized below, so every
|
|
1429
|
+
// possible flush prefix that contains generated source already contains the
|
|
1430
|
+
// exact, parseable recovery record.
|
|
1431
|
+
tree.write(markerPath, `${JSON.stringify(reservation, null, 2)}\n`, {
|
|
1432
|
+
mode: 0o600,
|
|
1433
|
+
});
|
|
1434
|
+
return {
|
|
1435
|
+
changes: treeChangeSnapshot(tree),
|
|
1436
|
+
contents,
|
|
1437
|
+
lockfileDigest: sha256(lockfile),
|
|
1438
|
+
packageManager,
|
|
1439
|
+
reservation,
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
function pathAllowed(path, scope) {
|
|
1443
|
+
return ((scope.exactPaths ?? []).includes(path) ||
|
|
1444
|
+
scope.roots.some((root) => path === root || path.startsWith(`${root}/`)));
|
|
1445
|
+
}
|
|
1446
|
+
function plannedFiles(tree, before, scope) {
|
|
1447
|
+
const afterChanges = treeChangeSnapshot(tree);
|
|
1448
|
+
const changedPaths = [
|
|
1449
|
+
...new Set([...before.changes.keys(), ...afterChanges.keys()]),
|
|
1450
|
+
]
|
|
1451
|
+
.filter((path) => before.changes.get(path) !== afterChanges.get(path))
|
|
1452
|
+
.sort(literalCompare);
|
|
1453
|
+
for (const path of changedPaths) {
|
|
1454
|
+
if (!pathAllowed(path, scope)) {
|
|
1455
|
+
throw new Error(`Post-generation install planning escaped its declared surface at ${path}.`);
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
const files = changedPaths.map((path) => {
|
|
1459
|
+
const contents = tree.read(path);
|
|
1460
|
+
if (contents === null) {
|
|
1461
|
+
throw new Error(`Post-generation install planning cannot retain deleted path ${path}.`);
|
|
1462
|
+
}
|
|
1463
|
+
const previous = before.contents.get(path);
|
|
1464
|
+
return {
|
|
1465
|
+
afterDigest: sha256(contents),
|
|
1466
|
+
beforeDigest: previous ? sha256(previous) : null,
|
|
1467
|
+
mode: previous ? 'modified' : 'created',
|
|
1468
|
+
path,
|
|
1469
|
+
};
|
|
1470
|
+
});
|
|
1471
|
+
for (const required of scope.requiredPaths) {
|
|
1472
|
+
if (!files.some(({ path }) => path === required)) {
|
|
1473
|
+
throw new Error(`Post-generation install planning omitted required path ${required}.`);
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
if (files.length === 0) {
|
|
1477
|
+
throw new Error('Post-generation install planning produced no source files.');
|
|
1478
|
+
}
|
|
1479
|
+
return files;
|
|
1480
|
+
}
|
|
1481
|
+
function serializeRecord(record) {
|
|
1482
|
+
const serialized = `${JSON.stringify(record, null, 2)}\n`;
|
|
1483
|
+
if (Buffer.byteLength(serialized) > MAX_RECORD_BYTES) {
|
|
1484
|
+
throw new Error('Post-generation install record exceeds 256 KiB.');
|
|
1485
|
+
}
|
|
1486
|
+
return serialized;
|
|
1487
|
+
}
|
|
1488
|
+
function parseReservation(source) {
|
|
1489
|
+
let value;
|
|
1490
|
+
try {
|
|
1491
|
+
value = JSON.parse(source);
|
|
1492
|
+
}
|
|
1493
|
+
catch {
|
|
1494
|
+
throw new Error('Post-generation install reservation is not valid JSON.');
|
|
1495
|
+
}
|
|
1496
|
+
if (!isRecord(value)) {
|
|
1497
|
+
throw new Error('Post-generation install reservation must be an object.');
|
|
1498
|
+
}
|
|
1499
|
+
const reservation = value;
|
|
1500
|
+
if (reservation.kind !== INSTALL_RESERVATION_KIND ||
|
|
1501
|
+
reservation.schemaVersion !== INSTALL_RESERVATION_SCHEMA_VERSION ||
|
|
1502
|
+
!validGeneration(reservation.generation) ||
|
|
1503
|
+
!DIGEST_PATTERN.test(reservation.operationFingerprint) ||
|
|
1504
|
+
typeof reservation.operationId !== 'string') {
|
|
1505
|
+
throw new Error('Post-generation install reservation is invalid.');
|
|
1506
|
+
}
|
|
1507
|
+
return reservation;
|
|
1508
|
+
}
|
|
1509
|
+
function createRecord(operation, before, files, installAuthority, semanticPrerequisites) {
|
|
1510
|
+
const retainedOperation = {
|
|
1511
|
+
generator: operation.generator,
|
|
1512
|
+
id: operation.id,
|
|
1513
|
+
request: operation.request,
|
|
1514
|
+
resumeArguments: operation.resumeArguments,
|
|
1515
|
+
semanticPrerequisites: operation.semanticPrerequisites ?? [],
|
|
1516
|
+
sourceRoot: operation.sourceRoot,
|
|
1517
|
+
};
|
|
1518
|
+
return {
|
|
1519
|
+
attempts: {
|
|
1520
|
+
frozen: 0,
|
|
1521
|
+
initial: 0,
|
|
1522
|
+
maxFrozen: MAX_FROZEN_ATTEMPTS,
|
|
1523
|
+
maxInitial: MAX_NON_FROZEN_ATTEMPTS,
|
|
1524
|
+
},
|
|
1525
|
+
classification: 'generated-source-complete-install-pending',
|
|
1526
|
+
cleanup: {
|
|
1527
|
+
createdPaths: files
|
|
1528
|
+
.filter(({ mode }) => mode === 'created')
|
|
1529
|
+
.map(({ path }) => path),
|
|
1530
|
+
modifiedPaths: files
|
|
1531
|
+
.filter(({ mode }) => mode === 'modified')
|
|
1532
|
+
.map(({ path }) => path),
|
|
1533
|
+
owner: `@empire-builder-kit/nx:${operation.generator}`,
|
|
1534
|
+
strategy: 'retain-generated-source',
|
|
1535
|
+
},
|
|
1536
|
+
execution: {
|
|
1537
|
+
generation: null,
|
|
1538
|
+
ownerPid: null,
|
|
1539
|
+
phase: 'non-frozen-checkpoint',
|
|
1540
|
+
},
|
|
1541
|
+
kind: INSTALL_RECORD_KIND,
|
|
1542
|
+
installAuthority,
|
|
1543
|
+
lockfile: {
|
|
1544
|
+
beforeDigest: before.lockfileDigest,
|
|
1545
|
+
currentDigest: before.lockfileDigest,
|
|
1546
|
+
path: LOCKFILE_PATH,
|
|
1547
|
+
},
|
|
1548
|
+
operation: {
|
|
1549
|
+
...retainedOperation,
|
|
1550
|
+
fingerprint: operationFingerprint(retainedOperation),
|
|
1551
|
+
},
|
|
1552
|
+
packageManager: before.packageManager,
|
|
1553
|
+
plannedSurface: {
|
|
1554
|
+
digest: sha256((0, canonical_json_1.canonicalJson)(files)),
|
|
1555
|
+
files,
|
|
1556
|
+
},
|
|
1557
|
+
revision: 0,
|
|
1558
|
+
schemaVersion: INSTALL_RECORD_SCHEMA_VERSION,
|
|
1559
|
+
semanticPrerequisites,
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
function isRecord(value) {
|
|
1563
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
1564
|
+
}
|
|
1565
|
+
function stringArray(value) {
|
|
1566
|
+
return (Array.isArray(value) &&
|
|
1567
|
+
value.length <= 1024 &&
|
|
1568
|
+
value.every((entry) => typeof entry === 'string'));
|
|
1569
|
+
}
|
|
1570
|
+
function validGeneration(value) {
|
|
1571
|
+
return typeof value === 'string' && GENERATION_PATTERN.test(value);
|
|
1572
|
+
}
|
|
1573
|
+
function parseRecord(source) {
|
|
1574
|
+
if (Buffer.byteLength(source) > MAX_RECORD_BYTES) {
|
|
1575
|
+
throw new Error('Post-generation install record exceeds 256 KiB.');
|
|
1576
|
+
}
|
|
1577
|
+
let value;
|
|
1578
|
+
try {
|
|
1579
|
+
value = JSON.parse(source);
|
|
1580
|
+
}
|
|
1581
|
+
catch {
|
|
1582
|
+
throw new Error('Post-generation install record is not valid JSON.');
|
|
1583
|
+
}
|
|
1584
|
+
if (!isRecord(value)) {
|
|
1585
|
+
throw new Error('Post-generation install record must be an object.');
|
|
1586
|
+
}
|
|
1587
|
+
const record = value;
|
|
1588
|
+
const operation = record.operation;
|
|
1589
|
+
const files = record.plannedSurface?.files;
|
|
1590
|
+
const authorityEntries = record.installAuthority?.entries;
|
|
1591
|
+
const operationPrerequisites = operation?.semanticPrerequisites;
|
|
1592
|
+
const boundPrerequisites = record.semanticPrerequisites;
|
|
1593
|
+
const packageManager = record.packageManager;
|
|
1594
|
+
const toolchain = isRecord(packageManager)
|
|
1595
|
+
? packageManager.toolchain
|
|
1596
|
+
: undefined;
|
|
1597
|
+
const node = isRecord(toolchain) ? toolchain.node : undefined;
|
|
1598
|
+
const retainedPackageManagerMatch = isRecord(packageManager) &&
|
|
1599
|
+
typeof packageManager.packageManager === 'string'
|
|
1600
|
+
? pnpm_toolchain_authority_js_1.EXACT_PNPM_PACKAGE_MANAGER_PATTERN.exec(packageManager.packageManager)
|
|
1601
|
+
: null;
|
|
1602
|
+
const running = record.execution?.phase === 'non-frozen-running' ||
|
|
1603
|
+
record.execution?.phase === 'frozen-running';
|
|
1604
|
+
if (record.schemaVersion !== INSTALL_RECORD_SCHEMA_VERSION ||
|
|
1605
|
+
record.kind !== INSTALL_RECORD_KIND ||
|
|
1606
|
+
![
|
|
1607
|
+
'generated-source-complete-install-pending',
|
|
1608
|
+
'install-pending-semantic-prerequisite-blocked',
|
|
1609
|
+
'install-pending-source-integrity-blocked',
|
|
1610
|
+
'install-pending-reviewed-recovery-required',
|
|
1611
|
+
].includes(record.classification) ||
|
|
1612
|
+
!(record.blocker === undefined ||
|
|
1613
|
+
[
|
|
1614
|
+
'active-marker-missing',
|
|
1615
|
+
'frozen-retry-exhausted',
|
|
1616
|
+
'install-authority-drift',
|
|
1617
|
+
'install-execution-ambiguous',
|
|
1618
|
+
'lockfile-drift',
|
|
1619
|
+
'lockfile-missing',
|
|
1620
|
+
'non-frozen-retry-exhausted',
|
|
1621
|
+
'planned-surface-drift',
|
|
1622
|
+
'semantic-prerequisite-drift',
|
|
1623
|
+
'workspace-batch-ambiguous',
|
|
1624
|
+
].includes(record.blocker)) ||
|
|
1625
|
+
(record.classification === 'generated-source-complete-install-pending' &&
|
|
1626
|
+
record.blocker !== undefined) ||
|
|
1627
|
+
(record.classification === 'install-pending-source-integrity-blocked' &&
|
|
1628
|
+
![
|
|
1629
|
+
'install-authority-drift',
|
|
1630
|
+
'lockfile-drift',
|
|
1631
|
+
'lockfile-missing',
|
|
1632
|
+
'planned-surface-drift',
|
|
1633
|
+
'workspace-batch-ambiguous',
|
|
1634
|
+
].includes(record.blocker ?? '')) ||
|
|
1635
|
+
(record.classification ===
|
|
1636
|
+
'install-pending-semantic-prerequisite-blocked' &&
|
|
1637
|
+
record.blocker !== 'semantic-prerequisite-drift') ||
|
|
1638
|
+
(record.classification === 'install-pending-reviewed-recovery-required' &&
|
|
1639
|
+
![
|
|
1640
|
+
'active-marker-missing',
|
|
1641
|
+
'frozen-retry-exhausted',
|
|
1642
|
+
'install-execution-ambiguous',
|
|
1643
|
+
'non-frozen-retry-exhausted',
|
|
1644
|
+
].includes(record.blocker ?? '')) ||
|
|
1645
|
+
!Number.isInteger(record.revision) ||
|
|
1646
|
+
record.revision < 0 ||
|
|
1647
|
+
!isRecord(operation) ||
|
|
1648
|
+
!['slice', 'workload'].includes(operation.generator) ||
|
|
1649
|
+
typeof operation.id !== 'string' ||
|
|
1650
|
+
!isRecord(operation.request) ||
|
|
1651
|
+
!stringArray(operation.resumeArguments) ||
|
|
1652
|
+
!Array.isArray(operationPrerequisites) ||
|
|
1653
|
+
operationPrerequisites.length > 16 ||
|
|
1654
|
+
typeof operation.sourceRoot !== 'string' ||
|
|
1655
|
+
typeof operation.fingerprint !== 'string' ||
|
|
1656
|
+
!DIGEST_PATTERN.test(operation.fingerprint) ||
|
|
1657
|
+
!isRecord(record.installAuthority) ||
|
|
1658
|
+
!DIGEST_PATTERN.test(record.installAuthority.digest) ||
|
|
1659
|
+
!Array.isArray(authorityEntries) ||
|
|
1660
|
+
authorityEntries.length < 2 ||
|
|
1661
|
+
authorityEntries.length > pnpm_toolchain_authority_js_1.PNPM_TOOLCHAIN_AUTHORITY_MAX_ENTRIES ||
|
|
1662
|
+
!Array.isArray(boundPrerequisites) ||
|
|
1663
|
+
boundPrerequisites.length !== operationPrerequisites.length ||
|
|
1664
|
+
!isRecord(packageManager) ||
|
|
1665
|
+
Object.keys(packageManager).sort().join(',') !==
|
|
1666
|
+
'packageManager,toolchain,version' ||
|
|
1667
|
+
!retainedPackageManagerMatch?.[1] ||
|
|
1668
|
+
packageManager.version !== retainedPackageManagerMatch[1] ||
|
|
1669
|
+
!isRecord(toolchain) ||
|
|
1670
|
+
Object.keys(toolchain).sort().join(',') !==
|
|
1671
|
+
'digest,entryCount,entryPoint,home,node,totalBytes' ||
|
|
1672
|
+
!DIGEST_PATTERN.test(toolchain.digest) ||
|
|
1673
|
+
typeof toolchain.home !== 'string' ||
|
|
1674
|
+
!(0, node_path_1.isAbsolute)(toolchain.home) ||
|
|
1675
|
+
/[\0\r\n"]/u.test(toolchain.home) ||
|
|
1676
|
+
typeof toolchain.entryPoint !== 'string' ||
|
|
1677
|
+
!(0, node_path_1.isAbsolute)(toolchain.entryPoint) ||
|
|
1678
|
+
/[\0\r\n"]/u.test(toolchain.entryPoint) ||
|
|
1679
|
+
!isRecord(node) ||
|
|
1680
|
+
Object.keys(node).sort().join(',') !== 'digest,path,version' ||
|
|
1681
|
+
!DIGEST_PATTERN.test(node.digest) ||
|
|
1682
|
+
typeof node.path !== 'string' ||
|
|
1683
|
+
!(0, node_path_1.isAbsolute)(node.path) ||
|
|
1684
|
+
/[\0\r\n"]/u.test(node.path) ||
|
|
1685
|
+
typeof node.version !== 'string' ||
|
|
1686
|
+
!/^v\d+\.\d+\.\d+/u.test(node.version) ||
|
|
1687
|
+
!Number.isInteger(toolchain.entryCount) ||
|
|
1688
|
+
toolchain.entryCount <= 0 ||
|
|
1689
|
+
toolchain.entryCount > pnpm_toolchain_authority_js_1.PNPM_TOOLCHAIN_AUTHORITY_MAX_ENTRIES ||
|
|
1690
|
+
!Number.isInteger(toolchain.totalBytes) ||
|
|
1691
|
+
toolchain.totalBytes <= 0 ||
|
|
1692
|
+
toolchain.totalBytes > pnpm_toolchain_authority_js_1.PNPM_TOOLCHAIN_AUTHORITY_MAX_TOTAL_BYTES ||
|
|
1693
|
+
!isRecord(record.attempts) ||
|
|
1694
|
+
!Number.isInteger(record.attempts.initial) ||
|
|
1695
|
+
!Number.isInteger(record.attempts.frozen) ||
|
|
1696
|
+
record.attempts.initial < 0 ||
|
|
1697
|
+
record.attempts.initial > MAX_NON_FROZEN_ATTEMPTS ||
|
|
1698
|
+
record.attempts.frozen < 0 ||
|
|
1699
|
+
record.attempts.frozen > MAX_FROZEN_ATTEMPTS ||
|
|
1700
|
+
record.attempts.maxInitial !== MAX_NON_FROZEN_ATTEMPTS ||
|
|
1701
|
+
record.attempts.maxFrozen !== MAX_FROZEN_ATTEMPTS ||
|
|
1702
|
+
!isRecord(record.execution) ||
|
|
1703
|
+
![
|
|
1704
|
+
'complete-checkpoint',
|
|
1705
|
+
'frozen-checkpoint',
|
|
1706
|
+
'frozen-running',
|
|
1707
|
+
'non-frozen-checkpoint',
|
|
1708
|
+
'non-frozen-running',
|
|
1709
|
+
'reviewed-recovery-required',
|
|
1710
|
+
].includes(record.execution.phase) ||
|
|
1711
|
+
!(record.execution.generation === null ||
|
|
1712
|
+
validGeneration(record.execution.generation)) ||
|
|
1713
|
+
!(record.execution.ownerPid === null ||
|
|
1714
|
+
(Number.isInteger(record.execution.ownerPid) &&
|
|
1715
|
+
record.execution.ownerPid > 0)) ||
|
|
1716
|
+
(running &&
|
|
1717
|
+
(!validGeneration(record.execution.generation) ||
|
|
1718
|
+
record.execution.ownerPid === null)) ||
|
|
1719
|
+
!isRecord(record.lockfile) ||
|
|
1720
|
+
record.lockfile.path !== LOCKFILE_PATH ||
|
|
1721
|
+
!DIGEST_PATTERN.test(record.lockfile.beforeDigest) ||
|
|
1722
|
+
!DIGEST_PATTERN.test(record.lockfile.currentDigest) ||
|
|
1723
|
+
!(record.lockfile.candidateDigest === undefined ||
|
|
1724
|
+
DIGEST_PATTERN.test(record.lockfile.candidateDigest)) ||
|
|
1725
|
+
!(record.lockfile.candidateGeneration === undefined ||
|
|
1726
|
+
validGeneration(record.lockfile.candidateGeneration)) ||
|
|
1727
|
+
(record.lockfile.candidateDigest === undefined) !==
|
|
1728
|
+
(record.lockfile.candidateGeneration === undefined) ||
|
|
1729
|
+
((record.execution.phase === 'frozen-checkpoint' ||
|
|
1730
|
+
record.execution.phase === 'frozen-running') &&
|
|
1731
|
+
(!record.lockfile.candidateDigest ||
|
|
1732
|
+
!record.lockfile.candidateGeneration)) ||
|
|
1733
|
+
((record.execution.phase === 'non-frozen-checkpoint' ||
|
|
1734
|
+
record.execution.phase === 'non-frozen-running' ||
|
|
1735
|
+
record.execution.phase === 'complete-checkpoint') &&
|
|
1736
|
+
(record.lockfile.candidateDigest !== undefined ||
|
|
1737
|
+
record.lockfile.candidateGeneration !== undefined)) ||
|
|
1738
|
+
!isRecord(record.plannedSurface) ||
|
|
1739
|
+
!DIGEST_PATTERN.test(record.plannedSurface.digest) ||
|
|
1740
|
+
!Array.isArray(files) ||
|
|
1741
|
+
files.length === 0 ||
|
|
1742
|
+
files.length > 1024 ||
|
|
1743
|
+
!isRecord(record.cleanup) ||
|
|
1744
|
+
record.cleanup.owner !== `@empire-builder-kit/nx:${operation.generator}` ||
|
|
1745
|
+
record.cleanup.strategy !== 'retain-generated-source' ||
|
|
1746
|
+
!stringArray(record.cleanup.createdPaths) ||
|
|
1747
|
+
!stringArray(record.cleanup.modifiedPaths)) {
|
|
1748
|
+
throw new Error('Post-generation install record is invalid.');
|
|
1749
|
+
}
|
|
1750
|
+
const retainedOperation = {
|
|
1751
|
+
generator: operation.generator,
|
|
1752
|
+
id: operation.id,
|
|
1753
|
+
request: operation.request,
|
|
1754
|
+
resumeArguments: operation.resumeArguments,
|
|
1755
|
+
semanticPrerequisites: operationPrerequisites,
|
|
1756
|
+
sourceRoot: operation.sourceRoot,
|
|
1757
|
+
};
|
|
1758
|
+
normalizedWorkspacePath(operation.sourceRoot, 'Install source root');
|
|
1759
|
+
if (operation.fingerprint !== operationFingerprint(retainedOperation)) {
|
|
1760
|
+
throw new Error('Post-generation install operation binding is invalid.');
|
|
1761
|
+
}
|
|
1762
|
+
let previousPrerequisite = '';
|
|
1763
|
+
for (let index = 0; index < operationPrerequisites.length; index += 1) {
|
|
1764
|
+
const prerequisite = operationPrerequisites[index];
|
|
1765
|
+
const bound = boundPrerequisites[index];
|
|
1766
|
+
if (!isRecord(prerequisite) ||
|
|
1767
|
+
prerequisite.kind !== 'registered-slice-feature' ||
|
|
1768
|
+
Object.keys(prerequisite).sort().join(',') !==
|
|
1769
|
+
'kind,path,selectedFeature' ||
|
|
1770
|
+
typeof prerequisite.path !== 'string' ||
|
|
1771
|
+
typeof prerequisite.selectedFeature !== 'string' ||
|
|
1772
|
+
!isRecord(bound) ||
|
|
1773
|
+
Object.keys(bound).sort().join(',') !==
|
|
1774
|
+
'digest,kind,path,selectedFeature' ||
|
|
1775
|
+
bound.kind !== prerequisite.kind ||
|
|
1776
|
+
bound.path !== prerequisite.path ||
|
|
1777
|
+
bound.selectedFeature !== prerequisite.selectedFeature ||
|
|
1778
|
+
typeof bound.digest !== 'string' ||
|
|
1779
|
+
!DIGEST_PATTERN.test(bound.digest)) {
|
|
1780
|
+
throw new Error('Post-generation install semantic prerequisite binding is invalid.');
|
|
1781
|
+
}
|
|
1782
|
+
postGenerationInstallFeaturePrerequisite(prerequisite.path, prerequisite.selectedFeature);
|
|
1783
|
+
const key = semanticPrerequisiteKey(prerequisite);
|
|
1784
|
+
if (key <= previousPrerequisite) {
|
|
1785
|
+
throw new Error('Post-generation install semantic prerequisite binding is not canonical.');
|
|
1786
|
+
}
|
|
1787
|
+
previousPrerequisite = key;
|
|
1788
|
+
}
|
|
1789
|
+
let previousAuthority = '';
|
|
1790
|
+
const validAuthorityKinds = [
|
|
1791
|
+
'local-input',
|
|
1792
|
+
'npmrc-input',
|
|
1793
|
+
'npmrc-resolution',
|
|
1794
|
+
'patch',
|
|
1795
|
+
'root-manifest',
|
|
1796
|
+
'workspace-config',
|
|
1797
|
+
'workspace-manifest',
|
|
1798
|
+
];
|
|
1799
|
+
for (const entry of authorityEntries) {
|
|
1800
|
+
if (!isRecord(entry) ||
|
|
1801
|
+
Object.keys(entry).sort().join(',') !== 'digest,kind,mode,path' ||
|
|
1802
|
+
typeof entry.kind !== 'string' ||
|
|
1803
|
+
!validAuthorityKinds.includes(entry.kind) ||
|
|
1804
|
+
typeof entry.path !== 'string' ||
|
|
1805
|
+
typeof entry.digest !== 'string' ||
|
|
1806
|
+
!DIGEST_PATTERN.test(entry.digest) ||
|
|
1807
|
+
!(entry.mode === null ||
|
|
1808
|
+
(typeof entry.mode === 'number' &&
|
|
1809
|
+
Number.isInteger(entry.mode) &&
|
|
1810
|
+
entry.mode >= 0 &&
|
|
1811
|
+
entry.mode <= 0o777)) ||
|
|
1812
|
+
(entry.kind === 'npmrc-resolution' && entry.mode !== null) ||
|
|
1813
|
+
(entry.kind !== 'npmrc-resolution' && entry.mode === null)) {
|
|
1814
|
+
throw new Error('Post-generation install authority entry is invalid.');
|
|
1815
|
+
}
|
|
1816
|
+
normalizedWorkspacePath(entry.path, 'Install-authority entry path');
|
|
1817
|
+
const key = `${entry.kind}:${entry.path}`;
|
|
1818
|
+
if (key <= previousAuthority) {
|
|
1819
|
+
throw new Error('Post-generation install authority is not canonical.');
|
|
1820
|
+
}
|
|
1821
|
+
previousAuthority = key;
|
|
1822
|
+
}
|
|
1823
|
+
if (record.installAuthority.digest !== sha256((0, canonical_json_1.canonicalJson)(authorityEntries))) {
|
|
1824
|
+
throw new Error('Post-generation install authority digest is invalid.');
|
|
1825
|
+
}
|
|
1826
|
+
let previous = '';
|
|
1827
|
+
for (const file of files) {
|
|
1828
|
+
if (!isRecord(file) ||
|
|
1829
|
+
typeof file.path !== 'string' ||
|
|
1830
|
+
file.path <= previous ||
|
|
1831
|
+
!['created', 'modified'].includes(file.mode) ||
|
|
1832
|
+
!DIGEST_PATTERN.test(file.afterDigest) ||
|
|
1833
|
+
!(file.beforeDigest === null ||
|
|
1834
|
+
(typeof file.beforeDigest === 'string' &&
|
|
1835
|
+
DIGEST_PATTERN.test(file.beforeDigest))) ||
|
|
1836
|
+
(file.mode === 'created' && file.beforeDigest !== null) ||
|
|
1837
|
+
(file.mode === 'modified' && file.beforeDigest === null)) {
|
|
1838
|
+
throw new Error('Post-generation install planned surface is invalid.');
|
|
1839
|
+
}
|
|
1840
|
+
normalizedWorkspacePath(file.path, 'Install-planned file');
|
|
1841
|
+
previous = file.path;
|
|
1842
|
+
}
|
|
1843
|
+
if (record.plannedSurface.digest !== sha256((0, canonical_json_1.canonicalJson)(files))) {
|
|
1844
|
+
throw new Error('Post-generation install planned surface digest is invalid.');
|
|
1845
|
+
}
|
|
1846
|
+
const created = files
|
|
1847
|
+
.filter(({ mode }) => mode === 'created')
|
|
1848
|
+
.map(({ path }) => path);
|
|
1849
|
+
const modified = files
|
|
1850
|
+
.filter(({ mode }) => mode === 'modified')
|
|
1851
|
+
.map(({ path }) => path);
|
|
1852
|
+
if ((0, canonical_json_1.canonicalJson)(record.cleanup.createdPaths) !== (0, canonical_json_1.canonicalJson)(created) ||
|
|
1853
|
+
(0, canonical_json_1.canonicalJson)(record.cleanup.modifiedPaths) !== (0, canonical_json_1.canonicalJson)(modified)) {
|
|
1854
|
+
throw new Error('Post-generation install cleanup ownership is invalid.');
|
|
1855
|
+
}
|
|
1856
|
+
return record;
|
|
1857
|
+
}
|
|
1858
|
+
function parseLease(source) {
|
|
1859
|
+
if (Buffer.byteLength(source) > MAX_LEASE_BYTES) {
|
|
1860
|
+
throw new Error('Post-generation install lease exceeds 16 KiB.');
|
|
1861
|
+
}
|
|
1862
|
+
let value;
|
|
1863
|
+
try {
|
|
1864
|
+
value = JSON.parse(source);
|
|
1865
|
+
}
|
|
1866
|
+
catch {
|
|
1867
|
+
throw new Error('Post-generation install lease is not valid JSON.');
|
|
1868
|
+
}
|
|
1869
|
+
if (!isRecord(value)) {
|
|
1870
|
+
throw new Error('Post-generation install lease must be an object.');
|
|
1871
|
+
}
|
|
1872
|
+
const lease = value;
|
|
1873
|
+
if (lease.kind !== INSTALL_LEASE_KIND ||
|
|
1874
|
+
lease.schemaVersion !== INSTALL_LEASE_SCHEMA_VERSION ||
|
|
1875
|
+
!validGeneration(lease.generation) ||
|
|
1876
|
+
!DIGEST_PATTERN.test(lease.operationFingerprint) ||
|
|
1877
|
+
!Number.isInteger(lease.ownerPid) ||
|
|
1878
|
+
lease.ownerPid <= 0 ||
|
|
1879
|
+
typeof lease.acquiredAt !== 'string' ||
|
|
1880
|
+
Number.isNaN(Date.parse(lease.acquiredAt))) {
|
|
1881
|
+
throw new Error('Post-generation install lease is invalid.');
|
|
1882
|
+
}
|
|
1883
|
+
return lease;
|
|
1884
|
+
}
|
|
1885
|
+
function parseBatchConflict(source) {
|
|
1886
|
+
if (Buffer.byteLength(source) > MAX_LEASE_BYTES) {
|
|
1887
|
+
throw new Error('Post-generation install batch conflict exceeds 16 KiB.');
|
|
1888
|
+
}
|
|
1889
|
+
let value;
|
|
1890
|
+
try {
|
|
1891
|
+
value = JSON.parse(source);
|
|
1892
|
+
}
|
|
1893
|
+
catch {
|
|
1894
|
+
throw new Error('Post-generation install batch conflict is not valid JSON.');
|
|
1895
|
+
}
|
|
1896
|
+
if (!isRecord(value)) {
|
|
1897
|
+
throw new Error('Post-generation install batch conflict is invalid.');
|
|
1898
|
+
}
|
|
1899
|
+
const conflict = value;
|
|
1900
|
+
if (conflict.kind !== INSTALL_BATCH_CONFLICT_KIND ||
|
|
1901
|
+
conflict.schemaVersion !== 1 ||
|
|
1902
|
+
typeof conflict.observedAt !== 'string' ||
|
|
1903
|
+
Number.isNaN(Date.parse(conflict.observedAt)) ||
|
|
1904
|
+
!Array.isArray(conflict.markers) ||
|
|
1905
|
+
conflict.markers.length < 2 ||
|
|
1906
|
+
conflict.markers.length > 1024) {
|
|
1907
|
+
throw new Error('Post-generation install batch conflict is invalid.');
|
|
1908
|
+
}
|
|
1909
|
+
let previous = '';
|
|
1910
|
+
for (const marker of conflict.markers) {
|
|
1911
|
+
if (!isRecord(marker) ||
|
|
1912
|
+
typeof marker.path !== 'string' ||
|
|
1913
|
+
marker.path <= previous ||
|
|
1914
|
+
!marker.path.startsWith(`${INSTALL_RECORD_DIRECTORY}/`) ||
|
|
1915
|
+
!DIGEST_PATTERN.test(marker.digest)) {
|
|
1916
|
+
throw new Error('Post-generation install batch conflict is invalid.');
|
|
1917
|
+
}
|
|
1918
|
+
normalizedWorkspacePath(marker.path, 'Batch-conflict marker path');
|
|
1919
|
+
previous = marker.path;
|
|
1920
|
+
}
|
|
1921
|
+
return conflict;
|
|
1922
|
+
}
|
|
1923
|
+
function batchConflictPath(workspaceRoot) {
|
|
1924
|
+
return confinedAbsolutePath(workspaceRoot, INSTALL_BATCH_CONFLICT_PATH);
|
|
1925
|
+
}
|
|
1926
|
+
function assertNoRetainedBatchConflict(workspaceRoot) {
|
|
1927
|
+
const absolute = batchConflictPath(workspaceRoot);
|
|
1928
|
+
if (!(0, node_fs_1.existsSync)(absolute))
|
|
1929
|
+
return;
|
|
1930
|
+
assertSafeRegularFile(absolute, 'Post-generation install batch conflict');
|
|
1931
|
+
parseBatchConflict((0, node_fs_1.readFileSync)(absolute, 'utf8'));
|
|
1932
|
+
throw new Error(`Post-generation install batch is ambiguous and requires reviewed reconciliation at ${INSTALL_BATCH_CONFLICT_PATH}; no automatic install was started.`);
|
|
1933
|
+
}
|
|
1934
|
+
function retainBatchConflict(workspaceRoot, markerPaths) {
|
|
1935
|
+
const markers = markerPaths.sort(literalCompare).map((path) => ({
|
|
1936
|
+
digest: recordIdentity(readDiskRecord(workspaceRoot, path)),
|
|
1937
|
+
path,
|
|
1938
|
+
}));
|
|
1939
|
+
const conflict = {
|
|
1940
|
+
kind: INSTALL_BATCH_CONFLICT_KIND,
|
|
1941
|
+
markers,
|
|
1942
|
+
observedAt: new Date().toISOString(),
|
|
1943
|
+
schemaVersion: 1,
|
|
1944
|
+
};
|
|
1945
|
+
ensureSafeDirectory(workspaceRoot, (0, node_path_1.dirname)(INSTALL_BATCH_CONFLICT_PATH));
|
|
1946
|
+
const absolute = batchConflictPath(workspaceRoot);
|
|
1947
|
+
try {
|
|
1948
|
+
writeExclusiveFile(absolute, `${JSON.stringify(conflict, null, 2)}\n`);
|
|
1949
|
+
}
|
|
1950
|
+
catch (error) {
|
|
1951
|
+
if (error.code !== 'EEXIST')
|
|
1952
|
+
throw error;
|
|
1953
|
+
assertSafeRegularFile(absolute, 'Post-generation install batch conflict');
|
|
1954
|
+
parseBatchConflict((0, node_fs_1.readFileSync)(absolute, 'utf8'));
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
function readDiskRecord(workspaceRoot, markerPath) {
|
|
1958
|
+
ensureSafeDirectory(workspaceRoot, INSTALL_RECORD_DIRECTORY);
|
|
1959
|
+
const absolute = confinedAbsolutePath(workspaceRoot, markerPath);
|
|
1960
|
+
assertSafeRegularFile(absolute, 'Post-generation install marker');
|
|
1961
|
+
return parseRecord((0, node_fs_1.readFileSync)(absolute, 'utf8'));
|
|
1962
|
+
}
|
|
1963
|
+
function readDiskLease(workspaceRoot) {
|
|
1964
|
+
ensureSafeDirectory(workspaceRoot, (0, node_path_1.dirname)(INSTALL_LEASE_PATH));
|
|
1965
|
+
const absolute = confinedAbsolutePath(workspaceRoot, INSTALL_LEASE_PATH);
|
|
1966
|
+
assertSafeRegularFile(absolute, 'Post-generation install lease');
|
|
1967
|
+
return parseLease((0, node_fs_1.readFileSync)(absolute, 'utf8'));
|
|
1968
|
+
}
|
|
1969
|
+
function processIsAbsent(pid) {
|
|
1970
|
+
try {
|
|
1971
|
+
process.kill(pid, 0);
|
|
1972
|
+
return false;
|
|
1973
|
+
}
|
|
1974
|
+
catch (error) {
|
|
1975
|
+
const code = error.code;
|
|
1976
|
+
if (code === 'ESRCH')
|
|
1977
|
+
return true;
|
|
1978
|
+
if (code === 'EPERM')
|
|
1979
|
+
return false;
|
|
1980
|
+
throw error;
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
function createLease(workspaceRoot, operationFingerprint) {
|
|
1984
|
+
const lease = {
|
|
1985
|
+
acquiredAt: new Date().toISOString(),
|
|
1986
|
+
generation: (0, node_crypto_1.randomUUID)(),
|
|
1987
|
+
kind: INSTALL_LEASE_KIND,
|
|
1988
|
+
operationFingerprint,
|
|
1989
|
+
ownerPid: process.pid,
|
|
1990
|
+
schemaVersion: INSTALL_LEASE_SCHEMA_VERSION,
|
|
1991
|
+
};
|
|
1992
|
+
ensureSafeDirectory(workspaceRoot, (0, node_path_1.dirname)(INSTALL_LEASE_PATH));
|
|
1993
|
+
writeExclusiveFile(confinedAbsolutePath(workspaceRoot, INSTALL_LEASE_PATH), `${JSON.stringify(lease, null, 2)}\n`);
|
|
1994
|
+
return lease;
|
|
1995
|
+
}
|
|
1996
|
+
function leaseIdentity(lease) {
|
|
1997
|
+
return sha256((0, canonical_json_1.canonicalJson)(lease));
|
|
1998
|
+
}
|
|
1999
|
+
function staleLeaseClaims(workspaceRoot) {
|
|
2000
|
+
const directory = ensureSafeDirectory(workspaceRoot, (0, node_path_1.dirname)(INSTALL_LEASE_PATH));
|
|
2001
|
+
const prefix = `${INSTALL_LEASE_PATH.slice(INSTALL_LEASE_PATH.lastIndexOf('/') + 1)}.stale-`;
|
|
2002
|
+
return (0, node_fs_1.readdirSync)(directory, { withFileTypes: true })
|
|
2003
|
+
.filter((entry) => entry.name.startsWith(prefix))
|
|
2004
|
+
.map((entry) => {
|
|
2005
|
+
if (!entry.isFile() ||
|
|
2006
|
+
entry.isSymbolicLink() ||
|
|
2007
|
+
!/^post-generation-install-lease\.json\.stale-[0-9a-f-]+-[0-9a-f-]+$/.test(entry.name)) {
|
|
2008
|
+
throw new Error(`Post-generation install lease directory contains unsafe takeover claim ${entry.name}.`);
|
|
2009
|
+
}
|
|
2010
|
+
const absolute = (0, node_path_1.join)(directory, entry.name);
|
|
2011
|
+
assertSafeRegularFile(absolute, 'Claimed stale post-generation install lease');
|
|
2012
|
+
return {
|
|
2013
|
+
lease: parseLease((0, node_fs_1.readFileSync)(absolute, 'utf8')),
|
|
2014
|
+
path: absolute,
|
|
2015
|
+
};
|
|
2016
|
+
})
|
|
2017
|
+
.sort((left, right) => literalCompare(left.path, right.path));
|
|
2018
|
+
}
|
|
2019
|
+
function recordIsBoundToAnyLease(record, leases) {
|
|
2020
|
+
if (!record.execution.generation)
|
|
2021
|
+
return false;
|
|
2022
|
+
const running = record.execution.phase === 'non-frozen-running' ||
|
|
2023
|
+
record.execution.phase === 'frozen-running';
|
|
2024
|
+
return leases.some((lease) => lease.operationFingerprint === record.operation.fingerprint &&
|
|
2025
|
+
lease.generation === record.execution.generation &&
|
|
2026
|
+
(!running || lease.ownerPid === record.execution.ownerPid));
|
|
2027
|
+
}
|
|
2028
|
+
function recordIsPristineForInitialLease(workspaceRoot, record) {
|
|
2029
|
+
return (record.revision === 0 &&
|
|
2030
|
+
record.classification === 'generated-source-complete-install-pending' &&
|
|
2031
|
+
record.blocker === undefined &&
|
|
2032
|
+
record.attempts.initial === 0 &&
|
|
2033
|
+
record.attempts.frozen === 0 &&
|
|
2034
|
+
record.execution.generation === null &&
|
|
2035
|
+
record.execution.ownerPid === null &&
|
|
2036
|
+
record.execution.phase === 'non-frozen-checkpoint' &&
|
|
2037
|
+
record.lockfile.beforeDigest === record.lockfile.currentDigest &&
|
|
2038
|
+
record.lockfile.candidateDigest === undefined &&
|
|
2039
|
+
record.lockfile.candidateGeneration === undefined &&
|
|
2040
|
+
installAuthorityMatches(workspaceRoot, record.installAuthority) &&
|
|
2041
|
+
semanticPrerequisitesMatch(workspaceRoot, record) &&
|
|
2042
|
+
plannedSurfaceMatches(workspaceRoot, record) &&
|
|
2043
|
+
lockfileDigest(workspaceRoot) === record.lockfile.currentDigest);
|
|
2044
|
+
}
|
|
2045
|
+
function assertDeadTakeoverAuthority(workspaceRoot, markerPath, expectedOperationFingerprint, leases) {
|
|
2046
|
+
if (leases.length === 0) {
|
|
2047
|
+
throw new Error('Post-generation install takeover has no stale lease.');
|
|
2048
|
+
}
|
|
2049
|
+
for (const lease of leases) {
|
|
2050
|
+
if (lease.operationFingerprint !== expectedOperationFingerprint) {
|
|
2051
|
+
throw new Error('Post-generation install dead lease belongs to a different generator request; takeover was refused.');
|
|
2052
|
+
}
|
|
2053
|
+
if (!processIsAbsent(lease.ownerPid)) {
|
|
2054
|
+
throw new Error(`Post-generation install lease is active for PID ${lease.ownerPid}; another agent must finish or stop before recovery.`);
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
const record = readDiskRecord(workspaceRoot, markerPath);
|
|
2058
|
+
if (record.operation.fingerprint !== expectedOperationFingerprint ||
|
|
2059
|
+
(!recordIsBoundToAnyLease(record, leases) &&
|
|
2060
|
+
!recordIsPristineForInitialLease(workspaceRoot, record))) {
|
|
2061
|
+
throw new Error('Post-generation install dead lease is not bound to the exact retained record; takeover was refused.');
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
function triggerProtocolCheckpoint(dependencies, checkpoint) {
|
|
2065
|
+
if (dependencies.protocolCheckpoint?.(checkpoint) === 'simulate-crash') {
|
|
2066
|
+
throw new SimulatedProtocolCrash(checkpoint);
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
function acquireLease(workspaceRoot, markerPath, operationFingerprint, dependencies) {
|
|
2070
|
+
const leasePath = confinedAbsolutePath(workspaceRoot, INSTALL_LEASE_PATH);
|
|
2071
|
+
const existingClaims = staleLeaseClaims(workspaceRoot);
|
|
2072
|
+
const staleLeases = existingClaims.map(({ lease }) => lease);
|
|
2073
|
+
if ((0, node_fs_1.existsSync)(leasePath)) {
|
|
2074
|
+
const current = readDiskLease(workspaceRoot);
|
|
2075
|
+
if (!processIsAbsent(current.ownerPid)) {
|
|
2076
|
+
throw new Error(`Post-generation install lease is active for PID ${current.ownerPid}; another agent must finish or stop before recovery.`);
|
|
2077
|
+
}
|
|
2078
|
+
assertDeadTakeoverAuthority(workspaceRoot, markerPath, operationFingerprint, [...staleLeases, current]);
|
|
2079
|
+
const claimPath = `${leasePath}.stale-${current.generation}-${(0, node_crypto_1.randomUUID)()}`;
|
|
2080
|
+
(0, node_fs_1.renameSync)(leasePath, claimPath);
|
|
2081
|
+
assertSafeRegularFile(claimPath, 'Claimed stale post-generation install lease');
|
|
2082
|
+
const claimed = parseLease((0, node_fs_1.readFileSync)(claimPath, 'utf8'));
|
|
2083
|
+
if (leaseIdentity(claimed) !== leaseIdentity(current)) {
|
|
2084
|
+
throw new Error('Post-generation install stale lease changed during atomic claim.');
|
|
2085
|
+
}
|
|
2086
|
+
staleLeases.push(claimed);
|
|
2087
|
+
existingClaims.push({ lease: claimed, path: claimPath });
|
|
2088
|
+
triggerProtocolCheckpoint(dependencies, 'after-stale-lease-rename');
|
|
2089
|
+
}
|
|
2090
|
+
else if (staleLeases.length > 0) {
|
|
2091
|
+
assertDeadTakeoverAuthority(workspaceRoot, markerPath, operationFingerprint, staleLeases);
|
|
2092
|
+
}
|
|
2093
|
+
else {
|
|
2094
|
+
const lease = createLease(workspaceRoot, operationFingerprint);
|
|
2095
|
+
triggerProtocolCheckpoint(dependencies, 'after-initial-lease-create');
|
|
2096
|
+
return {
|
|
2097
|
+
claims: [],
|
|
2098
|
+
lease,
|
|
2099
|
+
staleLeases: [],
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2102
|
+
const lease = createLease(workspaceRoot, operationFingerprint);
|
|
2103
|
+
triggerProtocolCheckpoint(dependencies, 'after-replacement-lease-create');
|
|
2104
|
+
return { claims: existingClaims, lease, staleLeases };
|
|
2105
|
+
}
|
|
2106
|
+
function clearStaleLeaseClaims(acquired) {
|
|
2107
|
+
for (const claim of acquired.claims) {
|
|
2108
|
+
assertSafeRegularFile(claim.path, 'Claimed stale post-generation install lease');
|
|
2109
|
+
const current = parseLease((0, node_fs_1.readFileSync)(claim.path, 'utf8'));
|
|
2110
|
+
if (leaseIdentity(current) !== leaseIdentity(claim.lease)) {
|
|
2111
|
+
throw new Error('Post-generation install stale claim changed before completion.');
|
|
2112
|
+
}
|
|
2113
|
+
(0, node_fs_1.unlinkSync)(claim.path);
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
function assertLeaseOwned(workspaceRoot, lease, operationFingerprint) {
|
|
2117
|
+
const current = readDiskLease(workspaceRoot);
|
|
2118
|
+
if (current.generation !== lease.generation ||
|
|
2119
|
+
current.ownerPid !== process.pid ||
|
|
2120
|
+
current.operationFingerprint !== operationFingerprint) {
|
|
2121
|
+
throw new Error('Post-generation install lease ownership changed.');
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
function releaseLease(workspaceRoot, lease, operationFingerprint) {
|
|
2125
|
+
assertLeaseOwned(workspaceRoot, lease, operationFingerprint);
|
|
2126
|
+
(0, node_fs_1.unlinkSync)(confinedAbsolutePath(workspaceRoot, INSTALL_LEASE_PATH));
|
|
2127
|
+
}
|
|
2128
|
+
function recordIdentity(record) {
|
|
2129
|
+
return sha256(serializeRecord(record));
|
|
2130
|
+
}
|
|
2131
|
+
function activeJournalAbsolutePath(workspaceRoot) {
|
|
2132
|
+
return confinedAbsolutePath(workspaceRoot, INSTALL_ACTIVE_PATH);
|
|
2133
|
+
}
|
|
2134
|
+
function parseActiveJournal(source) {
|
|
2135
|
+
if (Buffer.byteLength(source) > MAX_RECORD_BYTES + MAX_LEASE_BYTES) {
|
|
2136
|
+
throw new Error('Post-generation install active journal is too large.');
|
|
2137
|
+
}
|
|
2138
|
+
let value;
|
|
2139
|
+
try {
|
|
2140
|
+
value = JSON.parse(source);
|
|
2141
|
+
}
|
|
2142
|
+
catch {
|
|
2143
|
+
throw new Error('Post-generation install active journal is not valid JSON.');
|
|
2144
|
+
}
|
|
2145
|
+
if (!isRecord(value) || !isRecord(value.record)) {
|
|
2146
|
+
throw new Error('Post-generation install active journal is invalid.');
|
|
2147
|
+
}
|
|
2148
|
+
const journal = value;
|
|
2149
|
+
const record = parseRecord(`${JSON.stringify(journal.record)}\n`);
|
|
2150
|
+
if (journal.kind !== INSTALL_ACTIVE_KIND ||
|
|
2151
|
+
journal.schemaVersion !== INSTALL_ACTIVE_SCHEMA_VERSION ||
|
|
2152
|
+
typeof journal.markerPath !== 'string' ||
|
|
2153
|
+
!journal.markerPath.startsWith(`${INSTALL_RECORD_DIRECTORY}/`) ||
|
|
2154
|
+
typeof journal.leaseIdentity !== 'string' ||
|
|
2155
|
+
!DIGEST_PATTERN.test(journal.leaseIdentity) ||
|
|
2156
|
+
typeof journal.recordIdentity !== 'string' ||
|
|
2157
|
+
!DIGEST_PATTERN.test(journal.recordIdentity) ||
|
|
2158
|
+
journal.recordIdentity !== recordIdentity(record) ||
|
|
2159
|
+
!validGeneration(record.execution.generation) ||
|
|
2160
|
+
record.execution.ownerPid === null ||
|
|
2161
|
+
!['frozen-running', 'non-frozen-running'].includes(record.execution.phase)) {
|
|
2162
|
+
throw new Error('Post-generation install active journal is invalid.');
|
|
2163
|
+
}
|
|
2164
|
+
normalizedWorkspacePath(journal.markerPath, 'Active-journal marker path');
|
|
2165
|
+
return { ...journal, record };
|
|
2166
|
+
}
|
|
2167
|
+
function readActiveJournal(workspaceRoot) {
|
|
2168
|
+
const absolute = activeJournalAbsolutePath(workspaceRoot);
|
|
2169
|
+
if (!(0, node_fs_1.existsSync)(absolute))
|
|
2170
|
+
return undefined;
|
|
2171
|
+
assertSafeRegularFile(absolute, 'Post-generation install active journal');
|
|
2172
|
+
return parseActiveJournal((0, node_fs_1.readFileSync)(absolute, 'utf8'));
|
|
2173
|
+
}
|
|
2174
|
+
function createActiveJournal(workspaceRoot, markerPath, record, lease) {
|
|
2175
|
+
assertLeaseOwned(workspaceRoot, lease, record.operation.fingerprint);
|
|
2176
|
+
if (record.execution.generation !== lease.generation ||
|
|
2177
|
+
record.execution.ownerPid !== process.pid ||
|
|
2178
|
+
!['frozen-running', 'non-frozen-running'].includes(record.execution.phase)) {
|
|
2179
|
+
throw new Error('Post-generation install active journal requires an exact running checkpoint.');
|
|
2180
|
+
}
|
|
2181
|
+
const journal = {
|
|
2182
|
+
kind: INSTALL_ACTIVE_KIND,
|
|
2183
|
+
leaseIdentity: leaseIdentity(lease),
|
|
2184
|
+
markerPath,
|
|
2185
|
+
record,
|
|
2186
|
+
recordIdentity: recordIdentity(record),
|
|
2187
|
+
schemaVersion: INSTALL_ACTIVE_SCHEMA_VERSION,
|
|
2188
|
+
};
|
|
2189
|
+
ensureSafeDirectory(workspaceRoot, (0, node_path_1.dirname)(INSTALL_ACTIVE_PATH));
|
|
2190
|
+
writeExclusiveFile(activeJournalAbsolutePath(workspaceRoot), `${JSON.stringify(journal, null, 2)}\n`);
|
|
2191
|
+
}
|
|
2192
|
+
function clearActiveJournalAfterTransition(workspaceRoot, expected) {
|
|
2193
|
+
const journal = readActiveJournal(workspaceRoot);
|
|
2194
|
+
if (!journal)
|
|
2195
|
+
return;
|
|
2196
|
+
const exactRunning = journal.recordIdentity === recordIdentity(expected);
|
|
2197
|
+
const durableSuccessor = expected.revision === journal.record.revision + 1 &&
|
|
2198
|
+
expected.operation.fingerprint === journal.record.operation.fingerprint &&
|
|
2199
|
+
expected.execution.generation === journal.record.execution.generation &&
|
|
2200
|
+
expected.execution.ownerPid === null &&
|
|
2201
|
+
!['frozen-running', 'non-frozen-running'].includes(expected.execution.phase);
|
|
2202
|
+
if (!exactRunning && !durableSuccessor) {
|
|
2203
|
+
throw new Error('Post-generation install active journal is not bound to the transitioned record.');
|
|
2204
|
+
}
|
|
2205
|
+
(0, node_fs_1.unlinkSync)(activeJournalAbsolutePath(workspaceRoot));
|
|
2206
|
+
}
|
|
2207
|
+
function retainMissingActiveMarker(workspaceRoot, markerPath, record, lease) {
|
|
2208
|
+
assertLeaseOwned(workspaceRoot, lease, record.operation.fingerprint);
|
|
2209
|
+
const journal = readActiveJournal(workspaceRoot);
|
|
2210
|
+
if (!journal ||
|
|
2211
|
+
journal.markerPath !== markerPath ||
|
|
2212
|
+
journal.leaseIdentity !== leaseIdentity(lease) ||
|
|
2213
|
+
journal.recordIdentity !== recordIdentity(record)) {
|
|
2214
|
+
throw new Error('Post-generation install marker disappeared without an exact active journal.');
|
|
2215
|
+
}
|
|
2216
|
+
const reviewed = nextRecord(record, {
|
|
2217
|
+
blocker: 'active-marker-missing',
|
|
2218
|
+
classification: 'install-pending-reviewed-recovery-required',
|
|
2219
|
+
execution: {
|
|
2220
|
+
generation: lease.generation,
|
|
2221
|
+
ownerPid: null,
|
|
2222
|
+
phase: 'reviewed-recovery-required',
|
|
2223
|
+
},
|
|
2224
|
+
});
|
|
2225
|
+
ensureSafeDirectory(workspaceRoot, INSTALL_RECORD_DIRECTORY);
|
|
2226
|
+
writeExclusiveFile(confinedAbsolutePath(workspaceRoot, markerPath), serializeRecord(reviewed));
|
|
2227
|
+
assertLeaseOwned(workspaceRoot, lease, record.operation.fingerprint);
|
|
2228
|
+
clearActiveJournalAfterTransition(workspaceRoot, reviewed);
|
|
2229
|
+
const pending = diskPendingMarkerPaths(workspaceRoot);
|
|
2230
|
+
if (pending.length > 1)
|
|
2231
|
+
retainBatchConflict(workspaceRoot, pending);
|
|
2232
|
+
return reviewed;
|
|
2233
|
+
}
|
|
2234
|
+
function reconcileMissingActiveMarker(workspaceRoot, expectedMarkerPath, expectedOperationFingerprint) {
|
|
2235
|
+
const journal = readActiveJournal(workspaceRoot);
|
|
2236
|
+
if (!journal)
|
|
2237
|
+
return;
|
|
2238
|
+
if ((expectedMarkerPath && journal.markerPath !== expectedMarkerPath) ||
|
|
2239
|
+
(expectedOperationFingerprint &&
|
|
2240
|
+
journal.record.operation.fingerprint !== expectedOperationFingerprint)) {
|
|
2241
|
+
throw new Error('Post-generation install active journal belongs to a different generator request.');
|
|
2242
|
+
}
|
|
2243
|
+
const marker = confinedAbsolutePath(workspaceRoot, journal.markerPath);
|
|
2244
|
+
if ((0, node_fs_1.existsSync)(marker))
|
|
2245
|
+
return;
|
|
2246
|
+
const leasePath = confinedAbsolutePath(workspaceRoot, INSTALL_LEASE_PATH);
|
|
2247
|
+
const leases = [
|
|
2248
|
+
...staleLeaseClaims(workspaceRoot).map(({ lease }) => lease),
|
|
2249
|
+
...((0, node_fs_1.existsSync)(leasePath) ? [readDiskLease(workspaceRoot)] : []),
|
|
2250
|
+
];
|
|
2251
|
+
const activeLease = leases.find((lease) => leaseIdentity(lease) === journal.leaseIdentity);
|
|
2252
|
+
if (!activeLease) {
|
|
2253
|
+
throw new Error('Post-generation install active journal has no exact lease absence proof.');
|
|
2254
|
+
}
|
|
2255
|
+
if (!processIsAbsent(activeLease.ownerPid)) {
|
|
2256
|
+
throw new Error(`Post-generation install active journal is owned by live PID ${activeLease.ownerPid}; recovery was refused.`);
|
|
2257
|
+
}
|
|
2258
|
+
if (activeLease.generation !== journal.record.execution.generation ||
|
|
2259
|
+
activeLease.ownerPid !== journal.record.execution.ownerPid ||
|
|
2260
|
+
activeLease.operationFingerprint !== journal.record.operation.fingerprint) {
|
|
2261
|
+
throw new Error('Post-generation install active journal lease binding is invalid.');
|
|
2262
|
+
}
|
|
2263
|
+
const reviewed = nextRecord(journal.record, {
|
|
2264
|
+
blocker: 'active-marker-missing',
|
|
2265
|
+
classification: 'install-pending-reviewed-recovery-required',
|
|
2266
|
+
execution: {
|
|
2267
|
+
generation: activeLease.generation,
|
|
2268
|
+
ownerPid: null,
|
|
2269
|
+
phase: 'reviewed-recovery-required',
|
|
2270
|
+
},
|
|
2271
|
+
});
|
|
2272
|
+
ensureSafeDirectory(workspaceRoot, INSTALL_RECORD_DIRECTORY);
|
|
2273
|
+
writeExclusiveFile(marker, serializeRecord(reviewed));
|
|
2274
|
+
clearActiveJournalAfterTransition(workspaceRoot, reviewed);
|
|
2275
|
+
}
|
|
2276
|
+
function casWriteRecord(workspaceRoot, markerPath, expected, next, lease) {
|
|
2277
|
+
assertLeaseOwned(workspaceRoot, lease, expected.operation.fingerprint);
|
|
2278
|
+
if (next.revision !== expected.revision + 1 ||
|
|
2279
|
+
next.operation.fingerprint !== expected.operation.fingerprint ||
|
|
2280
|
+
next.execution.generation !== lease.generation ||
|
|
2281
|
+
!(next.execution.ownerPid === null ||
|
|
2282
|
+
next.execution.ownerPid === process.pid)) {
|
|
2283
|
+
throw new Error('Post-generation install record revision or lease generation is invalid.');
|
|
2284
|
+
}
|
|
2285
|
+
const current = readDiskRecord(workspaceRoot, markerPath);
|
|
2286
|
+
if (current.revision !== expected.revision ||
|
|
2287
|
+
recordIdentity(current) !== recordIdentity(expected)) {
|
|
2288
|
+
throw new Error('Post-generation install record changed concurrently; no attempt was started.');
|
|
2289
|
+
}
|
|
2290
|
+
const directory = ensureSafeDirectory(workspaceRoot, INSTALL_RECORD_DIRECTORY);
|
|
2291
|
+
const absolute = confinedAbsolutePath(workspaceRoot, markerPath);
|
|
2292
|
+
const temporary = (0, node_path_1.join)(directory, `.${operationKeyFromMarker(markerPath)}.tmp-${lease.generation}-${(0, node_crypto_1.randomUUID)()}`);
|
|
2293
|
+
writeExclusiveFile(temporary, serializeRecord(next));
|
|
2294
|
+
try {
|
|
2295
|
+
const beforeRename = readDiskRecord(workspaceRoot, markerPath);
|
|
2296
|
+
if (beforeRename.revision !== expected.revision ||
|
|
2297
|
+
recordIdentity(beforeRename) !== recordIdentity(expected)) {
|
|
2298
|
+
throw new Error('Post-generation install record changed during compare-and-swap.');
|
|
2299
|
+
}
|
|
2300
|
+
assertLeaseOwned(workspaceRoot, lease, expected.operation.fingerprint);
|
|
2301
|
+
(0, node_fs_1.renameSync)(temporary, absolute);
|
|
2302
|
+
}
|
|
2303
|
+
finally {
|
|
2304
|
+
if ((0, node_fs_1.existsSync)(temporary))
|
|
2305
|
+
(0, node_fs_1.unlinkSync)(temporary);
|
|
2306
|
+
}
|
|
2307
|
+
if (next.execution.phase !== 'frozen-running' &&
|
|
2308
|
+
next.execution.phase !== 'non-frozen-running') {
|
|
2309
|
+
clearActiveJournalAfterTransition(workspaceRoot, expected);
|
|
2310
|
+
}
|
|
2311
|
+
return next;
|
|
2312
|
+
}
|
|
2313
|
+
function operationKeyFromMarker(markerPath) {
|
|
2314
|
+
const filename = markerPath.slice(markerPath.lastIndexOf('/') + 1);
|
|
2315
|
+
return filename.endsWith('.json') ? filename.slice(0, -5) : filename;
|
|
2316
|
+
}
|
|
2317
|
+
function completionAbsolutePath(workspaceRoot) {
|
|
2318
|
+
return confinedAbsolutePath(workspaceRoot, INSTALL_COMPLETION_PATH);
|
|
2319
|
+
}
|
|
2320
|
+
function readCompletionRecord(workspaceRoot) {
|
|
2321
|
+
const absolute = completionAbsolutePath(workspaceRoot);
|
|
2322
|
+
assertSafeRegularFile(absolute, 'Post-generation install completion');
|
|
2323
|
+
const record = parseRecord((0, node_fs_1.readFileSync)(absolute, 'utf8'));
|
|
2324
|
+
if (record.classification !== 'generated-source-complete-install-pending' ||
|
|
2325
|
+
record.blocker !== undefined ||
|
|
2326
|
+
!validGeneration(record.execution.generation) ||
|
|
2327
|
+
record.execution.ownerPid !== null ||
|
|
2328
|
+
record.execution.phase !== 'complete-checkpoint') {
|
|
2329
|
+
throw new Error('Post-generation install completion tombstone is not an exact completed checkpoint.');
|
|
2330
|
+
}
|
|
2331
|
+
return record;
|
|
2332
|
+
}
|
|
2333
|
+
function casRetainCompletion(workspaceRoot, markerPath, expected, lease) {
|
|
2334
|
+
assertLeaseOwned(workspaceRoot, lease, expected.operation.fingerprint);
|
|
2335
|
+
const current = readDiskRecord(workspaceRoot, markerPath);
|
|
2336
|
+
if (expected.execution.generation !== lease.generation ||
|
|
2337
|
+
expected.execution.ownerPid !== null ||
|
|
2338
|
+
current.revision !== expected.revision ||
|
|
2339
|
+
recordIdentity(current) !== recordIdentity(expected)) {
|
|
2340
|
+
throw new Error('Post-generation install record changed before completion removal.');
|
|
2341
|
+
}
|
|
2342
|
+
const completion = completionAbsolutePath(workspaceRoot);
|
|
2343
|
+
if ((0, node_fs_1.existsSync)(completion)) {
|
|
2344
|
+
throw new Error('Post-generation install completion tombstone already exists.');
|
|
2345
|
+
}
|
|
2346
|
+
(0, node_fs_1.renameSync)(confinedAbsolutePath(workspaceRoot, markerPath), completion);
|
|
2347
|
+
const directory = confinedAbsolutePath(workspaceRoot, INSTALL_RECORD_DIRECTORY);
|
|
2348
|
+
if ((0, node_fs_1.existsSync)(directory) && (0, node_fs_1.readdirSync)(directory).length === 0) {
|
|
2349
|
+
(0, node_fs_1.rmdirSync)(directory);
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
function clearCompletionTombstone(workspaceRoot, expected) {
|
|
2353
|
+
const absolute = completionAbsolutePath(workspaceRoot);
|
|
2354
|
+
if (!(0, node_fs_1.existsSync)(absolute))
|
|
2355
|
+
return;
|
|
2356
|
+
const current = readCompletionRecord(workspaceRoot);
|
|
2357
|
+
if (recordIdentity(current) !== recordIdentity(expected)) {
|
|
2358
|
+
throw new Error('Post-generation install completion tombstone changed before cleanup.');
|
|
2359
|
+
}
|
|
2360
|
+
(0, node_fs_1.unlinkSync)(absolute);
|
|
2361
|
+
}
|
|
2362
|
+
function reconcileCompletionTombstone(workspaceRoot) {
|
|
2363
|
+
const absolute = completionAbsolutePath(workspaceRoot);
|
|
2364
|
+
if (!(0, node_fs_1.existsSync)(absolute))
|
|
2365
|
+
return;
|
|
2366
|
+
const completion = readCompletionRecord(workspaceRoot);
|
|
2367
|
+
const pending = diskPendingMarkerPaths(workspaceRoot);
|
|
2368
|
+
if (pending.length > 0) {
|
|
2369
|
+
throw new Error(`Post-generation install completion tombstone coexists with incomplete record ${pending[0]}; reviewed reconciliation is required.`);
|
|
2370
|
+
}
|
|
2371
|
+
if (!semanticPrerequisitesMatch(workspaceRoot, completion)) {
|
|
2372
|
+
throw new Error('Post-generation install completion tombstone is blocked by semantic-prerequisite-drift.');
|
|
2373
|
+
}
|
|
2374
|
+
if (!installAuthorityMatches(workspaceRoot, completion.installAuthority)) {
|
|
2375
|
+
throw new Error('Post-generation install completion tombstone is blocked by install-authority-drift.');
|
|
2376
|
+
}
|
|
2377
|
+
if (!plannedSurfaceMatches(workspaceRoot, completion) ||
|
|
2378
|
+
lockfileDigest(workspaceRoot) !== completion.lockfile.currentDigest) {
|
|
2379
|
+
throw new Error('Post-generation install completion tombstone no longer matches its completed source and lockfile.');
|
|
2380
|
+
}
|
|
2381
|
+
const leasePath = confinedAbsolutePath(workspaceRoot, INSTALL_LEASE_PATH);
|
|
2382
|
+
const claims = staleLeaseClaims(workspaceRoot);
|
|
2383
|
+
const canonical = (0, node_fs_1.existsSync)(leasePath)
|
|
2384
|
+
? readDiskLease(workspaceRoot)
|
|
2385
|
+
: undefined;
|
|
2386
|
+
const leases = [
|
|
2387
|
+
...claims.map(({ lease }) => lease),
|
|
2388
|
+
...(canonical ? [canonical] : []),
|
|
2389
|
+
];
|
|
2390
|
+
for (const lease of leases) {
|
|
2391
|
+
if (lease.operationFingerprint !== completion.operation.fingerprint ||
|
|
2392
|
+
lease.generation !== completion.execution.generation) {
|
|
2393
|
+
throw new Error('Post-generation install completion tombstone is paired with a different lease authority.');
|
|
2394
|
+
}
|
|
2395
|
+
if (!processIsAbsent(lease.ownerPid)) {
|
|
2396
|
+
throw new Error(`Post-generation install completion lease is active for PID ${lease.ownerPid}; cleanup was refused.`);
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
if (canonical) {
|
|
2400
|
+
const claimPath = `${leasePath}.stale-${canonical.generation}-${(0, node_crypto_1.randomUUID)()}`;
|
|
2401
|
+
(0, node_fs_1.renameSync)(leasePath, claimPath);
|
|
2402
|
+
assertSafeRegularFile(claimPath, 'Claimed completed post-generation install lease');
|
|
2403
|
+
const claimed = parseLease((0, node_fs_1.readFileSync)(claimPath, 'utf8'));
|
|
2404
|
+
if (leaseIdentity(claimed) !== leaseIdentity(canonical)) {
|
|
2405
|
+
throw new Error('Post-generation install completion lease changed during atomic claim.');
|
|
2406
|
+
}
|
|
2407
|
+
claims.push({ lease: claimed, path: claimPath });
|
|
2408
|
+
}
|
|
2409
|
+
for (const claim of claims) {
|
|
2410
|
+
assertSafeRegularFile(claim.path, 'Claimed completed post-generation install lease');
|
|
2411
|
+
const current = parseLease((0, node_fs_1.readFileSync)(claim.path, 'utf8'));
|
|
2412
|
+
if (leaseIdentity(current) !== leaseIdentity(claim.lease)) {
|
|
2413
|
+
throw new Error('Post-generation install completion lease claim changed before cleanup.');
|
|
2414
|
+
}
|
|
2415
|
+
(0, node_fs_1.unlinkSync)(claim.path);
|
|
2416
|
+
}
|
|
2417
|
+
clearCompletionTombstone(workspaceRoot, completion);
|
|
2418
|
+
}
|
|
2419
|
+
function lockfileDigest(workspaceRoot) {
|
|
2420
|
+
const absolute = confinedAbsolutePath(workspaceRoot, LOCKFILE_PATH);
|
|
2421
|
+
if (!(0, node_fs_1.existsSync)(absolute))
|
|
2422
|
+
return undefined;
|
|
2423
|
+
assertSafeRegularFile(absolute, 'Post-generation install lockfile');
|
|
2424
|
+
const source = (0, node_fs_1.readFileSync)(absolute);
|
|
2425
|
+
const reader = diskInstallAuthorityReader(workspaceRoot);
|
|
2426
|
+
const workspaceSource = reader.read('pnpm-workspace.yaml');
|
|
2427
|
+
const workspaceManifestPaths = workspaceSource
|
|
2428
|
+
? new Set(exactWorkspaceManifestPaths(reader, parseBoundedYaml(workspaceSource, 'Post-generation install pnpm-workspace.yaml')))
|
|
2429
|
+
: new Set();
|
|
2430
|
+
assertNoExoticLockfileAuthority(source, reader, workspaceManifestPaths);
|
|
2431
|
+
return sha256(source);
|
|
2432
|
+
}
|
|
2433
|
+
function plannedSurfaceMatches(workspaceRoot, record) {
|
|
2434
|
+
const root = workspaceRealRoot(workspaceRoot);
|
|
2435
|
+
return record.plannedSurface.files.every((file) => {
|
|
2436
|
+
const absolute = (0, node_path_1.resolve)(root, file.path);
|
|
2437
|
+
try {
|
|
2438
|
+
const fromRoot = (0, node_path_1.relative)(root, absolute);
|
|
2439
|
+
if (fromRoot === '..' || fromRoot.startsWith(`..${node_path_1.sep}`))
|
|
2440
|
+
return false;
|
|
2441
|
+
assertSafeRegularFile(absolute, 'Install-planned source');
|
|
2442
|
+
return sha256((0, node_fs_1.readFileSync)(absolute)) === file.afterDigest;
|
|
2443
|
+
}
|
|
2444
|
+
catch {
|
|
2445
|
+
return false;
|
|
2446
|
+
}
|
|
2447
|
+
});
|
|
2448
|
+
}
|
|
2449
|
+
function nextRecord(record, changes) {
|
|
2450
|
+
return {
|
|
2451
|
+
...record,
|
|
2452
|
+
...changes,
|
|
2453
|
+
revision: record.revision + 1,
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
function checkpoint(workspaceRoot, markerPath, record, lease, changes) {
|
|
2457
|
+
return casWriteRecord(workspaceRoot, markerPath, record, nextRecord(record, changes), lease);
|
|
2458
|
+
}
|
|
2459
|
+
function blockedRecord(workspaceRoot, markerPath, record, lease, blocker, execution = {
|
|
2460
|
+
generation: lease.generation,
|
|
2461
|
+
ownerPid: null,
|
|
2462
|
+
phase: record.execution.phase === 'frozen-running'
|
|
2463
|
+
? 'frozen-checkpoint'
|
|
2464
|
+
: record.execution.phase === 'non-frozen-running'
|
|
2465
|
+
? 'non-frozen-checkpoint'
|
|
2466
|
+
: record.execution.phase,
|
|
2467
|
+
}) {
|
|
2468
|
+
return checkpoint(workspaceRoot, markerPath, record, lease, {
|
|
2469
|
+
blocker,
|
|
2470
|
+
classification: blocker === 'semantic-prerequisite-drift'
|
|
2471
|
+
? 'install-pending-semantic-prerequisite-blocked'
|
|
2472
|
+
: 'install-pending-source-integrity-blocked',
|
|
2473
|
+
execution,
|
|
2474
|
+
});
|
|
2475
|
+
}
|
|
2476
|
+
function reviewedRecord(workspaceRoot, markerPath, record, lease, blocker) {
|
|
2477
|
+
return checkpoint(workspaceRoot, markerPath, record, lease, {
|
|
2478
|
+
blocker,
|
|
2479
|
+
classification: 'install-pending-reviewed-recovery-required',
|
|
2480
|
+
execution: {
|
|
2481
|
+
generation: lease.generation,
|
|
2482
|
+
ownerPid: null,
|
|
2483
|
+
phase: 'reviewed-recovery-required',
|
|
2484
|
+
},
|
|
2485
|
+
});
|
|
2486
|
+
}
|
|
2487
|
+
function pendingRecord(workspaceRoot, markerPath, record, lease, execution, lockfile = record.lockfile) {
|
|
2488
|
+
return checkpoint(workspaceRoot, markerPath, record, lease, {
|
|
2489
|
+
blocker: undefined,
|
|
2490
|
+
classification: 'generated-source-complete-install-pending',
|
|
2491
|
+
execution,
|
|
2492
|
+
lockfile,
|
|
2493
|
+
});
|
|
2494
|
+
}
|
|
2495
|
+
function diskPnpmPackageManagerAuthority(workspaceRoot) {
|
|
2496
|
+
const packagePath = confinedAbsolutePath(workspaceRoot, 'package.json');
|
|
2497
|
+
if (!(0, node_fs_1.existsSync)(packagePath)) {
|
|
2498
|
+
throw new Error('Post-generation install requires a root package.json with an exact pnpm packageManager authority.');
|
|
2499
|
+
}
|
|
2500
|
+
assertSafeRegularFile(packagePath, 'Post-generation install package.json');
|
|
2501
|
+
return (0, pnpm_toolchain_authority_js_1.parsePnpmPackageManagerAuthority)((0, node_fs_1.readFileSync)(packagePath, 'utf8'));
|
|
2502
|
+
}
|
|
2503
|
+
function pathIsInsideWorkspace(workspaceRoot, candidate) {
|
|
2504
|
+
const fromWorkspace = (0, node_path_1.relative)((0, node_path_1.resolve)(workspaceRoot), candidate);
|
|
2505
|
+
return (fromWorkspace === '' ||
|
|
2506
|
+
(fromWorkspace !== '..' &&
|
|
2507
|
+
!fromWorkspace.startsWith(`..${node_path_1.sep}`) &&
|
|
2508
|
+
!(0, node_path_1.isAbsolute)(fromWorkspace)));
|
|
2509
|
+
}
|
|
2510
|
+
function postGenerationInstallPnpmInvocation(args, options) {
|
|
2511
|
+
if (args.length === 0 ||
|
|
2512
|
+
args.length > 32 ||
|
|
2513
|
+
args.some((argument) => argument.length === 0 ||
|
|
2514
|
+
argument.length > 256 ||
|
|
2515
|
+
/[\0\r\n]/u.test(argument))) {
|
|
2516
|
+
throw new Error('Post-generation install pnpm arguments are unsafe for controlled execution.');
|
|
2517
|
+
}
|
|
2518
|
+
if (!DIGEST_PATTERN.test(options.toolchain.digest) ||
|
|
2519
|
+
!DIGEST_PATTERN.test(options.toolchain.node.digest) ||
|
|
2520
|
+
!(0, node_path_1.isAbsolute)(options.toolchain.node.path) ||
|
|
2521
|
+
/[\0\r\n"]/u.test(options.toolchain.node.path) ||
|
|
2522
|
+
!(0, node_path_1.isAbsolute)(options.toolchain.entryPoint) ||
|
|
2523
|
+
/[\0\r\n"]/u.test(options.toolchain.entryPoint) ||
|
|
2524
|
+
!(0, node_path_1.isAbsolute)(options.isolatedHome) ||
|
|
2525
|
+
/[\0\r\n"]/u.test(options.isolatedHome)) {
|
|
2526
|
+
throw new Error('Post-generation install pnpm execution authority is invalid.');
|
|
2527
|
+
}
|
|
2528
|
+
const userConfig = (0, node_path_1.join)(options.isolatedHome, 'npm-user.rc');
|
|
2529
|
+
const globalConfig = (0, node_path_1.join)(options.isolatedHome, 'npm-global.rc');
|
|
2530
|
+
if ((0, node_fs_1.existsSync)(userConfig) || (0, node_fs_1.existsSync)(globalConfig)) {
|
|
2531
|
+
throw new Error('Post-generation install isolated npm configuration paths must remain absent.');
|
|
2532
|
+
}
|
|
2533
|
+
for (const directory of [
|
|
2534
|
+
'appdata',
|
|
2535
|
+
'cache',
|
|
2536
|
+
'config',
|
|
2537
|
+
'data',
|
|
2538
|
+
'localappdata',
|
|
2539
|
+
'state',
|
|
2540
|
+
]) {
|
|
2541
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.join)(options.isolatedHome, directory), { recursive: true });
|
|
2542
|
+
}
|
|
2543
|
+
const inheritedEnvironment = options.environment ?? process.env;
|
|
2544
|
+
const systemRoot = inheritedEnvironment.SystemRoot ?? inheritedEnvironment.SYSTEMROOT;
|
|
2545
|
+
if (process.platform === 'win32' &&
|
|
2546
|
+
(typeof systemRoot !== 'string' ||
|
|
2547
|
+
!(0, node_path_1.isAbsolute)(systemRoot) ||
|
|
2548
|
+
systemRoot.includes(node_path_1.delimiter) ||
|
|
2549
|
+
/[\0\r\n"%]/u.test(systemRoot))) {
|
|
2550
|
+
throw new Error('Post-generation install requires one controlled absolute Windows SystemRoot.');
|
|
2551
|
+
}
|
|
2552
|
+
const controlledPath = [
|
|
2553
|
+
(0, node_path_1.dirname)(options.toolchain.node.path),
|
|
2554
|
+
...(process.platform === 'win32' && systemRoot
|
|
2555
|
+
? [(0, node_path_1.join)(systemRoot, 'System32')]
|
|
2556
|
+
: []),
|
|
2557
|
+
].join(node_path_1.delimiter);
|
|
2558
|
+
const environment = {
|
|
2559
|
+
...Object.fromEntries(Object.entries(inheritedEnvironment).filter(([key]) => /^EBK_/u.test(key) ||
|
|
2560
|
+
/^LC_/u.test(key) ||
|
|
2561
|
+
ALLOWED_NPM_CREDENTIAL_ENVIRONMENT.has(key) ||
|
|
2562
|
+
/^(?:CI|COMSPEC|FORCE_COLOR|HTTP_PROXY|HTTPS_PROXY|LANG|NO_COLOR|NO_PROXY|PATHEXT|SYSTEMROOT|TEMP|TMP|TMPDIR|WINDIR)$/iu.test(key))),
|
|
2563
|
+
APPDATA: (0, node_path_1.join)(options.isolatedHome, 'appdata'),
|
|
2564
|
+
COREPACK_ENV_FILE: '0',
|
|
2565
|
+
HOME: options.isolatedHome,
|
|
2566
|
+
LOCALAPPDATA: (0, node_path_1.join)(options.isolatedHome, 'localappdata'),
|
|
2567
|
+
PATH: controlledPath,
|
|
2568
|
+
USERPROFILE: options.isolatedHome,
|
|
2569
|
+
XDG_CACHE_HOME: (0, node_path_1.join)(options.isolatedHome, 'cache'),
|
|
2570
|
+
XDG_CONFIG_HOME: (0, node_path_1.join)(options.isolatedHome, 'config'),
|
|
2571
|
+
XDG_DATA_HOME: (0, node_path_1.join)(options.isolatedHome, 'data'),
|
|
2572
|
+
XDG_STATE_HOME: (0, node_path_1.join)(options.isolatedHome, 'state'),
|
|
2573
|
+
npm_config_globalconfig: globalConfig,
|
|
2574
|
+
npm_config_userconfig: userConfig,
|
|
2575
|
+
};
|
|
2576
|
+
return {
|
|
2577
|
+
args: [options.toolchain.entryPoint, ...args],
|
|
2578
|
+
command: options.toolchain.node.path,
|
|
2579
|
+
env: environment,
|
|
2580
|
+
};
|
|
2581
|
+
}
|
|
2582
|
+
function withIsolatedPnpmHome(workspaceRoot, callback) {
|
|
2583
|
+
const isolatedHome = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'ebk-post-generation-pnpm-'));
|
|
2584
|
+
(0, node_fs_1.chmodSync)(isolatedHome, 0o700);
|
|
2585
|
+
if (pathIsInsideWorkspace(workspaceRoot, isolatedHome)) {
|
|
2586
|
+
(0, node_fs_1.rmSync)(isolatedHome, { force: true, recursive: true });
|
|
2587
|
+
throw new Error('Post-generation install isolated pnpm home must be outside the workspace.');
|
|
2588
|
+
}
|
|
2589
|
+
let retainForAmbiguousTermination = false;
|
|
2590
|
+
try {
|
|
2591
|
+
return callback(isolatedHome);
|
|
2592
|
+
}
|
|
2593
|
+
catch (error) {
|
|
2594
|
+
retainForAmbiguousTermination = installExecutionIsAmbiguous(error);
|
|
2595
|
+
throw error;
|
|
2596
|
+
}
|
|
2597
|
+
finally {
|
|
2598
|
+
if (!retainForAmbiguousTermination) {
|
|
2599
|
+
(0, node_fs_1.rmSync)(isolatedHome, { force: true, recursive: true });
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
function assertPinnedPnpmAuthority(workspaceRoot, expected) {
|
|
2604
|
+
const current = diskPnpmPackageManagerAuthority(workspaceRoot);
|
|
2605
|
+
if (current.packageManager !== expected.packageManager ||
|
|
2606
|
+
current.version !== expected.version) {
|
|
2607
|
+
throw new Error('Post-generation install root packageManager changed from its retained planning authority.');
|
|
2608
|
+
}
|
|
2609
|
+
let observed;
|
|
2610
|
+
try {
|
|
2611
|
+
const toolchain = (0, pnpm_toolchain_authority_js_1.assertBoundPnpmToolchain)(current, expected.toolchain);
|
|
2612
|
+
observed = withIsolatedPnpmHome(workspaceRoot, (isolatedHome) => {
|
|
2613
|
+
const invocation = postGenerationInstallPnpmInvocation(['--version'], {
|
|
2614
|
+
isolatedHome,
|
|
2615
|
+
toolchain,
|
|
2616
|
+
});
|
|
2617
|
+
return (0, node_child_process_1.execFileSync)(invocation.command, invocation.args, {
|
|
2618
|
+
cwd: workspaceRoot,
|
|
2619
|
+
encoding: 'utf8',
|
|
2620
|
+
env: invocation.env,
|
|
2621
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
2622
|
+
timeout: INSTALL_TIMEOUT_MS,
|
|
2623
|
+
windowsHide: true,
|
|
2624
|
+
}).trim();
|
|
2625
|
+
});
|
|
2626
|
+
}
|
|
2627
|
+
catch (error) {
|
|
2628
|
+
throw new Error(`Post-generation install could not verify the bound pnpm authority: ${error instanceof Error ? error.message : String(error)}`);
|
|
2629
|
+
}
|
|
2630
|
+
if (observed !== expected.version) {
|
|
2631
|
+
throw new Error(`Post-generation install requires pnpm ${expected.version}, but the bound toolchain reported ${observed || 'no version'}.`);
|
|
2632
|
+
}
|
|
2633
|
+
return expected.version;
|
|
2634
|
+
}
|
|
2635
|
+
function packageManagerAuthorityMatches(workspaceRoot, expected) {
|
|
2636
|
+
try {
|
|
2637
|
+
const current = diskPnpmPackageManagerAuthority(workspaceRoot);
|
|
2638
|
+
return (current.packageManager === expected.packageManager &&
|
|
2639
|
+
current.version === expected.version &&
|
|
2640
|
+
(0, canonical_json_1.canonicalJson)((0, pnpm_toolchain_authority_js_1.assertBoundPnpmToolchain)(current, expected.toolchain)) ===
|
|
2641
|
+
(0, canonical_json_1.canonicalJson)(expected.toolchain));
|
|
2642
|
+
}
|
|
2643
|
+
catch {
|
|
2644
|
+
return false;
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
function installExecutionIsAmbiguous(error) {
|
|
2648
|
+
if (!isRecord(error))
|
|
2649
|
+
return false;
|
|
2650
|
+
return (error.killed === true ||
|
|
2651
|
+
typeof error.signal === 'string' ||
|
|
2652
|
+
error.code === 'ETIMEDOUT' ||
|
|
2653
|
+
(typeof error.code === 'string' &&
|
|
2654
|
+
(error.status === null || error.status === undefined)));
|
|
2655
|
+
}
|
|
2656
|
+
function defaultInstall(workspaceRoot, frozen, packageManager) {
|
|
2657
|
+
const toolchain = (0, pnpm_toolchain_authority_js_1.assertBoundPnpmToolchain)(packageManager, packageManager.toolchain);
|
|
2658
|
+
// HOME, configuration, cache, and execution state remain ephemeral. The
|
|
2659
|
+
// content-addressed store is deliberately retained under the ignored
|
|
2660
|
+
// workspace cache so ordinary pinned-pnpm commands can safely continue from
|
|
2661
|
+
// the generated install instead of inheriting a deleted store authority.
|
|
2662
|
+
ensureSafeDirectory(workspaceRoot, POST_GENERATION_STORE_DIRECTORY);
|
|
2663
|
+
withIsolatedPnpmHome(workspaceRoot, (isolatedHome) => {
|
|
2664
|
+
const invocation = postGenerationInstallPnpmInvocation([
|
|
2665
|
+
'install',
|
|
2666
|
+
frozen ? '--frozen-lockfile' : '--no-frozen-lockfile',
|
|
2667
|
+
'--config.block-exotic-subdeps=true',
|
|
2668
|
+
`--config.cache-dir=${(0, node_path_1.join)(isolatedHome, 'cache')}`,
|
|
2669
|
+
'--config.deploy-all-files=false',
|
|
2670
|
+
'--config.enable-modules-dir=true',
|
|
2671
|
+
'--config.enable-global-virtual-store=false',
|
|
2672
|
+
'--config.git-branch-lockfile=false',
|
|
2673
|
+
'--config.ignore-pnpmfile=true',
|
|
2674
|
+
'--config.ignore-scripts=true',
|
|
2675
|
+
'--config.ignore-workspace=false',
|
|
2676
|
+
'--config.inject-workspace-packages=false',
|
|
2677
|
+
'--config.lockfile=true',
|
|
2678
|
+
'--config.lockfile-only=false',
|
|
2679
|
+
'--config.merge-git-branch-lockfiles=false',
|
|
2680
|
+
'--config.modules-dir=node_modules',
|
|
2681
|
+
'--config.node-linker=isolated',
|
|
2682
|
+
'--config.package-import-method=copy',
|
|
2683
|
+
'--config.resolution-only=false',
|
|
2684
|
+
'--config.shared-workspace-lockfile=true',
|
|
2685
|
+
'--config.side-effects-cache=false',
|
|
2686
|
+
'--config.side-effects-cache-readonly=false',
|
|
2687
|
+
`--config.state-dir=${(0, node_path_1.join)(isolatedHome, 'state')}`,
|
|
2688
|
+
`--config.store-dir=${POST_GENERATION_STORE_DIRECTORY}`,
|
|
2689
|
+
'--config.symlink=true',
|
|
2690
|
+
'--config.use-store-server=false',
|
|
2691
|
+
'--config.verify-store-integrity=true',
|
|
2692
|
+
], { isolatedHome, toolchain });
|
|
2693
|
+
(0, node_child_process_1.execFileSync)(invocation.command, invocation.args, {
|
|
2694
|
+
cwd: workspaceRoot,
|
|
2695
|
+
env: invocation.env,
|
|
2696
|
+
stdio: process.env.NX_GENERATE_QUIET === 'true' ? 'ignore' : 'inherit',
|
|
2697
|
+
timeout: INSTALL_TIMEOUT_MS,
|
|
2698
|
+
windowsHide: true,
|
|
2699
|
+
});
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2702
|
+
function withAttemptEnvironment(lease, phase, callback) {
|
|
2703
|
+
const keys = {
|
|
2704
|
+
EBK_POST_GENERATION_INSTALL_GENERATION: lease.generation,
|
|
2705
|
+
EBK_POST_GENERATION_INSTALL_OWNER_PID: String(process.pid),
|
|
2706
|
+
EBK_POST_GENERATION_INSTALL_PHASE: phase,
|
|
2707
|
+
};
|
|
2708
|
+
const previous = Object.fromEntries(Object.keys(keys).map((key) => [key, process.env[key]]));
|
|
2709
|
+
Object.assign(process.env, keys);
|
|
2710
|
+
try {
|
|
2711
|
+
return callback();
|
|
2712
|
+
}
|
|
2713
|
+
finally {
|
|
2714
|
+
for (const [key, value] of Object.entries(previous)) {
|
|
2715
|
+
if (value === undefined)
|
|
2716
|
+
delete process.env[key];
|
|
2717
|
+
else
|
|
2718
|
+
process.env[key] = value;
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
function matchingStaleLease(record, acquired) {
|
|
2723
|
+
return acquired.staleLeases.find((lease) => lease.generation === record.execution.generation &&
|
|
2724
|
+
lease.ownerPid === record.execution.ownerPid &&
|
|
2725
|
+
lease.operationFingerprint === record.operation.fingerprint);
|
|
2726
|
+
}
|
|
2727
|
+
function expectedCheckpointLock(record) {
|
|
2728
|
+
return record.execution.phase === 'frozen-checkpoint' ||
|
|
2729
|
+
record.execution.phase === 'frozen-running' ||
|
|
2730
|
+
(record.execution.phase === 'reviewed-recovery-required' &&
|
|
2731
|
+
record.lockfile.candidateDigest)
|
|
2732
|
+
? record.lockfile.candidateDigest
|
|
2733
|
+
: record.lockfile.currentDigest;
|
|
2734
|
+
}
|
|
2735
|
+
function recoverOrRebindRecord(workspaceRoot, markerPath, record, acquired) {
|
|
2736
|
+
const running = record.execution.phase === 'non-frozen-running' ||
|
|
2737
|
+
record.execution.phase === 'frozen-running';
|
|
2738
|
+
if (!running &&
|
|
2739
|
+
(record.execution.generation === null ||
|
|
2740
|
+
record.execution.generation === acquired.lease.generation)) {
|
|
2741
|
+
return record;
|
|
2742
|
+
}
|
|
2743
|
+
if (running) {
|
|
2744
|
+
const stale = matchingStaleLease(record, acquired);
|
|
2745
|
+
if (!stale || !processIsAbsent(stale.ownerPid)) {
|
|
2746
|
+
throw new Error('Post-generation install interrupted attempt has no matching stale lease absence proof.');
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
else if (record.execution.ownerPid !== null) {
|
|
2750
|
+
throw new Error('Post-generation install checkpoint retained an invalid active owner.');
|
|
2751
|
+
}
|
|
2752
|
+
if (record.execution.phase === 'reviewed-recovery-required' ||
|
|
2753
|
+
record.classification === 'install-pending-reviewed-recovery-required') {
|
|
2754
|
+
return checkpoint(workspaceRoot, markerPath, record, acquired.lease, {
|
|
2755
|
+
execution: {
|
|
2756
|
+
generation: acquired.lease.generation,
|
|
2757
|
+
ownerPid: null,
|
|
2758
|
+
phase: 'reviewed-recovery-required',
|
|
2759
|
+
},
|
|
2760
|
+
});
|
|
2761
|
+
}
|
|
2762
|
+
if (!plannedSurfaceMatches(workspaceRoot, record)) {
|
|
2763
|
+
return blockedRecord(workspaceRoot, markerPath, record, acquired.lease, 'planned-surface-drift');
|
|
2764
|
+
}
|
|
2765
|
+
if (!semanticPrerequisitesMatch(workspaceRoot, record)) {
|
|
2766
|
+
return blockedRecord(workspaceRoot, markerPath, record, acquired.lease, 'semantic-prerequisite-drift');
|
|
2767
|
+
}
|
|
2768
|
+
if (!installAuthorityMatches(workspaceRoot, record.installAuthority)) {
|
|
2769
|
+
return blockedRecord(workspaceRoot, markerPath, record, acquired.lease, 'install-authority-drift');
|
|
2770
|
+
}
|
|
2771
|
+
const observedLock = lockfileDigest(workspaceRoot);
|
|
2772
|
+
if (!observedLock) {
|
|
2773
|
+
return blockedRecord(workspaceRoot, markerPath, record, acquired.lease, 'lockfile-missing');
|
|
2774
|
+
}
|
|
2775
|
+
if (record.execution.phase === 'non-frozen-running' &&
|
|
2776
|
+
observedLock !== record.lockfile.currentDigest) {
|
|
2777
|
+
// A dead owner never gets to attest the lock bytes it produced. Even a
|
|
2778
|
+
// syntactically valid lockfile can have been substituted after the crash,
|
|
2779
|
+
// so recovery requires restoration of the pre-attempt bytes followed by a
|
|
2780
|
+
// fresh, leased non-frozen convergence.
|
|
2781
|
+
return blockedRecord(workspaceRoot, markerPath, record, acquired.lease, 'lockfile-drift', {
|
|
2782
|
+
generation: acquired.lease.generation,
|
|
2783
|
+
ownerPid: null,
|
|
2784
|
+
phase: 'non-frozen-checkpoint',
|
|
2785
|
+
});
|
|
2786
|
+
}
|
|
2787
|
+
const expectedLock = expectedCheckpointLock(record);
|
|
2788
|
+
if (!expectedLock || observedLock !== expectedLock) {
|
|
2789
|
+
return blockedRecord(workspaceRoot, markerPath, record, acquired.lease, 'lockfile-drift', {
|
|
2790
|
+
generation: acquired.lease.generation,
|
|
2791
|
+
ownerPid: null,
|
|
2792
|
+
phase: record.execution.phase === 'frozen-running'
|
|
2793
|
+
? 'frozen-checkpoint'
|
|
2794
|
+
: record.execution.phase === 'non-frozen-running'
|
|
2795
|
+
? 'non-frozen-checkpoint'
|
|
2796
|
+
: record.execution.phase,
|
|
2797
|
+
});
|
|
2798
|
+
}
|
|
2799
|
+
return pendingRecord(workspaceRoot, markerPath, record, acquired.lease, {
|
|
2800
|
+
generation: acquired.lease.generation,
|
|
2801
|
+
ownerPid: null,
|
|
2802
|
+
phase: record.execution.phase === 'frozen-running'
|
|
2803
|
+
? 'frozen-checkpoint'
|
|
2804
|
+
: record.execution.phase === 'non-frozen-running'
|
|
2805
|
+
? 'non-frozen-checkpoint'
|
|
2806
|
+
: record.execution.phase,
|
|
2807
|
+
});
|
|
2808
|
+
}
|
|
2809
|
+
function requireStableIntegrity(workspaceRoot, markerPath, record, lease) {
|
|
2810
|
+
if (!plannedSurfaceMatches(workspaceRoot, record)) {
|
|
2811
|
+
return blockedRecord(workspaceRoot, markerPath, record, lease, 'planned-surface-drift');
|
|
2812
|
+
}
|
|
2813
|
+
if (!semanticPrerequisitesMatch(workspaceRoot, record)) {
|
|
2814
|
+
return blockedRecord(workspaceRoot, markerPath, record, lease, 'semantic-prerequisite-drift');
|
|
2815
|
+
}
|
|
2816
|
+
if (!installAuthorityMatches(workspaceRoot, record.installAuthority)) {
|
|
2817
|
+
return blockedRecord(workspaceRoot, markerPath, record, lease, 'install-authority-drift');
|
|
2818
|
+
}
|
|
2819
|
+
const observedLock = lockfileDigest(workspaceRoot);
|
|
2820
|
+
if (!observedLock) {
|
|
2821
|
+
return blockedRecord(workspaceRoot, markerPath, record, lease, 'lockfile-missing');
|
|
2822
|
+
}
|
|
2823
|
+
const expectedLock = record.execution.phase === 'frozen-checkpoint'
|
|
2824
|
+
? record.lockfile.candidateDigest
|
|
2825
|
+
: record.lockfile.currentDigest;
|
|
2826
|
+
if (!expectedLock || observedLock !== expectedLock) {
|
|
2827
|
+
return blockedRecord(workspaceRoot, markerPath, record, lease, 'lockfile-drift');
|
|
2828
|
+
}
|
|
2829
|
+
if (record.classification === 'install-pending-source-integrity-blocked' ||
|
|
2830
|
+
record.classification === 'install-pending-semantic-prerequisite-blocked') {
|
|
2831
|
+
return pendingRecord(workspaceRoot, markerPath, record, lease, {
|
|
2832
|
+
generation: lease.generation,
|
|
2833
|
+
ownerPid: null,
|
|
2834
|
+
phase: record.execution.phase,
|
|
2835
|
+
});
|
|
2836
|
+
}
|
|
2837
|
+
return record;
|
|
2838
|
+
}
|
|
2839
|
+
function completeCheckpoint(workspaceRoot, markerPath, record, lease, currentDigest) {
|
|
2840
|
+
return checkpoint(workspaceRoot, markerPath, record, lease, {
|
|
2841
|
+
blocker: undefined,
|
|
2842
|
+
classification: 'generated-source-complete-install-pending',
|
|
2843
|
+
execution: {
|
|
2844
|
+
generation: lease.generation,
|
|
2845
|
+
ownerPid: null,
|
|
2846
|
+
phase: 'complete-checkpoint',
|
|
2847
|
+
},
|
|
2848
|
+
lockfile: {
|
|
2849
|
+
beforeDigest: record.lockfile.beforeDigest,
|
|
2850
|
+
currentDigest,
|
|
2851
|
+
path: LOCKFILE_PATH,
|
|
2852
|
+
},
|
|
2853
|
+
});
|
|
2854
|
+
}
|
|
2855
|
+
function runFrozenAttempt(workspaceRoot, markerPath, record, lease, dependencies) {
|
|
2856
|
+
record = requireStableIntegrity(workspaceRoot, markerPath, record, lease);
|
|
2857
|
+
if (record.classification === 'install-pending-source-integrity-blocked' ||
|
|
2858
|
+
record.classification === 'install-pending-semantic-prerequisite-blocked') {
|
|
2859
|
+
throw new Error(`Post-generation install recovery is blocked by ${record.blocker ?? 'invalid state'}.`);
|
|
2860
|
+
}
|
|
2861
|
+
const candidateDigest = record.lockfile.candidateDigest;
|
|
2862
|
+
if (!candidateDigest || !record.lockfile.candidateGeneration) {
|
|
2863
|
+
throw new Error('Post-generation frozen recovery has no lease-produced lock candidate.');
|
|
2864
|
+
}
|
|
2865
|
+
if (record.attempts.frozen >= record.attempts.maxFrozen) {
|
|
2866
|
+
const reviewed = reviewedRecord(workspaceRoot, markerPath, record, lease, 'frozen-retry-exhausted');
|
|
2867
|
+
throw new Error(`Post-generation install requires reviewed cleanup/recovery at ${markerPath} after ${reviewed.attempts.frozen} frozen attempts.`);
|
|
2868
|
+
}
|
|
2869
|
+
const candidateBytes = (0, node_fs_1.readFileSync)(confinedAbsolutePath(workspaceRoot, LOCKFILE_PATH));
|
|
2870
|
+
if (sha256(candidateBytes) !== candidateDigest) {
|
|
2871
|
+
throw new Error('Post-generation frozen recovery candidate changed before execution.');
|
|
2872
|
+
}
|
|
2873
|
+
assertPinnedPnpmAuthority(workspaceRoot, record.packageManager);
|
|
2874
|
+
record = checkpoint(workspaceRoot, markerPath, record, lease, {
|
|
2875
|
+
attempts: {
|
|
2876
|
+
...record.attempts,
|
|
2877
|
+
frozen: record.attempts.frozen + 1,
|
|
2878
|
+
},
|
|
2879
|
+
blocker: undefined,
|
|
2880
|
+
classification: 'generated-source-complete-install-pending',
|
|
2881
|
+
execution: {
|
|
2882
|
+
generation: lease.generation,
|
|
2883
|
+
ownerPid: process.pid,
|
|
2884
|
+
phase: 'frozen-running',
|
|
2885
|
+
},
|
|
2886
|
+
});
|
|
2887
|
+
createActiveJournal(workspaceRoot, markerPath, record, lease);
|
|
2888
|
+
let failed = false;
|
|
2889
|
+
let failure;
|
|
2890
|
+
try {
|
|
2891
|
+
withAttemptEnvironment(lease, 'frozen', () => (dependencies.frozenInstall ??
|
|
2892
|
+
((root) => defaultInstall(root, true, record.packageManager)))(workspaceRoot));
|
|
2893
|
+
}
|
|
2894
|
+
catch (error) {
|
|
2895
|
+
failed = true;
|
|
2896
|
+
failure = error;
|
|
2897
|
+
}
|
|
2898
|
+
const stableExecution = {
|
|
2899
|
+
generation: lease.generation,
|
|
2900
|
+
ownerPid: null,
|
|
2901
|
+
phase: 'frozen-checkpoint',
|
|
2902
|
+
};
|
|
2903
|
+
if (!(0, node_fs_1.existsSync)(confinedAbsolutePath(workspaceRoot, markerPath))) {
|
|
2904
|
+
retainMissingActiveMarker(workspaceRoot, markerPath, record, lease);
|
|
2905
|
+
throw new Error('Post-generation install primary marker disappeared during the frozen attempt; reviewed recovery is required (active-marker-missing).');
|
|
2906
|
+
}
|
|
2907
|
+
if (installExecutionIsAmbiguous(failure)) {
|
|
2908
|
+
reviewedRecord(workspaceRoot, markerPath, record, lease, 'install-execution-ambiguous');
|
|
2909
|
+
throw new Error('Post-generation frozen install ended ambiguously; reviewed recovery is required before any retry.');
|
|
2910
|
+
}
|
|
2911
|
+
if (!packageManagerAuthorityMatches(workspaceRoot, record.packageManager)) {
|
|
2912
|
+
blockedRecord(workspaceRoot, markerPath, record, lease, 'install-authority-drift', stableExecution);
|
|
2913
|
+
throw new Error('Post-generation install recovery is blocked by package-manager install-authority-drift.');
|
|
2914
|
+
}
|
|
2915
|
+
const pendingAfterFrozen = diskPendingMarkerPaths(workspaceRoot);
|
|
2916
|
+
if (pendingAfterFrozen.length !== 1 || pendingAfterFrozen[0] !== markerPath) {
|
|
2917
|
+
if (pendingAfterFrozen.length > 1) {
|
|
2918
|
+
retainBatchConflict(workspaceRoot, pendingAfterFrozen);
|
|
2919
|
+
}
|
|
2920
|
+
blockedRecord(workspaceRoot, markerPath, record, lease, 'workspace-batch-ambiguous', stableExecution);
|
|
2921
|
+
throw new Error('Post-generation install batch became ambiguous during the frozen attempt; no completion was attributed.');
|
|
2922
|
+
}
|
|
2923
|
+
if (!plannedSurfaceMatches(workspaceRoot, record)) {
|
|
2924
|
+
blockedRecord(workspaceRoot, markerPath, record, lease, 'planned-surface-drift', stableExecution);
|
|
2925
|
+
throw new Error('Post-generation install recovery is blocked by planned-surface-drift.');
|
|
2926
|
+
}
|
|
2927
|
+
if (!semanticPrerequisitesMatch(workspaceRoot, record)) {
|
|
2928
|
+
blockedRecord(workspaceRoot, markerPath, record, lease, 'semantic-prerequisite-drift', stableExecution);
|
|
2929
|
+
throw new Error('Post-generation install recovery is blocked by semantic-prerequisite-drift.');
|
|
2930
|
+
}
|
|
2931
|
+
if (!installAuthorityMatches(workspaceRoot, record.installAuthority)) {
|
|
2932
|
+
blockedRecord(workspaceRoot, markerPath, record, lease, 'install-authority-drift', stableExecution);
|
|
2933
|
+
throw new Error('Post-generation install recovery is blocked by install-authority-drift.');
|
|
2934
|
+
}
|
|
2935
|
+
const lockfileAbsolute = confinedAbsolutePath(workspaceRoot, LOCKFILE_PATH);
|
|
2936
|
+
if (!(0, node_fs_1.existsSync)(lockfileAbsolute)) {
|
|
2937
|
+
blockedRecord(workspaceRoot, markerPath, record, lease, 'lockfile-missing', stableExecution);
|
|
2938
|
+
throw new Error('Post-generation install recovery is blocked by lockfile-missing.');
|
|
2939
|
+
}
|
|
2940
|
+
assertSafeRegularFile(lockfileAbsolute, 'Post-generation install lockfile');
|
|
2941
|
+
const observedBytes = (0, node_fs_1.readFileSync)(lockfileAbsolute);
|
|
2942
|
+
if (!observedBytes.equals(candidateBytes)) {
|
|
2943
|
+
blockedRecord(workspaceRoot, markerPath, record, lease, 'lockfile-drift', stableExecution);
|
|
2944
|
+
throw new Error('Post-generation install recovery is blocked by lockfile-drift.');
|
|
2945
|
+
}
|
|
2946
|
+
record = pendingRecord(workspaceRoot, markerPath, record, lease, stableExecution);
|
|
2947
|
+
if (!failed) {
|
|
2948
|
+
record = completeCheckpoint(workspaceRoot, markerPath, record, lease, candidateDigest);
|
|
2949
|
+
triggerProtocolCheckpoint(dependencies, 'after-complete-checkpoint');
|
|
2950
|
+
casRetainCompletion(workspaceRoot, markerPath, record, lease);
|
|
2951
|
+
triggerProtocolCheckpoint(dependencies, 'after-marker-clear-before-lease-release');
|
|
2952
|
+
return;
|
|
2953
|
+
}
|
|
2954
|
+
if (record.attempts.frozen >= record.attempts.maxFrozen) {
|
|
2955
|
+
const reviewed = reviewedRecord(workspaceRoot, markerPath, record, lease, 'frozen-retry-exhausted');
|
|
2956
|
+
throw new Error(`Post-generation install requires reviewed cleanup/recovery at ${markerPath} after ${reviewed.attempts.frozen} frozen attempts.`);
|
|
2957
|
+
}
|
|
2958
|
+
throw new Error(`Post-generation install remains generated-source-complete/install-pending; rerun the exact generator request retained at ${markerPath}.`);
|
|
2959
|
+
}
|
|
2960
|
+
function runNonFrozenAttempt(workspaceRoot, markerPath, record, lease, dependencies) {
|
|
2961
|
+
record = requireStableIntegrity(workspaceRoot, markerPath, record, lease);
|
|
2962
|
+
if (record.classification === 'install-pending-source-integrity-blocked' ||
|
|
2963
|
+
record.classification === 'install-pending-semantic-prerequisite-blocked') {
|
|
2964
|
+
throw new Error(`Post-generation install recovery is blocked by ${record.blocker ?? 'invalid state'}.`);
|
|
2965
|
+
}
|
|
2966
|
+
if (record.attempts.initial >= record.attempts.maxInitial) {
|
|
2967
|
+
const reviewed = reviewedRecord(workspaceRoot, markerPath, record, lease, 'non-frozen-retry-exhausted');
|
|
2968
|
+
throw new Error(`Post-generation install requires reviewed cleanup/recovery at ${markerPath} after ${reviewed.attempts.initial} non-frozen attempts.`);
|
|
2969
|
+
}
|
|
2970
|
+
assertPinnedPnpmAuthority(workspaceRoot, record.packageManager);
|
|
2971
|
+
record = checkpoint(workspaceRoot, markerPath, record, lease, {
|
|
2972
|
+
attempts: {
|
|
2973
|
+
...record.attempts,
|
|
2974
|
+
initial: record.attempts.initial + 1,
|
|
2975
|
+
},
|
|
2976
|
+
blocker: undefined,
|
|
2977
|
+
classification: 'generated-source-complete-install-pending',
|
|
2978
|
+
execution: {
|
|
2979
|
+
generation: lease.generation,
|
|
2980
|
+
ownerPid: process.pid,
|
|
2981
|
+
phase: 'non-frozen-running',
|
|
2982
|
+
},
|
|
2983
|
+
lockfile: {
|
|
2984
|
+
beforeDigest: record.lockfile.beforeDigest,
|
|
2985
|
+
currentDigest: record.lockfile.currentDigest,
|
|
2986
|
+
path: LOCKFILE_PATH,
|
|
2987
|
+
},
|
|
2988
|
+
});
|
|
2989
|
+
createActiveJournal(workspaceRoot, markerPath, record, lease);
|
|
2990
|
+
let failed = false;
|
|
2991
|
+
let failure;
|
|
2992
|
+
try {
|
|
2993
|
+
withAttemptEnvironment(lease, 'non-frozen', () => (dependencies.initialInstall ??
|
|
2994
|
+
dependencies.resumedInitialInstall ??
|
|
2995
|
+
((root) => defaultInstall(root, false, record.packageManager)))(workspaceRoot));
|
|
2996
|
+
}
|
|
2997
|
+
catch (error) {
|
|
2998
|
+
failed = true;
|
|
2999
|
+
failure = error;
|
|
3000
|
+
}
|
|
3001
|
+
const stableExecution = {
|
|
3002
|
+
generation: lease.generation,
|
|
3003
|
+
ownerPid: null,
|
|
3004
|
+
phase: 'non-frozen-checkpoint',
|
|
3005
|
+
};
|
|
3006
|
+
if (!(0, node_fs_1.existsSync)(confinedAbsolutePath(workspaceRoot, markerPath))) {
|
|
3007
|
+
retainMissingActiveMarker(workspaceRoot, markerPath, record, lease);
|
|
3008
|
+
throw new Error('Post-generation install primary marker disappeared during the non-frozen attempt; reviewed recovery is required (active-marker-missing).');
|
|
3009
|
+
}
|
|
3010
|
+
if (installExecutionIsAmbiguous(failure)) {
|
|
3011
|
+
reviewedRecord(workspaceRoot, markerPath, record, lease, 'install-execution-ambiguous');
|
|
3012
|
+
throw new Error('Post-generation non-frozen install ended ambiguously; reviewed recovery is required before any retry.');
|
|
3013
|
+
}
|
|
3014
|
+
if (!packageManagerAuthorityMatches(workspaceRoot, record.packageManager)) {
|
|
3015
|
+
blockedRecord(workspaceRoot, markerPath, record, lease, 'install-authority-drift', stableExecution);
|
|
3016
|
+
throw new Error('Post-generation install recovery is blocked by package-manager install-authority-drift.');
|
|
3017
|
+
}
|
|
3018
|
+
const pendingAfterInstall = diskPendingMarkerPaths(workspaceRoot);
|
|
3019
|
+
if (pendingAfterInstall.length !== 1 ||
|
|
3020
|
+
pendingAfterInstall[0] !== markerPath) {
|
|
3021
|
+
if (pendingAfterInstall.length > 1) {
|
|
3022
|
+
retainBatchConflict(workspaceRoot, pendingAfterInstall);
|
|
3023
|
+
}
|
|
3024
|
+
blockedRecord(workspaceRoot, markerPath, record, lease, 'workspace-batch-ambiguous', stableExecution);
|
|
3025
|
+
throw new Error('Post-generation install batch became ambiguous during the non-frozen attempt; no lock candidate was attributed.');
|
|
3026
|
+
}
|
|
3027
|
+
if (!plannedSurfaceMatches(workspaceRoot, record)) {
|
|
3028
|
+
blockedRecord(workspaceRoot, markerPath, record, lease, 'planned-surface-drift', stableExecution);
|
|
3029
|
+
throw new Error('Post-generation install recovery is blocked by planned-surface-drift.');
|
|
3030
|
+
}
|
|
3031
|
+
if (!semanticPrerequisitesMatch(workspaceRoot, record)) {
|
|
3032
|
+
blockedRecord(workspaceRoot, markerPath, record, lease, 'semantic-prerequisite-drift', stableExecution);
|
|
3033
|
+
throw new Error('Post-generation install recovery is blocked by semantic-prerequisite-drift.');
|
|
3034
|
+
}
|
|
3035
|
+
if (!installAuthorityMatches(workspaceRoot, record.installAuthority)) {
|
|
3036
|
+
blockedRecord(workspaceRoot, markerPath, record, lease, 'install-authority-drift', stableExecution);
|
|
3037
|
+
throw new Error('Post-generation install recovery is blocked by install-authority-drift.');
|
|
3038
|
+
}
|
|
3039
|
+
const observedLock = lockfileDigest(workspaceRoot);
|
|
3040
|
+
if (!observedLock) {
|
|
3041
|
+
blockedRecord(workspaceRoot, markerPath, record, lease, 'lockfile-missing', stableExecution);
|
|
3042
|
+
throw new Error('Post-generation install recovery is blocked by lockfile-missing.');
|
|
3043
|
+
}
|
|
3044
|
+
if (observedLock !== record.lockfile.currentDigest) {
|
|
3045
|
+
record = pendingRecord(workspaceRoot, markerPath, record, lease, {
|
|
3046
|
+
generation: lease.generation,
|
|
3047
|
+
ownerPid: null,
|
|
3048
|
+
phase: 'frozen-checkpoint',
|
|
3049
|
+
}, {
|
|
3050
|
+
...record.lockfile,
|
|
3051
|
+
candidateDigest: observedLock,
|
|
3052
|
+
candidateGeneration: lease.generation,
|
|
3053
|
+
});
|
|
3054
|
+
runFrozenAttempt(workspaceRoot, markerPath, record, lease, dependencies);
|
|
3055
|
+
return;
|
|
3056
|
+
}
|
|
3057
|
+
record = pendingRecord(workspaceRoot, markerPath, record, lease, stableExecution);
|
|
3058
|
+
if (!failed) {
|
|
3059
|
+
record = completeCheckpoint(workspaceRoot, markerPath, record, lease, observedLock);
|
|
3060
|
+
triggerProtocolCheckpoint(dependencies, 'after-complete-checkpoint');
|
|
3061
|
+
casRetainCompletion(workspaceRoot, markerPath, record, lease);
|
|
3062
|
+
triggerProtocolCheckpoint(dependencies, 'after-marker-clear-before-lease-release');
|
|
3063
|
+
return;
|
|
3064
|
+
}
|
|
3065
|
+
if (record.attempts.initial >= record.attempts.maxInitial) {
|
|
3066
|
+
const reviewed = reviewedRecord(workspaceRoot, markerPath, record, lease, 'non-frozen-retry-exhausted');
|
|
3067
|
+
throw new Error(`Post-generation install requires reviewed cleanup/recovery at ${markerPath} after ${reviewed.attempts.initial} non-frozen attempts.`);
|
|
3068
|
+
}
|
|
3069
|
+
throw new Error(`Post-generation install remains generated-source-complete/install-pending before lockfile convergence; rerun the exact generator request retained at ${markerPath}.`);
|
|
3070
|
+
}
|
|
3071
|
+
function diskPendingMarkerPaths(workspaceRoot) {
|
|
3072
|
+
const directory = ensureSafeDirectory(workspaceRoot, INSTALL_RECORD_DIRECTORY);
|
|
3073
|
+
return (0, node_fs_1.readdirSync)(directory, { withFileTypes: true })
|
|
3074
|
+
.filter((entry) => {
|
|
3075
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*\.json$/.test(entry.name)) {
|
|
3076
|
+
return false;
|
|
3077
|
+
}
|
|
3078
|
+
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
3079
|
+
throw new Error(`Post-generation install marker directory contains unsafe entry ${entry.name}.`);
|
|
3080
|
+
}
|
|
3081
|
+
assertSafeRegularFile((0, node_path_1.join)(directory, entry.name), 'Post-generation install marker');
|
|
3082
|
+
return true;
|
|
3083
|
+
})
|
|
3084
|
+
.map((entry) => `${INSTALL_RECORD_DIRECTORY}/${entry.name}`)
|
|
3085
|
+
.sort(literalCompare);
|
|
3086
|
+
}
|
|
3087
|
+
function finalizeAcquiredLease(workspaceRoot, acquired, expectedOperationFingerprint) {
|
|
3088
|
+
const completion = (0, node_fs_1.existsSync)(completionAbsolutePath(workspaceRoot))
|
|
3089
|
+
? readCompletionRecord(workspaceRoot)
|
|
3090
|
+
: undefined;
|
|
3091
|
+
if (completion &&
|
|
3092
|
+
(completion.operation.fingerprint !== expectedOperationFingerprint ||
|
|
3093
|
+
completion.execution.generation !== acquired.lease.generation)) {
|
|
3094
|
+
throw new Error('Post-generation install completion tombstone is not bound to the owned lease.');
|
|
3095
|
+
}
|
|
3096
|
+
releaseLease(workspaceRoot, acquired.lease, expectedOperationFingerprint);
|
|
3097
|
+
if (completion) {
|
|
3098
|
+
clearCompletionTombstone(workspaceRoot, completion);
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
function executeRecovery(workspaceRoot, markerPath, expectedOperationFingerprint, dependencies) {
|
|
3102
|
+
reconcileMissingActiveMarker(workspaceRoot, markerPath, expectedOperationFingerprint);
|
|
3103
|
+
assertNoRetainedBatchConflict(workspaceRoot);
|
|
3104
|
+
const beforeLease = diskPendingMarkerPaths(workspaceRoot);
|
|
3105
|
+
if (beforeLease.length !== 1 || beforeLease[0] !== markerPath) {
|
|
3106
|
+
if (beforeLease.length > 1) {
|
|
3107
|
+
retainBatchConflict(workspaceRoot, beforeLease);
|
|
3108
|
+
}
|
|
3109
|
+
throw new Error(beforeLease.length === 0
|
|
3110
|
+
? `Post-generation install marker disappeared before recovery at ${markerPath}.`
|
|
3111
|
+
: `Post-generation install batch is ambiguous (${beforeLease.join(', ')}); no shared-lockfile install was started. Remove or reconcile every operation as one reviewed batch.`);
|
|
3112
|
+
}
|
|
3113
|
+
const acquired = acquireLease(workspaceRoot, markerPath, expectedOperationFingerprint, dependencies);
|
|
3114
|
+
let simulatedCrash = false;
|
|
3115
|
+
try {
|
|
3116
|
+
const pending = diskPendingMarkerPaths(workspaceRoot);
|
|
3117
|
+
if (pending.length !== 1 || pending[0] !== markerPath) {
|
|
3118
|
+
if (pending.length > 1) {
|
|
3119
|
+
retainBatchConflict(workspaceRoot, pending);
|
|
3120
|
+
}
|
|
3121
|
+
throw new Error(pending.length === 0
|
|
3122
|
+
? `Post-generation install marker disappeared before recovery at ${markerPath}.`
|
|
3123
|
+
: `Post-generation install batch is ambiguous (${pending.join(', ')}); no shared-lockfile install was started. Remove or reconcile every operation as one reviewed batch.`);
|
|
3124
|
+
}
|
|
3125
|
+
let record = readDiskRecord(workspaceRoot, markerPath);
|
|
3126
|
+
if (record.operation.fingerprint !== expectedOperationFingerprint) {
|
|
3127
|
+
throw new Error('Post-generation install recovery belongs to a different generator request.');
|
|
3128
|
+
}
|
|
3129
|
+
record = recoverOrRebindRecord(workspaceRoot, markerPath, record, acquired);
|
|
3130
|
+
// Keep every renamed stale lease until the marker is CAS-rebound to the
|
|
3131
|
+
// replacement generation. A crash before this point therefore retains the
|
|
3132
|
+
// complete absence-proof chain needed by the next recovery process.
|
|
3133
|
+
clearStaleLeaseClaims(acquired);
|
|
3134
|
+
if (record.classification === 'install-pending-source-integrity-blocked' ||
|
|
3135
|
+
record.classification === 'install-pending-semantic-prerequisite-blocked') {
|
|
3136
|
+
const integrity = requireStableIntegrity(workspaceRoot, markerPath, record, acquired.lease);
|
|
3137
|
+
if (integrity.classification ===
|
|
3138
|
+
'install-pending-source-integrity-blocked' ||
|
|
3139
|
+
integrity.classification ===
|
|
3140
|
+
'install-pending-semantic-prerequisite-blocked') {
|
|
3141
|
+
throw new Error(`Post-generation install recovery is blocked by ${integrity.blocker ?? 'invalid state'}.`);
|
|
3142
|
+
}
|
|
3143
|
+
record = integrity;
|
|
3144
|
+
}
|
|
3145
|
+
else if (record.execution.phase !== 'complete-checkpoint' &&
|
|
3146
|
+
record.execution.phase !== 'reviewed-recovery-required') {
|
|
3147
|
+
record = requireStableIntegrity(workspaceRoot, markerPath, record, acquired.lease);
|
|
3148
|
+
if (record.classification === 'install-pending-source-integrity-blocked' ||
|
|
3149
|
+
record.classification ===
|
|
3150
|
+
'install-pending-semantic-prerequisite-blocked') {
|
|
3151
|
+
throw new Error(`Post-generation install recovery is blocked by ${record.blocker ?? 'invalid state'}.`);
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
if (record.execution.phase === 'complete-checkpoint') {
|
|
3155
|
+
casRetainCompletion(workspaceRoot, markerPath, record, acquired.lease);
|
|
3156
|
+
triggerProtocolCheckpoint(dependencies, 'after-marker-clear-before-lease-release');
|
|
3157
|
+
return;
|
|
3158
|
+
}
|
|
3159
|
+
if (record.execution.phase === 'reviewed-recovery-required' ||
|
|
3160
|
+
record.classification === 'install-pending-reviewed-recovery-required') {
|
|
3161
|
+
throw new Error(`Post-generation install requires reviewed cleanup/recovery at ${markerPath}; automatic attempts are exhausted or provenance is ambiguous.`);
|
|
3162
|
+
}
|
|
3163
|
+
if (record.execution.phase === 'frozen-checkpoint') {
|
|
3164
|
+
runFrozenAttempt(workspaceRoot, markerPath, record, acquired.lease, dependencies);
|
|
3165
|
+
return;
|
|
3166
|
+
}
|
|
3167
|
+
runNonFrozenAttempt(workspaceRoot, markerPath, record, acquired.lease, dependencies);
|
|
3168
|
+
}
|
|
3169
|
+
catch (error) {
|
|
3170
|
+
simulatedCrash = error instanceof SimulatedProtocolCrash;
|
|
3171
|
+
throw error;
|
|
3172
|
+
}
|
|
3173
|
+
finally {
|
|
3174
|
+
if (!simulatedCrash) {
|
|
3175
|
+
let activeJournalRetained = true;
|
|
3176
|
+
try {
|
|
3177
|
+
activeJournalRetained = readActiveJournal(workspaceRoot) !== undefined;
|
|
3178
|
+
}
|
|
3179
|
+
catch {
|
|
3180
|
+
// An unreadable journal is still durable evidence. Keep the exact lease
|
|
3181
|
+
// so a later process can reconcile it only after proving this PID absent.
|
|
3182
|
+
}
|
|
3183
|
+
if (!activeJournalRetained) {
|
|
3184
|
+
finalizeAcquiredLease(workspaceRoot, acquired, expectedOperationFingerprint);
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
function pendingMarkerPaths(tree) {
|
|
3190
|
+
return tree
|
|
3191
|
+
.children(INSTALL_RECORD_DIRECTORY)
|
|
3192
|
+
.filter((entry) => /^[a-z0-9]+(?:-[a-z0-9]+)*\.json$/.test(entry) &&
|
|
3193
|
+
tree.exists(`${INSTALL_RECORD_DIRECTORY}/${entry}`))
|
|
3194
|
+
.map((entry) => `${INSTALL_RECORD_DIRECTORY}/${entry}`)
|
|
3195
|
+
.sort(literalCompare);
|
|
3196
|
+
}
|
|
3197
|
+
function reconcilePostGenerationInstallWorkspaceState(tree, expectedMarkerPath, expectedOperationFingerprint) {
|
|
3198
|
+
if (!(0, node_fs_1.existsSync)((0, node_path_1.resolve)(tree.root)))
|
|
3199
|
+
return;
|
|
3200
|
+
assertSafeExistingDirectoryChain(tree.root, INSTALL_RECORD_DIRECTORY);
|
|
3201
|
+
reconcileMissingActiveMarker(tree.root, expectedMarkerPath, expectedOperationFingerprint);
|
|
3202
|
+
reconcileCompletionTombstone(tree.root);
|
|
3203
|
+
}
|
|
3204
|
+
/**
|
|
3205
|
+
* Prevents a non-installing generator from changing workspace bytes while a
|
|
3206
|
+
* Slice or workload install still owns an exact recovery surface.
|
|
3207
|
+
*/
|
|
3208
|
+
function assertPostGenerationInstallWorkspaceAvailable(tree, operationId) {
|
|
3209
|
+
if (operationId.length > 256 ||
|
|
3210
|
+
!/^[a-z0-9]+(?:[-:][a-z0-9]+)*$/.test(operationId)) {
|
|
3211
|
+
throw new Error('Post-generation install workspace operation id must be bounded lowercase kebab-case segments.');
|
|
3212
|
+
}
|
|
3213
|
+
reconcilePostGenerationInstallWorkspaceState(tree);
|
|
3214
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.resolve)(tree.root))) {
|
|
3215
|
+
assertNoRetainedBatchConflict(tree.root);
|
|
3216
|
+
}
|
|
3217
|
+
else if (tree.exists(INSTALL_BATCH_CONFLICT_PATH)) {
|
|
3218
|
+
throw new Error(`Post-generation install batch is ambiguous and requires reviewed reconciliation at ${INSTALL_BATCH_CONFLICT_PATH}; no other generator may write workspace source.`);
|
|
3219
|
+
}
|
|
3220
|
+
const markers = pendingMarkerPaths(tree);
|
|
3221
|
+
if (markers.length > 0) {
|
|
3222
|
+
throw new Error(`Another post-generation install is pending at ${markers[0]}; resume it before starting ${operationId}.`);
|
|
3223
|
+
}
|
|
3224
|
+
const retainedPaths = [
|
|
3225
|
+
INSTALL_ACTIVE_PATH,
|
|
3226
|
+
INSTALL_COMPLETION_PATH,
|
|
3227
|
+
INSTALL_LEASE_PATH,
|
|
3228
|
+
...tree
|
|
3229
|
+
.children((0, node_path_1.dirname)(INSTALL_LEASE_PATH))
|
|
3230
|
+
.filter((entry) => entry.startsWith(`${INSTALL_LEASE_PATH.slice(INSTALL_LEASE_PATH.lastIndexOf('/') + 1)}.stale-`))
|
|
3231
|
+
.map((entry) => `${(0, node_path_1.dirname)(INSTALL_LEASE_PATH)}/${entry}`),
|
|
3232
|
+
]
|
|
3233
|
+
.filter((path) => tree.exists(path))
|
|
3234
|
+
.sort(literalCompare);
|
|
3235
|
+
if (retainedPaths.length > 0) {
|
|
3236
|
+
throw new Error(`Post-generation install state is retained at ${retainedPaths[0]}; reconcile it before starting ${operationId}.`);
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
function resumePendingPostGenerationInstall(tree, operation, skipInstall, dependencies = {}) {
|
|
3240
|
+
assertOperation(operation);
|
|
3241
|
+
reconcilePostGenerationInstallWorkspaceState(tree, postGenerationInstallMarkerPath(operation.key), expectedFingerprint(operation));
|
|
3242
|
+
const markerPath = postGenerationInstallMarkerPath(operation.key);
|
|
3243
|
+
const markers = pendingMarkerPaths(tree);
|
|
3244
|
+
const source = tree.read(markerPath, 'utf8');
|
|
3245
|
+
if (source === null) {
|
|
3246
|
+
if (markers.length > 0) {
|
|
3247
|
+
throw new Error(`Another post-generation install is pending at ${markers[0]}; resume it before starting ${operation.id}.`);
|
|
3248
|
+
}
|
|
3249
|
+
return undefined;
|
|
3250
|
+
}
|
|
3251
|
+
if (markers[0] !== markerPath) {
|
|
3252
|
+
throw new Error(`Post-generation installs share one lockfile; resume ${markers[0]} before ${markerPath}.`);
|
|
3253
|
+
}
|
|
3254
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.resolve)(tree.root))) {
|
|
3255
|
+
assertSafeExistingDirectoryChain(tree.root, INSTALL_RECORD_DIRECTORY);
|
|
3256
|
+
const absolute = confinedAbsolutePath(tree.root, markerPath);
|
|
3257
|
+
if ((0, node_fs_1.existsSync)(absolute)) {
|
|
3258
|
+
assertSafeRegularFile(absolute, 'Post-generation install marker');
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
const record = parseRecord(source);
|
|
3262
|
+
const fingerprint = expectedFingerprint(operation);
|
|
3263
|
+
if (record.operation.fingerprint !== fingerprint ||
|
|
3264
|
+
record.operation.sourceRoot !== operation.sourceRoot) {
|
|
3265
|
+
throw new Error('Post-generation install recovery belongs to a different generator request.');
|
|
3266
|
+
}
|
|
3267
|
+
if (skipInstall) {
|
|
3268
|
+
throw new Error(`Generated source is complete but installation remains pending at ${markerPath}; rerun without --skipInstall.`);
|
|
3269
|
+
}
|
|
3270
|
+
if (record.execution.phase === 'reviewed-recovery-required' ||
|
|
3271
|
+
record.classification === 'install-pending-reviewed-recovery-required') {
|
|
3272
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.resolve)(tree.root)) && readActiveJournal(tree.root)) {
|
|
3273
|
+
return () => executeRecovery(tree.root, markerPath, fingerprint, dependencies);
|
|
3274
|
+
}
|
|
3275
|
+
throw new Error(`Post-generation install requires reviewed cleanup/recovery at ${markerPath}; automatic attempts are exhausted.`);
|
|
3276
|
+
}
|
|
3277
|
+
return () => executeRecovery(tree.root, markerPath, fingerprint, dependencies);
|
|
3278
|
+
}
|
|
3279
|
+
function createPostGenerationInstallCallback(tree, before, scope, operation, dependencies = {}) {
|
|
3280
|
+
assertOperation(operation);
|
|
3281
|
+
const markerPath = postGenerationInstallMarkerPath(operation.key);
|
|
3282
|
+
const reservationSource = tree.read(markerPath, 'utf8');
|
|
3283
|
+
if (reservationSource === null) {
|
|
3284
|
+
throw new Error('Post-generation install planning did not reserve recovery state before generating source.');
|
|
3285
|
+
}
|
|
3286
|
+
const reservation = parseReservation(reservationSource);
|
|
3287
|
+
if (reservation.generation !== before.reservation.generation ||
|
|
3288
|
+
reservation.operationFingerprint !== expectedFingerprint(operation) ||
|
|
3289
|
+
(0, canonical_json_1.canonicalJson)(reservation) !== (0, canonical_json_1.canonicalJson)(before.reservation)) {
|
|
3290
|
+
throw new Error('Post-generation install planning reservation changed before finalization.');
|
|
3291
|
+
}
|
|
3292
|
+
const files = plannedFiles(tree, before, scope);
|
|
3293
|
+
const authorityReader = treeInstallAuthorityReader(tree);
|
|
3294
|
+
const record = createRecord(operation, before, files, buildInstallAuthority(authorityReader), bindSemanticPrerequisites(authorityReader, operation.semanticPrerequisites ?? []));
|
|
3295
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.resolve)(tree.root))) {
|
|
3296
|
+
assertSafeExistingDirectoryChain(tree.root, INSTALL_RECORD_DIRECTORY);
|
|
3297
|
+
}
|
|
3298
|
+
const existing = pendingMarkerPaths(tree);
|
|
3299
|
+
if (existing.length !== 1 ||
|
|
3300
|
+
existing[0] !== markerPath ||
|
|
3301
|
+
!tree.exists(markerPath)) {
|
|
3302
|
+
throw new Error(`Post-generation install planning reservation is ambiguous at ${existing[0] ?? markerPath}.`);
|
|
3303
|
+
}
|
|
3304
|
+
tree.write(markerPath, serializeRecord(record), { mode: 0o600 });
|
|
3305
|
+
const fingerprint = record.operation.fingerprint;
|
|
3306
|
+
return () => executeRecovery(tree.root, markerPath, fingerprint, dependencies);
|
|
3307
|
+
}
|
|
3308
|
+
//# sourceMappingURL=post-generation-install.js.map
|