@mannyc1/ts-release 0.0.7 → 0.2.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/ARCHITECTURE.md +124 -92
- package/CHANGELOG.md +115 -0
- package/LICENSE +21 -0
- package/README.md +278 -324
- package/SPEC.md +206 -310
- package/apps/release-ts/README.md +32 -0
- package/apps/release-ts/src/cli/command.ts +164 -0
- package/apps/release-ts/src/cli/commands.ts +351 -0
- package/apps/release-ts/src/cli/main.ts +43 -0
- package/apps/release-ts/src/cli/node-main.ts +49 -0
- package/apps/ts-release-agents/LICENSE +21 -0
- package/apps/ts-release-agents/README.md +21 -0
- package/apps/ts-release-agents/evals/cases.json +11 -0
- package/apps/ts-release-agents/manifests/claude.json +5 -0
- package/apps/ts-release-agents/manifests/codex.json +5 -0
- package/apps/ts-release-agents/package.json +11 -0
- package/apps/ts-release-agents/skills/release/SKILL.md +48 -0
- package/apps/ts-release-agents/skills/release/references/configuration.md +18 -0
- package/apps/ts-release-agents/skills/release/references/recovery.md +10 -0
- package/apps/ts-release-agents/skills/release/references/staged-workflow.md +12 -0
- package/apps/ts-release-agents/skills/release/references/target-selection.md +13 -0
- package/apps/ts-release-agents/skills/release/references/verification.md +14 -0
- package/apps/ts-release-agents/src/build.ts +105 -0
- package/apps/ts-release-agents/src/check.ts +117 -0
- package/apps/ts-release-agents/src/install.ts +121 -0
- package/apps/ts-release-agents/test/contract.test.ts +56 -0
- package/apps/ts-release-agents/tsconfig.json +5 -0
- package/dist/api/api.d.ts +15 -0
- package/dist/api/api.d.ts.map +1 -0
- package/dist/api/api.js +244 -0
- package/dist/api/api.js.map +1 -0
- package/dist/api/errors.d.ts +39 -0
- package/dist/api/errors.d.ts.map +1 -0
- package/dist/api/errors.js +32 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/input.d.ts +10 -0
- package/dist/api/input.d.ts.map +1 -0
- package/dist/api/input.js +73 -0
- package/dist/api/input.js.map +1 -0
- package/dist/api/runtime.d.ts +18 -0
- package/dist/api/runtime.d.ts.map +1 -0
- package/dist/api/runtime.js +7 -0
- package/dist/api/runtime.js.map +1 -0
- package/dist/api/types.d.ts +76 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +17 -0
- package/dist/api/types.js.map +1 -0
- package/dist/bin/ts-release.js +39239 -0
- package/dist/capabilities/bun-targets.d.ts +3 -0
- package/dist/capabilities/bun-targets.d.ts.map +1 -0
- package/dist/capabilities/bun-targets.js +3 -0
- package/dist/capabilities/bun-targets.js.map +1 -0
- package/dist/capabilities/field-ownership.d.ts +17 -0
- package/dist/capabilities/field-ownership.d.ts.map +1 -0
- package/dist/capabilities/field-ownership.js +40 -0
- package/dist/capabilities/field-ownership.js.map +1 -0
- package/dist/capabilities/module.d.ts +91 -0
- package/dist/capabilities/module.d.ts.map +1 -0
- package/dist/capabilities/module.js +2 -0
- package/dist/capabilities/module.js.map +1 -0
- package/dist/capabilities/registry.d.ts +829 -0
- package/dist/capabilities/registry.d.ts.map +1 -0
- package/dist/capabilities/registry.js +291 -0
- package/dist/capabilities/registry.js.map +1 -0
- package/dist/config/config.d.ts +6 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +42 -0
- package/dist/config/config.js.map +1 -0
- package/dist/correction/coordinator.d.ts +248 -0
- package/dist/correction/coordinator.d.ts.map +1 -0
- package/dist/correction/coordinator.js +274 -0
- package/dist/correction/coordinator.js.map +1 -0
- package/dist/correction/index.d.ts +3 -0
- package/dist/correction/index.d.ts.map +1 -0
- package/dist/correction/index.js +3 -0
- package/dist/correction/index.js.map +1 -0
- package/dist/correction/intent.d.ts +111 -0
- package/dist/correction/intent.d.ts.map +1 -0
- package/dist/correction/intent.js +146 -0
- package/dist/correction/intent.js.map +1 -0
- package/dist/drivers/archive.d.ts +8 -0
- package/dist/drivers/archive.d.ts.map +1 -0
- package/dist/drivers/archive.js +101 -0
- package/dist/drivers/archive.js.map +1 -0
- package/dist/drivers/contain.d.ts +6 -0
- package/dist/drivers/contain.d.ts.map +1 -0
- package/dist/drivers/contain.js +17 -0
- package/dist/drivers/contain.js.map +1 -0
- package/dist/drivers/environment.d.ts +4 -0
- package/dist/drivers/environment.d.ts.map +1 -0
- package/dist/drivers/environment.js +18 -0
- package/dist/drivers/environment.js.map +1 -0
- package/dist/drivers/errors.d.ts +9 -0
- package/dist/drivers/errors.d.ts.map +1 -0
- package/dist/drivers/errors.js +6 -0
- package/dist/drivers/errors.js.map +1 -0
- package/dist/drivers/glob.d.ts +2 -0
- package/dist/drivers/glob.d.ts.map +1 -0
- package/dist/drivers/glob.js +162 -0
- package/dist/drivers/glob.js.map +1 -0
- package/dist/drivers/process.d.ts +48 -0
- package/dist/drivers/process.d.ts.map +1 -0
- package/dist/drivers/process.js +257 -0
- package/dist/drivers/process.js.map +1 -0
- package/dist/drivers/redact.d.ts +4 -0
- package/dist/drivers/redact.d.ts.map +1 -0
- package/dist/drivers/redact.js +24 -0
- package/dist/drivers/redact.js.map +1 -0
- package/dist/drivers/seccomp-helper-source.d.ts +7 -0
- package/dist/drivers/seccomp-helper-source.d.ts.map +1 -0
- package/dist/drivers/seccomp-helper-source.js +121 -0
- package/dist/drivers/seccomp-helper-source.js.map +1 -0
- package/dist/drivers/utils.d.ts +4 -0
- package/dist/drivers/utils.d.ts.map +1 -0
- package/dist/drivers/utils.js +5 -0
- package/dist/drivers/utils.js.map +1 -0
- package/dist/drivers/workspace.d.ts +6 -0
- package/dist/drivers/workspace.d.ts.map +1 -0
- package/dist/drivers/workspace.js +49 -0
- package/dist/drivers/workspace.js.map +1 -0
- package/dist/extensions/provider-adapter.d.ts +48 -0
- package/dist/extensions/provider-adapter.d.ts.map +1 -0
- package/dist/extensions/provider-adapter.js +64 -0
- package/dist/extensions/provider-adapter.js.map +1 -0
- package/dist/host.d.ts +33 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +20 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +19 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/model/artifact-collection.d.ts +84 -0
- package/dist/model/artifact-collection.d.ts.map +1 -0
- package/dist/model/artifact-collection.js +185 -0
- package/dist/model/artifact-collection.js.map +1 -0
- package/dist/model/authority.d.ts +89 -0
- package/dist/model/authority.d.ts.map +1 -0
- package/dist/model/authority.js +80 -0
- package/dist/model/authority.js.map +1 -0
- package/dist/model/bun-targets.d.ts +34 -0
- package/dist/model/bun-targets.d.ts.map +1 -0
- package/dist/model/bun-targets.js +17 -0
- package/dist/model/bun-targets.js.map +1 -0
- package/dist/model/canonical.d.ts +8 -0
- package/dist/model/canonical.d.ts.map +1 -0
- package/dist/model/canonical.js +205 -0
- package/dist/model/canonical.js.map +1 -0
- package/dist/model/catalog.d.ts +76 -0
- package/dist/model/catalog.d.ts.map +1 -0
- package/dist/model/catalog.js +174 -0
- package/dist/model/catalog.js.map +1 -0
- package/dist/model/digest.d.ts +53 -0
- package/dist/model/digest.d.ts.map +1 -0
- package/dist/model/digest.js +127 -0
- package/dist/model/digest.js.map +1 -0
- package/dist/model/errors.d.ts +14 -0
- package/dist/model/errors.d.ts.map +1 -0
- package/dist/model/errors.js +8 -0
- package/dist/model/errors.js.map +1 -0
- package/dist/model/primitives.d.ts +19 -0
- package/dist/model/primitives.d.ts.map +1 -0
- package/dist/model/primitives.js +18 -0
- package/dist/model/primitives.js.map +1 -0
- package/dist/model/pypi.d.ts +12 -0
- package/dist/model/pypi.d.ts.map +1 -0
- package/dist/model/pypi.js +20 -0
- package/dist/model/pypi.js.map +1 -0
- package/dist/model/python-distribution.d.ts +33 -0
- package/dist/model/python-distribution.d.ts.map +1 -0
- package/dist/model/python-distribution.js +367 -0
- package/dist/model/python-distribution.js.map +1 -0
- package/dist/model/secret-patterns.d.ts +2 -0
- package/dist/model/secret-patterns.d.ts.map +1 -0
- package/dist/model/secret-patterns.js +12 -0
- package/dist/model/secret-patterns.js.map +1 -0
- package/dist/platform/bun.d.ts +7 -0
- package/dist/platform/bun.d.ts.map +1 -0
- package/dist/platform/bun.js +15 -0
- package/dist/platform/bun.js.map +1 -0
- package/dist/platform/credentials.d.ts +32 -0
- package/dist/platform/credentials.d.ts.map +1 -0
- package/dist/platform/credentials.js +772 -0
- package/dist/platform/credentials.js.map +1 -0
- package/dist/platform/host-support.d.ts +2 -0
- package/dist/platform/host-support.d.ts.map +1 -0
- package/dist/platform/host-support.js +4 -0
- package/dist/platform/host-support.js.map +1 -0
- package/dist/platform/node.d.ts +7 -0
- package/dist/platform/node.d.ts.map +1 -0
- package/dist/platform/node.js +15 -0
- package/dist/platform/node.js.map +1 -0
- package/dist/platform/release-runtime.d.ts +11 -0
- package/dist/platform/release-runtime.d.ts.map +1 -0
- package/dist/platform/release-runtime.js +34 -0
- package/dist/platform/release-runtime.js.map +1 -0
- package/dist/platform/services.d.ts +15 -0
- package/dist/platform/services.d.ts.map +1 -0
- package/dist/platform/services.js +30 -0
- package/dist/platform/services.js.map +1 -0
- package/dist/platform/source-observer.d.ts +5 -0
- package/dist/platform/source-observer.d.ts.map +1 -0
- package/dist/platform/source-observer.js +159 -0
- package/dist/platform/source-observer.js.map +1 -0
- package/dist/provider-sdk.d.ts +14 -0
- package/dist/provider-sdk.d.ts.map +1 -0
- package/dist/provider-sdk.js +10 -0
- package/dist/provider-sdk.js.map +1 -0
- package/dist/publication/adapter.d.ts +212 -0
- package/dist/publication/adapter.d.ts.map +1 -0
- package/dist/publication/adapter.js +83 -0
- package/dist/publication/adapter.js.map +1 -0
- package/dist/publication/authority.d.ts +137 -0
- package/dist/publication/authority.d.ts.map +1 -0
- package/dist/publication/authority.js +233 -0
- package/dist/publication/authority.js.map +1 -0
- package/dist/publication/catalog-git.d.ts +37 -0
- package/dist/publication/catalog-git.d.ts.map +1 -0
- package/dist/publication/catalog-git.js +451 -0
- package/dist/publication/catalog-git.js.map +1 -0
- package/dist/publication/claim.d.ts +39 -0
- package/dist/publication/claim.d.ts.map +1 -0
- package/dist/publication/claim.js +31 -0
- package/dist/publication/claim.js.map +1 -0
- package/dist/publication/coordinator.d.ts +246 -0
- package/dist/publication/coordinator.d.ts.map +1 -0
- package/dist/publication/coordinator.js +370 -0
- package/dist/publication/coordinator.js.map +1 -0
- package/dist/publication/github.d.ts +36 -0
- package/dist/publication/github.d.ts.map +1 -0
- package/dist/publication/github.js +644 -0
- package/dist/publication/github.js.map +1 -0
- package/dist/publication/http.d.ts +69 -0
- package/dist/publication/http.d.ts.map +1 -0
- package/dist/publication/http.js +29 -0
- package/dist/publication/http.js.map +1 -0
- package/dist/publication/index.d.ts +5 -0
- package/dist/publication/index.d.ts.map +1 -0
- package/dist/publication/index.js +5 -0
- package/dist/publication/index.js.map +1 -0
- package/dist/publication/npm.d.ts +40 -0
- package/dist/publication/npm.d.ts.map +1 -0
- package/dist/publication/npm.js +359 -0
- package/dist/publication/npm.js.map +1 -0
- package/dist/publication/profiles.d.ts +136 -0
- package/dist/publication/profiles.d.ts.map +1 -0
- package/dist/publication/profiles.js +90 -0
- package/dist/publication/profiles.js.map +1 -0
- package/dist/publication/publisher.d.ts +82 -0
- package/dist/publication/publisher.d.ts.map +1 -0
- package/dist/publication/publisher.js +53 -0
- package/dist/publication/publisher.js.map +1 -0
- package/dist/publication/pypi.d.ts +45 -0
- package/dist/publication/pypi.d.ts.map +1 -0
- package/dist/publication/pypi.js +369 -0
- package/dist/publication/pypi.js.map +1 -0
- package/dist/publication/recovery.d.ts +663 -0
- package/dist/publication/recovery.d.ts.map +1 -0
- package/dist/publication/recovery.js +252 -0
- package/dist/publication/recovery.js.map +1 -0
- package/dist/publication/report.d.ts +1061 -0
- package/dist/publication/report.d.ts.map +1 -0
- package/dist/publication/report.js +441 -0
- package/dist/publication/report.js.map +1 -0
- package/dist/recipes/config.d.ts +296 -0
- package/dist/recipes/config.d.ts.map +1 -0
- package/dist/recipes/config.js +325 -0
- package/dist/recipes/config.js.map +1 -0
- package/dist/release/capabilities.d.ts +12 -0
- package/dist/release/capabilities.d.ts.map +1 -0
- package/dist/release/capabilities.js +395 -0
- package/dist/release/capabilities.js.map +1 -0
- package/dist/release/compiler.d.ts +5 -0
- package/dist/release/compiler.d.ts.map +1 -0
- package/dist/release/compiler.js +19 -0
- package/dist/release/compiler.js.map +1 -0
- package/dist/release/config.d.ts +5 -0
- package/dist/release/config.d.ts.map +1 -0
- package/dist/release/config.js +10 -0
- package/dist/release/config.js.map +1 -0
- package/dist/release/context.d.ts +96 -0
- package/dist/release/context.d.ts.map +1 -0
- package/dist/release/context.js +168 -0
- package/dist/release/context.js.map +1 -0
- package/dist/release/graph.d.ts +271 -0
- package/dist/release/graph.d.ts.map +1 -0
- package/dist/release/graph.js +766 -0
- package/dist/release/graph.js.map +1 -0
- package/dist/release/inspect.d.ts +88 -0
- package/dist/release/inspect.d.ts.map +1 -0
- package/dist/release/inspect.js +75 -0
- package/dist/release/inspect.js.map +1 -0
- package/dist/release/prepare.d.ts +24 -0
- package/dist/release/prepare.d.ts.map +1 -0
- package/dist/release/prepare.js +1116 -0
- package/dist/release/prepare.js.map +1 -0
- package/dist/release/prepared-ref.d.ts +60 -0
- package/dist/release/prepared-ref.d.ts.map +1 -0
- package/dist/release/prepared-ref.js +131 -0
- package/dist/release/prepared-ref.js.map +1 -0
- package/dist/release/prepared-store.d.ts +90 -0
- package/dist/release/prepared-store.d.ts.map +1 -0
- package/dist/release/prepared-store.js +337 -0
- package/dist/release/prepared-store.js.map +1 -0
- package/dist/release/prepared.d.ts +172 -0
- package/dist/release/prepared.d.ts.map +1 -0
- package/dist/release/prepared.js +268 -0
- package/dist/release/prepared.js.map +1 -0
- package/dist/release/staging.d.ts +31 -0
- package/dist/release/staging.d.ts.map +1 -0
- package/dist/release/staging.js +218 -0
- package/dist/release/staging.js.map +1 -0
- package/dist/resolve/authored.d.ts +53 -0
- package/dist/resolve/authored.d.ts.map +1 -0
- package/dist/resolve/authored.js +55 -0
- package/dist/resolve/authored.js.map +1 -0
- package/dist/resolve/encode.d.ts +3 -0
- package/dist/resolve/encode.d.ts.map +1 -0
- package/dist/resolve/encode.js +21 -0
- package/dist/resolve/encode.js.map +1 -0
- package/dist/resolve/errors.d.ts +7 -0
- package/dist/resolve/errors.d.ts.map +1 -0
- package/dist/resolve/errors.js +17 -0
- package/dist/resolve/errors.js.map +1 -0
- package/dist/resolve/facts.d.ts +12 -0
- package/dist/resolve/facts.d.ts.map +1 -0
- package/dist/resolve/facts.js +18 -0
- package/dist/resolve/facts.js.map +1 -0
- package/dist/resolve/resolve.d.ts +10 -0
- package/dist/resolve/resolve.d.ts.map +1 -0
- package/dist/resolve/resolve.js +242 -0
- package/dist/resolve/resolve.js.map +1 -0
- package/dist/store.d.ts +11 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +9 -0
- package/dist/store.js.map +1 -0
- package/examples/README.md +20 -30
- package/examples/github-release/release.config.json +11 -18
- package/examples/homebrew-tap/README.md +11 -0
- package/examples/homebrew-tap/release.config.json +40 -28
- package/examples/multi-target/release.config.json +28 -53
- package/examples/npm-first-publish/release.config.json +23 -25
- package/examples/npm-only/release.config.json +22 -30
- package/examples/portable-cli/README.md +17 -0
- package/examples/portable-cli/artifacts/portable-example-0.1.0-darwin-arm64 +1 -0
- package/examples/portable-cli/artifacts/portable-example-0.1.0-darwin-x64 +1 -0
- package/examples/portable-cli/artifacts/portable-example-0.1.0-linux-arm64 +1 -0
- package/examples/portable-cli/artifacts/portable-example-0.1.0-linux-x64 +1 -0
- package/examples/portable-cli/artifacts/portable-example-0.1.0-windows-x64.exe +1 -0
- package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-macosx_10_15_x86_64.whl +1 -0
- package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-macosx_11_0_arm64.whl +1 -0
- package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-manylinux2014_aarch64.whl +1 -0
- package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-manylinux2014_x86_64.whl +1 -0
- package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-win_amd64.whl +1 -0
- package/examples/portable-cli/package.json +9 -0
- package/examples/portable-cli/release.config.json +53 -0
- package/examples/portable-cli/src/cli.ts +3 -0
- package/examples/pypi-registry/README.md +8 -0
- package/examples/scoop-bucket/README.md +10 -0
- package/examples/scoop-bucket/release.config.json +40 -30
- package/package.json +60 -156
- package/src/api/api.ts +365 -0
- package/src/api/errors.ts +36 -0
- package/src/api/input.ts +92 -0
- package/src/api/runtime.ts +14 -0
- package/src/api/types.ts +103 -0
- package/src/capabilities/bun-targets.ts +2 -0
- package/src/capabilities/field-ownership.ts +60 -0
- package/src/capabilities/module.ts +158 -0
- package/src/capabilities/registry.ts +333 -0
- package/src/config/config.ts +48 -0
- package/src/correction/coordinator.ts +336 -0
- package/src/correction/index.ts +2 -0
- package/src/correction/intent.ts +172 -0
- package/src/drivers/archive.ts +102 -0
- package/src/drivers/contain.ts +20 -0
- package/src/drivers/environment.ts +28 -0
- package/src/drivers/errors.ts +6 -0
- package/src/drivers/glob.ts +137 -0
- package/src/drivers/process.ts +340 -0
- package/src/drivers/redact.ts +31 -0
- package/src/drivers/seccomp-helper-source.ts +121 -0
- package/src/drivers/utils.ts +10 -0
- package/src/drivers/workspace.ts +40 -0
- package/src/extensions/provider-adapter.ts +99 -0
- package/src/host.ts +140 -0
- package/src/index.ts +64 -0
- package/src/model/artifact-collection.ts +237 -0
- package/src/model/authority.ts +116 -0
- package/src/model/bun-targets.ts +34 -0
- package/src/model/canonical.ts +205 -0
- package/src/model/catalog.ts +220 -0
- package/src/model/digest.ts +172 -0
- package/src/model/errors.ts +6 -0
- package/src/model/primitives.ts +37 -0
- package/src/model/pypi.ts +30 -0
- package/src/model/python-distribution.ts +360 -0
- package/src/model/secret-patterns.ts +11 -0
- package/src/platform/bun.ts +38 -0
- package/src/platform/credentials.ts +1083 -0
- package/src/platform/host-support.ts +4 -0
- package/src/platform/node.ts +38 -0
- package/src/platform/release-runtime.ts +47 -0
- package/src/platform/services.ts +57 -0
- package/src/platform/source-observer.ts +193 -0
- package/src/provider-sdk.ts +48 -0
- package/src/publication/adapter.ts +132 -0
- package/src/publication/authority.ts +397 -0
- package/src/publication/catalog-git.ts +603 -0
- package/src/publication/claim.ts +51 -0
- package/src/publication/coordinator.ts +578 -0
- package/src/publication/github.ts +954 -0
- package/src/publication/http.ts +101 -0
- package/src/publication/index.ts +4 -0
- package/src/publication/npm.ts +525 -0
- package/src/publication/profiles.ts +94 -0
- package/src/publication/publisher.ts +130 -0
- package/src/publication/pypi.ts +463 -0
- package/src/publication/recovery.ts +349 -0
- package/src/publication/report.ts +628 -0
- package/src/recipes/config.ts +395 -0
- package/src/release/capabilities.ts +459 -0
- package/src/release/compiler.ts +28 -0
- package/src/release/config.ts +12 -0
- package/src/release/context.ts +223 -0
- package/src/release/graph.ts +866 -0
- package/src/release/inspect.ts +83 -0
- package/src/release/prepare.ts +1331 -0
- package/src/release/prepared-ref.ts +193 -0
- package/src/release/prepared-store.ts +377 -0
- package/src/release/prepared.ts +299 -0
- package/src/release/staging.ts +257 -0
- package/src/resolve/authored.ts +64 -0
- package/src/resolve/encode.ts +22 -0
- package/src/resolve/errors.ts +15 -0
- package/src/resolve/facts.ts +18 -0
- package/src/resolve/resolve.ts +328 -0
- package/src/store.ts +28 -0
- package/templates/README.md +55 -72
- package/templates/bun-cli-github/README.md +7 -9
- package/templates/bun-cli-github/release.config.json +35 -73
- package/templates/github-actions/release.yml +57 -0
- package/templates/github-actions/reviewed-release.yml +80 -0
- package/templates/multi-target-homebrew/README.md +10 -0
- package/templates/multi-target-homebrew/release.config.json +43 -60
- package/templates/multi-target-scoop/README.md +10 -0
- package/templates/multi-target-scoop/release.config.json +43 -62
- package/templates/npm-github/release.config.json +25 -38
- package/templates/npm-github/reviewed-release.config.json +37 -0
- package/templates/npm-only/README.md +10 -0
- package/templates/npm-only/release.config.json +15 -30
- package/templates/portable-cli/README.md +13 -0
- package/templates/portable-cli/release.config.json +53 -0
- package/dist/artifacts/adapter.d.ts +0 -39
- package/dist/artifacts/adapter.d.ts.map +0 -1
- package/dist/artifacts/adapter.js +0 -23
- package/dist/artifacts/adapter.js.map +0 -1
- package/dist/artifacts/registry.d.ts +0 -21
- package/dist/artifacts/registry.d.ts.map +0 -1
- package/dist/artifacts/registry.js +0 -21
- package/dist/artifacts/registry.js.map +0 -1
- package/dist/config/errors.d.ts +0 -24
- package/dist/config/errors.d.ts.map +0 -1
- package/dist/config/errors.js +0 -19
- package/dist/config/errors.js.map +0 -1
- package/dist/config/load.d.ts +0 -9
- package/dist/config/load.d.ts.map +0 -1
- package/dist/config/load.js +0 -29
- package/dist/config/load.js.map +0 -1
- package/dist/config/schema.d.ts +0 -12
- package/dist/config/schema.d.ts.map +0 -1
- package/dist/config/schema.js +0 -19
- package/dist/config/schema.js.map +0 -1
- package/dist/domain/artifact.d.ts +0 -118
- package/dist/domain/artifact.d.ts.map +0 -1
- package/dist/domain/artifact.js +0 -197
- package/dist/domain/artifact.js.map +0 -1
- package/dist/domain/evidence.d.ts +0 -59
- package/dist/domain/evidence.d.ts.map +0 -1
- package/dist/domain/evidence.js +0 -51
- package/dist/domain/evidence.js.map +0 -1
- package/dist/domain/operation.d.ts +0 -143
- package/dist/domain/operation.d.ts.map +0 -1
- package/dist/domain/operation.js +0 -198
- package/dist/domain/operation.js.map +0 -1
- package/dist/domain/release.d.ts +0 -93
- package/dist/domain/release.d.ts.map +0 -1
- package/dist/domain/release.js +0 -82
- package/dist/domain/release.js.map +0 -1
- package/dist/domain/remote-state.d.ts +0 -58
- package/dist/domain/remote-state.d.ts.map +0 -1
- package/dist/domain/remote-state.js +0 -55
- package/dist/domain/remote-state.js.map +0 -1
- package/dist/domain/target.d.ts +0 -148
- package/dist/domain/target.d.ts.map +0 -1
- package/dist/domain/target.js +0 -151
- package/dist/domain/target.js.map +0 -1
- package/dist/host/host.d.ts +0 -32
- package/dist/host/host.d.ts.map +0 -1
- package/dist/host/host.js +0 -24
- package/dist/host/host.js.map +0 -1
- package/dist/host/http-live.d.ts +0 -6
- package/dist/host/http-live.d.ts.map +0 -1
- package/dist/host/http-live.js +0 -86
- package/dist/host/http-live.js.map +0 -1
- package/dist/host/http.d.ts +0 -43
- package/dist/host/http.d.ts.map +0 -1
- package/dist/host/http.js +0 -59
- package/dist/host/http.js.map +0 -1
- package/dist/host/platform.d.ts +0 -10
- package/dist/host/platform.d.ts.map +0 -1
- package/dist/host/platform.js +0 -114
- package/dist/host/platform.js.map +0 -1
- package/dist/host/test.d.ts +0 -23
- package/dist/host/test.d.ts.map +0 -1
- package/dist/host/test.js +0 -222
- package/dist/host/test.js.map +0 -1
- package/dist/internal/workspace-path.d.ts +0 -15
- package/dist/internal/workspace-path.d.ts.map +0 -1
- package/dist/internal/workspace-path.js +0 -36
- package/dist/internal/workspace-path.js.map +0 -1
- package/dist/planner/artifact-inventory.d.ts +0 -9
- package/dist/planner/artifact-inventory.d.ts.map +0 -1
- package/dist/planner/artifact-inventory.js +0 -86
- package/dist/planner/artifact-inventory.js.map +0 -1
- package/dist/planner/create-release-plan.d.ts +0 -5
- package/dist/planner/create-release-plan.d.ts.map +0 -1
- package/dist/planner/create-release-plan.js +0 -25
- package/dist/planner/create-release-plan.js.map +0 -1
- package/dist/planner/errors.d.ts +0 -67
- package/dist/planner/errors.d.ts.map +0 -1
- package/dist/planner/errors.js +0 -58
- package/dist/planner/errors.js.map +0 -1
- package/dist/planner/evidence-recorder.d.ts +0 -28
- package/dist/planner/evidence-recorder.d.ts.map +0 -1
- package/dist/planner/evidence-recorder.js +0 -362
- package/dist/planner/evidence-recorder.js.map +0 -1
- package/dist/planner/executor.d.ts +0 -58
- package/dist/planner/executor.d.ts.map +0 -1
- package/dist/planner/executor.js +0 -147
- package/dist/planner/executor.js.map +0 -1
- package/dist/planner/normalize-release.d.ts +0 -19
- package/dist/planner/normalize-release.d.ts.map +0 -1
- package/dist/planner/normalize-release.js +0 -342
- package/dist/planner/normalize-release.js.map +0 -1
- package/dist/planner/reconcile.d.ts +0 -17
- package/dist/planner/reconcile.d.ts.map +0 -1
- package/dist/planner/reconcile.js +0 -259
- package/dist/planner/reconcile.js.map +0 -1
- package/dist/planner/render-plan.d.ts +0 -15
- package/dist/planner/render-plan.d.ts.map +0 -1
- package/dist/planner/render-plan.js +0 -231
- package/dist/planner/render-plan.js.map +0 -1
- package/dist/targets/adapter-helpers.d.ts +0 -79
- package/dist/targets/adapter-helpers.d.ts.map +0 -1
- package/dist/targets/adapter-helpers.js +0 -194
- package/dist/targets/adapter-helpers.js.map +0 -1
- package/dist/targets/adapter.d.ts +0 -17
- package/dist/targets/adapter.d.ts.map +0 -1
- package/dist/targets/adapter.js +0 -2
- package/dist/targets/adapter.js.map +0 -1
- package/dist/targets/github-release.d.ts +0 -25
- package/dist/targets/github-release.d.ts.map +0 -1
- package/dist/targets/github-release.js +0 -82
- package/dist/targets/github-release.js.map +0 -1
- package/dist/targets/github.d.ts +0 -11
- package/dist/targets/github.d.ts.map +0 -1
- package/dist/targets/github.js +0 -117
- package/dist/targets/github.js.map +0 -1
- package/dist/targets/homebrew.d.ts +0 -11
- package/dist/targets/homebrew.d.ts.map +0 -1
- package/dist/targets/homebrew.js +0 -210
- package/dist/targets/homebrew.js.map +0 -1
- package/dist/targets/live.d.ts +0 -5
- package/dist/targets/live.d.ts.map +0 -1
- package/dist/targets/live.js +0 -50
- package/dist/targets/live.js.map +0 -1
- package/dist/targets/npm.d.ts +0 -10
- package/dist/targets/npm.d.ts.map +0 -1
- package/dist/targets/npm.js +0 -112
- package/dist/targets/npm.js.map +0 -1
- package/dist/targets/pypi.d.ts +0 -11
- package/dist/targets/pypi.d.ts.map +0 -1
- package/dist/targets/pypi.js +0 -141
- package/dist/targets/pypi.js.map +0 -1
- package/dist/targets/registry.d.ts +0 -25
- package/dist/targets/registry.d.ts.map +0 -1
- package/dist/targets/registry.js +0 -34
- package/dist/targets/registry.js.map +0 -1
- package/dist/targets/scoop.d.ts +0 -11
- package/dist/targets/scoop.d.ts.map +0 -1
- package/dist/targets/scoop.js +0 -88
- package/dist/targets/scoop.js.map +0 -1
- package/dist/types/effect-internal.d.ts +0 -5
- package/dist/types/effect-internal.d.ts.map +0 -1
- package/dist/types/effect-internal.js +0 -2
- package/dist/types/effect-internal.js.map +0 -1
- package/dist/workflows/config.d.ts +0 -202
- package/dist/workflows/config.d.ts.map +0 -1
- package/dist/workflows/config.js +0 -420
- package/dist/workflows/config.js.map +0 -1
- package/dist/workflows/diagnostics.d.ts +0 -62
- package/dist/workflows/diagnostics.d.ts.map +0 -1
- package/dist/workflows/diagnostics.js +0 -530
- package/dist/workflows/diagnostics.js.map +0 -1
- package/dist/workflows/distribution.d.ts +0 -25
- package/dist/workflows/distribution.d.ts.map +0 -1
- package/dist/workflows/distribution.js +0 -24
- package/dist/workflows/distribution.js.map +0 -1
- package/dist/workflows/evidence.d.ts +0 -17
- package/dist/workflows/evidence.d.ts.map +0 -1
- package/dist/workflows/evidence.js +0 -32
- package/dist/workflows/evidence.js.map +0 -1
- package/dist/workflows/index.d.ts +0 -8
- package/dist/workflows/index.d.ts.map +0 -1
- package/dist/workflows/index.js +0 -7
- package/dist/workflows/index.js.map +0 -1
- package/dist/workflows/init.d.ts +0 -84
- package/dist/workflows/init.d.ts.map +0 -1
- package/dist/workflows/init.js +0 -487
- package/dist/workflows/init.js.map +0 -1
- package/dist/workflows/live.d.ts +0 -9
- package/dist/workflows/live.d.ts.map +0 -1
- package/dist/workflows/live.js +0 -7
- package/dist/workflows/live.js.map +0 -1
- package/dist/workflows/options.d.ts +0 -30
- package/dist/workflows/options.d.ts.map +0 -1
- package/dist/workflows/options.js +0 -16
- package/dist/workflows/options.js.map +0 -1
- package/examples/non-strict-skips/artifacts/release-example-skips-0.1.0.tgz +0 -1
- package/examples/non-strict-skips/release.config.json +0 -31
- package/examples/pypi-registry/release.config.json +0 -31
- package/templates/github-actions/plan-and-approved-execute.yml +0 -58
- package/templates/github-actions/plan-only.yml +0 -33
- package/templates/github-actions/trusted-publishing.yml +0 -61
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect"
|
|
2
|
+
import * as Schema from "effect/Schema"
|
|
3
|
+
import { encodeCanonicalJson } from "../model/canonical.js"
|
|
4
|
+
import { digestEquals, parseSha256Hex, sha256Digest, sha512Digest } from "../model/digest.js"
|
|
5
|
+
import {
|
|
6
|
+
CatalogManagedState,
|
|
7
|
+
PreparedCatalogDownload,
|
|
8
|
+
compareCatalogVersions,
|
|
9
|
+
decodeCatalogManagedState,
|
|
10
|
+
encodeCatalogManagedState,
|
|
11
|
+
renderCatalog
|
|
12
|
+
} from "../model/catalog.js"
|
|
13
|
+
import { SubjectId } from "../model/authority.js"
|
|
14
|
+
import type { CredentialProviderShape } from "../publication/authority.js"
|
|
15
|
+
import { CredentialProvider } from "../publication/authority.js"
|
|
16
|
+
import { publishReleaseSubjects } from "../publication/coordinator.js"
|
|
17
|
+
import type { AuthorizedMutationHttpShape, HttpAuthorizerShape } from "../publication/http.js"
|
|
18
|
+
import { makeCatalogSubject } from "../publication/catalog-git.js"
|
|
19
|
+
import type { ReleaseReport } from "../publication/report.js"
|
|
20
|
+
import {
|
|
21
|
+
PreparedPublication,
|
|
22
|
+
encodePreparedRelease,
|
|
23
|
+
type PreparedPublication as PreparedPublicationType
|
|
24
|
+
} from "../release/prepared.js"
|
|
25
|
+
import type { PreparedBundle } from "../release/prepared-store.js"
|
|
26
|
+
import {
|
|
27
|
+
type AuthoredCorrection,
|
|
28
|
+
CorrectionIntentV2,
|
|
29
|
+
encodeCorrectionIntent,
|
|
30
|
+
makeCorrectionIntent,
|
|
31
|
+
type CorrectionIntent,
|
|
32
|
+
type CatalogForwardCorrection,
|
|
33
|
+
type GithubReleaseCorrection,
|
|
34
|
+
type NpmDeprecationCorrection
|
|
35
|
+
} from "./intent.js"
|
|
36
|
+
|
|
37
|
+
export class CorrectionValidationError
|
|
38
|
+
extends Schema.TaggedErrorClass<CorrectionValidationError>()("CorrectionValidationError", {
|
|
39
|
+
reason: Schema.String
|
|
40
|
+
}) {}
|
|
41
|
+
|
|
42
|
+
export class CorrectionUnsupported extends Schema.TaggedClass<CorrectionUnsupported>()("CorrectionUnsupported", {
|
|
43
|
+
provider: Schema.Literals(["npm", "github", "catalog-git"]), reason: Schema.String, evidence: Schema.NonEmptyString,
|
|
44
|
+
proposal: Schema.optionalKey(Schema.String)
|
|
45
|
+
}) {}
|
|
46
|
+
|
|
47
|
+
export interface CorrectionExecuted {
|
|
48
|
+
readonly _tag: "CorrectionExecuted"
|
|
49
|
+
readonly provider: "catalog-git"
|
|
50
|
+
readonly report: ReleaseReport
|
|
51
|
+
readonly reason: string
|
|
52
|
+
readonly evidence: string
|
|
53
|
+
readonly proposal: string
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type CorrectionOutcome = typeof CorrectionUnsupported.Type | CorrectionExecuted
|
|
57
|
+
|
|
58
|
+
const preparedDigest = (bundle: PreparedBundle) => sha256Digest(encodePreparedRelease(bundle.manifest))
|
|
59
|
+
const publicationBaselineDigest = (publication: PreparedPublicationType) => sha256Digest(
|
|
60
|
+
new TextEncoder().encode(encodeCanonicalJson(Schema.encodeSync(PreparedPublication)(publication)))
|
|
61
|
+
)
|
|
62
|
+
const findPublication = (bundle: PreparedBundle, id: string) => bundle.manifest.publications.find((publication) => publication.id.toString() === id)
|
|
63
|
+
|
|
64
|
+
const choosePublication = <Tag extends "PreparedNpmPublication" | "PreparedGitHubPublication" | "PreparedCatalogPublication">(
|
|
65
|
+
bundle: PreparedBundle,
|
|
66
|
+
tag: Tag,
|
|
67
|
+
publicationId?: { readonly toString: () => string }
|
|
68
|
+
): Extract<PreparedBundle["manifest"]["publications"][number], { readonly _tag: Tag }> => {
|
|
69
|
+
const candidates = bundle.manifest.publications.filter((publication): publication is Extract<
|
|
70
|
+
PreparedBundle["manifest"]["publications"][number],
|
|
71
|
+
{ readonly _tag: Tag }
|
|
72
|
+
> => publication._tag === tag && (
|
|
73
|
+
publicationId === undefined || publication.id.toString() === publicationId.toString()
|
|
74
|
+
))
|
|
75
|
+
if (candidates.length !== 1) {
|
|
76
|
+
throw new Error(publicationId === undefined
|
|
77
|
+
? `Authored correction requires exactly one ${tag === "PreparedNpmPublication" ? "npm" : tag === "PreparedGitHubPublication" ? "GitHub" : "catalog Git"} publication; specify publicationId when more than one exists.`
|
|
78
|
+
: `Authored correction publicationId ${publicationId} does not identify exactly one prepared ${tag === "PreparedNpmPublication" ? "npm" : tag === "PreparedGitHubPublication" ? "GitHub" : "catalog Git"} publication.`)
|
|
79
|
+
}
|
|
80
|
+
return candidates[0]!
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Resolve human intent only after the exact prepared bundle has been loaded. */
|
|
84
|
+
export const bindAuthoredCorrection = (
|
|
85
|
+
bundle: PreparedBundle,
|
|
86
|
+
authored: AuthoredCorrection
|
|
87
|
+
): CorrectionIntent => {
|
|
88
|
+
const digest = preparedDigest(bundle)
|
|
89
|
+
if (authored.provider === "npm") {
|
|
90
|
+
const publication = choosePublication(bundle, "PreparedNpmPublication", authored.publicationId)
|
|
91
|
+
const artifact = bundle.manifest.artifacts.find((candidate) => candidate.id === publication.artifactId)
|
|
92
|
+
const bytes = artifact === undefined ? undefined : bundle.blobs.get(artifact.id.toString())
|
|
93
|
+
if (bytes === undefined) throw new Error("Prepared npm correction subject has no verified tarball bytes.")
|
|
94
|
+
return makeCorrectionIntent({
|
|
95
|
+
schemaVersion: "correction-intent/v2",
|
|
96
|
+
preparedDigest: digest,
|
|
97
|
+
correction: {
|
|
98
|
+
_tag: "NpmDeprecationCorrection",
|
|
99
|
+
provider: "npm",
|
|
100
|
+
publicationId: publication.id,
|
|
101
|
+
registryUrl: publication.registryUrl,
|
|
102
|
+
packageName: publication.packageName,
|
|
103
|
+
version: publication.version,
|
|
104
|
+
baselineDigest: publicationBaselineDigest(publication),
|
|
105
|
+
tarballIntegrity: sha512Digest(bytes),
|
|
106
|
+
message: authored.message,
|
|
107
|
+
...(authored.replacement === undefined ? {} : { replacement: authored.replacement })
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
if (authored.provider === "catalog-git") {
|
|
112
|
+
const publication = choosePublication(bundle, "PreparedCatalogPublication", authored.publicationId)
|
|
113
|
+
if (compareCatalogVersions(publication.version.toString(), authored.replacementVersion.toString()) >= 0) {
|
|
114
|
+
throw new Error("Catalog correction replacementVersion must be SemVer-greater than the prepared generation.")
|
|
115
|
+
}
|
|
116
|
+
const expectedArchitectures = publication.renderer.downloads.map((download) => download.architecture).sort()
|
|
117
|
+
const observedArchitectures = authored.downloads.map((download) => download.architecture).sort()
|
|
118
|
+
if (new Set(observedArchitectures).size !== observedArchitectures.length ||
|
|
119
|
+
expectedArchitectures.join("\n") !== observedArchitectures.join("\n")) {
|
|
120
|
+
throw new Error("Catalog correction downloads must replace the exact prepared architecture set.")
|
|
121
|
+
}
|
|
122
|
+
const downloads = authored.downloads.map((download) => {
|
|
123
|
+
const expectedUrl = `https://github.com/${publication.sourceRepository}/releases/download/${encodeURIComponent(authored.replacementTag)}/${encodeURIComponent(download.filename)}`
|
|
124
|
+
if (download.url !== expectedUrl) {
|
|
125
|
+
throw new Error("Catalog correction download URL must be the exact replacement GitHub release asset coordinate.")
|
|
126
|
+
}
|
|
127
|
+
return PreparedCatalogDownload.make({
|
|
128
|
+
architecture: download.architecture,
|
|
129
|
+
url: download.url,
|
|
130
|
+
filename: download.filename,
|
|
131
|
+
sha256: parseSha256Hex(download.sha256)
|
|
132
|
+
})
|
|
133
|
+
}) as [PreparedCatalogDownload, ...Array<PreparedCatalogDownload>]
|
|
134
|
+
const target = bundle.blobs.get(publication.targetArtifactId.toString())
|
|
135
|
+
const state = bundle.blobs.get(publication.stateArtifactId.toString())
|
|
136
|
+
if (target === undefined || state === undefined ||
|
|
137
|
+
!digestEquals(sha256Digest(target), publication.targetDigest) ||
|
|
138
|
+
!digestEquals(sha256Digest(state), publication.stateDigest)) {
|
|
139
|
+
throw new Error("Prepared catalog correction subject has no exact target/state baseline bytes.")
|
|
140
|
+
}
|
|
141
|
+
const managed = decodeCatalogManagedState(state)
|
|
142
|
+
if (managed === undefined || managed.status !== "active" || managed.generation !== publication.version ||
|
|
143
|
+
!digestEquals(managed.targetDigest, publication.targetDigest)) {
|
|
144
|
+
throw new Error("Prepared catalog correction baseline is not one canonical active managed pair.")
|
|
145
|
+
}
|
|
146
|
+
return makeCorrectionIntent({
|
|
147
|
+
schemaVersion: "correction-intent/v2",
|
|
148
|
+
preparedDigest: digest,
|
|
149
|
+
correction: {
|
|
150
|
+
_tag: "CatalogForwardCorrection",
|
|
151
|
+
provider: "catalog-git",
|
|
152
|
+
publicationId: publication.id,
|
|
153
|
+
baselineDigest: publicationBaselineDigest(publication),
|
|
154
|
+
baselineTargetDigest: publication.targetDigest,
|
|
155
|
+
baselineStateDigest: publication.stateDigest,
|
|
156
|
+
replacementVersion: authored.replacementVersion,
|
|
157
|
+
replacementTag: authored.replacementTag,
|
|
158
|
+
downloads,
|
|
159
|
+
reason: authored.reason
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
const publication = choosePublication(bundle, "PreparedGitHubPublication", authored.publicationId)
|
|
164
|
+
return makeCorrectionIntent({
|
|
165
|
+
schemaVersion: "correction-intent/v2",
|
|
166
|
+
preparedDigest: digest,
|
|
167
|
+
correction: {
|
|
168
|
+
_tag: "GithubReleaseCorrection",
|
|
169
|
+
provider: "github",
|
|
170
|
+
publicationId: publication.id,
|
|
171
|
+
repository: publication.repository,
|
|
172
|
+
tag: publication.tag,
|
|
173
|
+
baselineDigest: publicationBaselineDigest(publication),
|
|
174
|
+
marker: authored.message,
|
|
175
|
+
...(authored.replacement === undefined ? {} : { replacement: authored.replacement })
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const verifyNpm = (bundle: PreparedBundle, correction: NpmDeprecationCorrection): void => {
|
|
181
|
+
const publication = findPublication(bundle, correction.publicationId.toString())
|
|
182
|
+
if (publication?._tag !== "PreparedNpmPublication" || publication.registryUrl !== correction.registryUrl ||
|
|
183
|
+
publication.packageName !== correction.packageName || publication.version !== correction.version) {
|
|
184
|
+
throw new Error("npm correction subject is not the exact npm publication in the prepared manifest.")
|
|
185
|
+
}
|
|
186
|
+
if (!digestEquals(publicationBaselineDigest(publication), correction.baselineDigest)) {
|
|
187
|
+
throw new Error("npm correction baseline digest is not the exact prepared publication digest.")
|
|
188
|
+
}
|
|
189
|
+
const artifact = bundle.manifest.artifacts.find((candidate) => candidate.id === publication.artifactId)
|
|
190
|
+
const bytes = artifact === undefined ? undefined : bundle.blobs.get(artifact.id.toString())
|
|
191
|
+
if (bytes === undefined) throw new Error("npm correction subject has no verified prepared tarball.")
|
|
192
|
+
const integrity = sha512Digest(bytes)
|
|
193
|
+
if (!digestEquals(integrity, correction.tarballIntegrity)) {
|
|
194
|
+
throw new Error("npm correction tarball integrity is not the prepared artifact integrity.")
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const verifyGithub = (bundle: PreparedBundle, correction: GithubReleaseCorrection): void => {
|
|
199
|
+
const publication = findPublication(bundle, correction.publicationId.toString())
|
|
200
|
+
if (publication?._tag !== "PreparedGitHubPublication" || publication.repository !== correction.repository || publication.tag !== correction.tag) {
|
|
201
|
+
throw new Error("GitHub correction subject is not the exact GitHub publication in the prepared manifest.")
|
|
202
|
+
}
|
|
203
|
+
if (!digestEquals(publicationBaselineDigest(publication), correction.baselineDigest)) {
|
|
204
|
+
throw new Error("GitHub correction baseline digest is not the exact prepared publication digest.")
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const verifyCatalog = (bundle: PreparedBundle, correction: CatalogForwardCorrection): void => {
|
|
209
|
+
const publication = findPublication(bundle, correction.publicationId.toString())
|
|
210
|
+
if (publication?._tag !== "PreparedCatalogPublication") {
|
|
211
|
+
throw new Error("Catalog correction subject is not the exact catalog publication in the prepared manifest.")
|
|
212
|
+
}
|
|
213
|
+
if (!digestEquals(publicationBaselineDigest(publication), correction.baselineDigest) ||
|
|
214
|
+
!digestEquals(publication.targetDigest, correction.baselineTargetDigest) ||
|
|
215
|
+
!digestEquals(publication.stateDigest, correction.baselineStateDigest)) {
|
|
216
|
+
throw new Error("Catalog correction baseline digest is not the exact prepared target/state publication.")
|
|
217
|
+
}
|
|
218
|
+
if (compareCatalogVersions(publication.version.toString(), correction.replacementVersion.toString()) >= 0) {
|
|
219
|
+
throw new Error("Catalog correction is not a SemVer-forward replacement.")
|
|
220
|
+
}
|
|
221
|
+
const expectedArchitectures = publication.renderer.downloads.map((download) => download.architecture).sort().join("\n")
|
|
222
|
+
const replacementArchitectures = correction.downloads.map((download) => download.architecture).sort().join("\n")
|
|
223
|
+
if (expectedArchitectures !== replacementArchitectures) {
|
|
224
|
+
throw new Error("Catalog correction replacement architecture set differs from the prepared renderer.")
|
|
225
|
+
}
|
|
226
|
+
for (const download of correction.downloads) {
|
|
227
|
+
const expected = `https://github.com/${publication.sourceRepository}/releases/download/${encodeURIComponent(correction.replacementTag)}/${encodeURIComponent(download.filename)}`
|
|
228
|
+
if (download.url !== expected) throw new Error("Catalog correction carries a foreign replacement download coordinate.")
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export const verifyCorrectionIntent = (bundle: PreparedBundle, intent: CorrectionIntent): void => {
|
|
233
|
+
encodeCorrectionIntent(intent)
|
|
234
|
+
if (!digestEquals(preparedDigest(bundle), intent.preparedDigest)) {
|
|
235
|
+
throw new Error("Correction intent is bound to a different prepared release.")
|
|
236
|
+
}
|
|
237
|
+
switch (intent.correction._tag) {
|
|
238
|
+
case "NpmDeprecationCorrection": return verifyNpm(bundle, intent.correction)
|
|
239
|
+
case "GithubReleaseCorrection": return verifyGithub(bundle, intent.correction)
|
|
240
|
+
case "CatalogForwardCorrection": return verifyCatalog(bundle, intent.correction)
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const providerOf = (intent: CorrectionIntent): "npm" | "github" | "catalog-git" => intent.correction.provider
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* The only correction entry point. Provider adapters are built at the host
|
|
248
|
+
* boundary. Neither admitted provider currently exposes a conditional write
|
|
249
|
+
* that protects the exact observed generation, so a validated intent becomes
|
|
250
|
+
* an external proposal and never a read-then-unconditional mutation.
|
|
251
|
+
*/
|
|
252
|
+
export const correctPreparedRelease = Effect.fn("correctPreparedRelease")(function*(input: {
|
|
253
|
+
readonly bundle: PreparedBundle
|
|
254
|
+
readonly intent: CorrectionIntent
|
|
255
|
+
readonly services?: {
|
|
256
|
+
readonly credentials: CredentialProviderShape
|
|
257
|
+
readonly http: HttpAuthorizerShape
|
|
258
|
+
readonly mutationHttp: AuthorizedMutationHttpShape
|
|
259
|
+
}
|
|
260
|
+
}) {
|
|
261
|
+
try {
|
|
262
|
+
verifyCorrectionIntent(input.bundle, input.intent)
|
|
263
|
+
} catch (cause) {
|
|
264
|
+
return yield* Effect.fail(new CorrectionValidationError({ reason: cause instanceof Error ? cause.message : String(cause) }))
|
|
265
|
+
}
|
|
266
|
+
const provider = providerOf(input.intent)
|
|
267
|
+
if (input.intent.correction._tag === "CatalogForwardCorrection") {
|
|
268
|
+
if (input.services === undefined) return CorrectionUnsupported.make({
|
|
269
|
+
provider,
|
|
270
|
+
reason: "Catalog correction requires the host-owned credential and GitHub HTTP authority boundary.",
|
|
271
|
+
evidence: "docs/release-program/remediation/231-catalog-delivery.md",
|
|
272
|
+
proposal: new TextDecoder().decode(encodeCorrectionIntent(input.intent))
|
|
273
|
+
})
|
|
274
|
+
const correction = input.intent.correction
|
|
275
|
+
const publication = findPublication(input.bundle, correction.publicationId.toString())
|
|
276
|
+
if (publication?._tag !== "PreparedCatalogPublication") {
|
|
277
|
+
return yield* new CorrectionValidationError({ reason: "Verified catalog correction publication disappeared." })
|
|
278
|
+
}
|
|
279
|
+
const baselineTarget = input.bundle.blobs.get(publication.targetArtifactId.toString())
|
|
280
|
+
const baselineState = input.bundle.blobs.get(publication.stateArtifactId.toString())
|
|
281
|
+
if (baselineTarget === undefined || baselineState === undefined) {
|
|
282
|
+
return yield* new CorrectionValidationError({ reason: "Verified catalog correction baseline bytes disappeared." })
|
|
283
|
+
}
|
|
284
|
+
const target = renderCatalog(correction.replacementVersion, publication.renderer.renderer, correction.downloads)
|
|
285
|
+
const state = encodeCatalogManagedState(CatalogManagedState.make({
|
|
286
|
+
schemaVersion: "ts-release/catalog-state/v2",
|
|
287
|
+
catalogId: publication.catalogId,
|
|
288
|
+
renderer: publication.renderer.renderer._tag,
|
|
289
|
+
generation: correction.replacementVersion,
|
|
290
|
+
status: "corrected",
|
|
291
|
+
targetDigest: sha256Digest(target),
|
|
292
|
+
sourceRepository: publication.sourceRepository,
|
|
293
|
+
sourceTag: correction.replacementTag,
|
|
294
|
+
correctionId: input.intent.correctionId,
|
|
295
|
+
reason: correction.reason,
|
|
296
|
+
replacementVersion: correction.replacementVersion
|
|
297
|
+
}))
|
|
298
|
+
const subject = makeCatalogSubject(publication, input.services.http, input.services.mutationHttp, {
|
|
299
|
+
id: SubjectId.make(`${publication.authority.subject}#correction-${input.intent.correctionId.hex}`),
|
|
300
|
+
purpose: "correct",
|
|
301
|
+
target,
|
|
302
|
+
state,
|
|
303
|
+
baselineTarget,
|
|
304
|
+
baselineState
|
|
305
|
+
})
|
|
306
|
+
const report = yield* publishReleaseSubjects({
|
|
307
|
+
prepared: SubjectId.make(`correction:sha256-${input.intent.correctionId.hex}`),
|
|
308
|
+
subjects: [subject]
|
|
309
|
+
}).pipe(Effect.provideService(CredentialProvider, input.services.credentials))
|
|
310
|
+
return {
|
|
311
|
+
_tag: "CorrectionExecuted",
|
|
312
|
+
provider: "catalog-git",
|
|
313
|
+
report,
|
|
314
|
+
reason: "Catalog correction ran through the exact conditional Git-data subject.",
|
|
315
|
+
evidence: "docs/release-program/remediation/231-catalog-delivery.md",
|
|
316
|
+
proposal: ""
|
|
317
|
+
} as const
|
|
318
|
+
}
|
|
319
|
+
return CorrectionUnsupported.make({
|
|
320
|
+
provider,
|
|
321
|
+
reason: provider === "npm"
|
|
322
|
+
? "npm exposes no proved conditional deprecation write for the observed package generation; use the exact external proposal instead."
|
|
323
|
+
: "GitHub exposes no proved conditional release-metadata write for the observed release generation.",
|
|
324
|
+
evidence: "docs/release-program/remediation/229-provider-recovery.md",
|
|
325
|
+
proposal: new TextDecoder().decode(encodeCorrectionIntent(input.intent))
|
|
326
|
+
})
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
export const correctionEvidenceProjection = (intent: CorrectionIntent): string => {
|
|
330
|
+
const encoded = Schema.encodeSync(CorrectionIntentV2)(intent)
|
|
331
|
+
return encodeCanonicalJson({
|
|
332
|
+
correctionId: encoded.correctionId,
|
|
333
|
+
preparedDigest: encoded.preparedDigest,
|
|
334
|
+
provider: providerOf(intent)
|
|
335
|
+
})
|
|
336
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema"
|
|
2
|
+
import { encodeCanonicalJson, parseStrictJson } from "../model/canonical.js"
|
|
3
|
+
import {
|
|
4
|
+
Sha256Digest,
|
|
5
|
+
Sha256Hex,
|
|
6
|
+
Sha512Digest,
|
|
7
|
+
digestEquals,
|
|
8
|
+
sha256Digest
|
|
9
|
+
} from "../model/digest.js"
|
|
10
|
+
import { NonEmptyName, Version } from "../model/primitives.js"
|
|
11
|
+
import { CatalogArchitecture, PreparedCatalogDownload } from "../model/catalog.js"
|
|
12
|
+
|
|
13
|
+
const optional = Schema.optionalKey
|
|
14
|
+
const boundedText = Schema.String.check(Schema.makeFilter((value: string) =>
|
|
15
|
+
[...value].length <= 2048 && [...value].every((character) => {
|
|
16
|
+
const codePoint = character.codePointAt(0) ?? 0
|
|
17
|
+
return codePoint === 9 || codePoint === 10 || codePoint === 13 || codePoint >= 0x20
|
|
18
|
+
}) ? undefined : "Correction text must be bounded and contain no control characters."))
|
|
19
|
+
const publicMessage = boundedText.pipe(Schema.check(Schema.makeFilter((value: string) =>
|
|
20
|
+
value.length > 0 ? undefined : "Correction message must be nonempty.")))
|
|
21
|
+
|
|
22
|
+
export class ReplacementCoordinate extends Schema.Class<ReplacementCoordinate>("ReplacementCoordinate")({
|
|
23
|
+
provider: Schema.Literals(["npm", "github"]), coordinate: publicMessage
|
|
24
|
+
}) {}
|
|
25
|
+
|
|
26
|
+
export class NpmDeprecationCorrection extends Schema.TaggedClass<NpmDeprecationCorrection>()("NpmDeprecationCorrection", {
|
|
27
|
+
provider: Schema.Literal("npm"), publicationId: NonEmptyName, registryUrl: Schema.NonEmptyString,
|
|
28
|
+
packageName: NonEmptyName, version: Version, baselineDigest: Sha256Digest, tarballIntegrity: Sha512Digest,
|
|
29
|
+
message: publicMessage, replacement: optional(ReplacementCoordinate)
|
|
30
|
+
}) {}
|
|
31
|
+
|
|
32
|
+
export class GithubReleaseCorrection extends Schema.TaggedClass<GithubReleaseCorrection>()("GithubReleaseCorrection", {
|
|
33
|
+
provider: Schema.Literal("github"), publicationId: NonEmptyName, repository: Schema.NonEmptyString,
|
|
34
|
+
tag: NonEmptyName, baselineDigest: Sha256Digest, marker: publicMessage,
|
|
35
|
+
replacement: optional(ReplacementCoordinate)
|
|
36
|
+
}) {}
|
|
37
|
+
|
|
38
|
+
export class CatalogForwardCorrection extends Schema.TaggedClass<CatalogForwardCorrection>()("CatalogForwardCorrection", {
|
|
39
|
+
provider: Schema.Literal("catalog-git"),
|
|
40
|
+
publicationId: NonEmptyName,
|
|
41
|
+
baselineDigest: Sha256Digest,
|
|
42
|
+
baselineTargetDigest: Sha256Digest,
|
|
43
|
+
baselineStateDigest: Sha256Digest,
|
|
44
|
+
replacementVersion: Version,
|
|
45
|
+
replacementTag: NonEmptyName,
|
|
46
|
+
downloads: Schema.NonEmptyArray(PreparedCatalogDownload),
|
|
47
|
+
reason: publicMessage
|
|
48
|
+
}) {}
|
|
49
|
+
|
|
50
|
+
/** Human-authored correction request. Destination and immutable subject facts
|
|
51
|
+
* are deliberately absent: they are derived from the loaded prepared bundle. */
|
|
52
|
+
export class AuthoredNpmDeprecation
|
|
53
|
+
extends Schema.Class<AuthoredNpmDeprecation>("AuthoredNpmDeprecation")({
|
|
54
|
+
provider: Schema.Literal("npm"),
|
|
55
|
+
kind: Schema.Literal("deprecate"),
|
|
56
|
+
publicationId: optional(NonEmptyName),
|
|
57
|
+
message: publicMessage,
|
|
58
|
+
replacement: optional(ReplacementCoordinate)
|
|
59
|
+
}) {}
|
|
60
|
+
|
|
61
|
+
export class AuthoredGithubReleaseAmendment
|
|
62
|
+
extends Schema.Class<AuthoredGithubReleaseAmendment>("AuthoredGithubReleaseAmendment")({
|
|
63
|
+
provider: Schema.Literal("github"),
|
|
64
|
+
kind: Schema.Literal("amend-release-metadata"),
|
|
65
|
+
publicationId: optional(NonEmptyName),
|
|
66
|
+
message: publicMessage,
|
|
67
|
+
replacement: optional(ReplacementCoordinate)
|
|
68
|
+
}) {}
|
|
69
|
+
|
|
70
|
+
export class AuthoredCatalogDownload extends Schema.Class<AuthoredCatalogDownload>("AuthoredCatalogDownload")({
|
|
71
|
+
architecture: CatalogArchitecture,
|
|
72
|
+
url: Schema.NonEmptyString,
|
|
73
|
+
filename: NonEmptyName,
|
|
74
|
+
sha256: Sha256Hex
|
|
75
|
+
}) {}
|
|
76
|
+
|
|
77
|
+
export class AuthoredCatalogForwardCorrection
|
|
78
|
+
extends Schema.Class<AuthoredCatalogForwardCorrection>("AuthoredCatalogForwardCorrection")({
|
|
79
|
+
provider: Schema.Literal("catalog-git"),
|
|
80
|
+
kind: Schema.Literal("forward-catalog-state"),
|
|
81
|
+
publicationId: optional(NonEmptyName),
|
|
82
|
+
replacementVersion: Version,
|
|
83
|
+
replacementTag: NonEmptyName,
|
|
84
|
+
downloads: Schema.NonEmptyArray(AuthoredCatalogDownload),
|
|
85
|
+
reason: publicMessage
|
|
86
|
+
}) {}
|
|
87
|
+
|
|
88
|
+
export const AuthoredCorrection = Schema.Union([
|
|
89
|
+
AuthoredNpmDeprecation,
|
|
90
|
+
AuthoredGithubReleaseAmendment,
|
|
91
|
+
AuthoredCatalogForwardCorrection
|
|
92
|
+
])
|
|
93
|
+
export type AuthoredCorrection = typeof AuthoredCorrection.Type
|
|
94
|
+
|
|
95
|
+
export const decodeAuthoredCorrection = Schema.decodeUnknownSync(AuthoredCorrection, {
|
|
96
|
+
onExcessProperty: "error"
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
export const CorrectionVariant = Schema.Union([
|
|
100
|
+
NpmDeprecationCorrection, GithubReleaseCorrection, CatalogForwardCorrection
|
|
101
|
+
])
|
|
102
|
+
export type CorrectionVariant = typeof CorrectionVariant.Type
|
|
103
|
+
|
|
104
|
+
const CorrectionIntentUnsignedV2 = Schema.Struct({
|
|
105
|
+
schemaVersion: Schema.Literal("correction-intent/v2"),
|
|
106
|
+
preparedDigest: Sha256Digest,
|
|
107
|
+
correction: CorrectionVariant
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
export class CorrectionIntentV2 extends Schema.Class<CorrectionIntentV2>("CorrectionIntentV2")({
|
|
111
|
+
schemaVersion: Schema.Literal("correction-intent/v2"), preparedDigest: Sha256Digest,
|
|
112
|
+
correction: CorrectionVariant, correctionId: Sha256Digest
|
|
113
|
+
}) {}
|
|
114
|
+
export type CorrectionIntent = typeof CorrectionIntentV2.Type
|
|
115
|
+
export type CorrectionIntentInput = Omit<CorrectionIntent, "correctionId">
|
|
116
|
+
|
|
117
|
+
export class CorrectionIntentError
|
|
118
|
+
extends Schema.TaggedErrorClass<CorrectionIntentError>()("CorrectionIntentError", {
|
|
119
|
+
reason: Schema.String
|
|
120
|
+
}) {}
|
|
121
|
+
|
|
122
|
+
const normalizeCorrection = (value: unknown): CorrectionVariant => Schema.decodeUnknownSync(CorrectionVariant)(value)
|
|
123
|
+
const normalizeUnsigned = (value: CorrectionIntentInput): CorrectionIntentInput =>
|
|
124
|
+
Schema.decodeUnknownSync(CorrectionIntentUnsignedV2, { onExcessProperty: "error" })({
|
|
125
|
+
schemaVersion: value.schemaVersion,
|
|
126
|
+
preparedDigest: value.preparedDigest,
|
|
127
|
+
correction: normalizeCorrection(value.correction)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
const unsignedBytes = (value: CorrectionIntentInput): Uint8Array => {
|
|
131
|
+
const normalized = normalizeUnsigned(value)
|
|
132
|
+
const encoded = Schema.encodeSync(CorrectionIntentUnsignedV2)(normalized)
|
|
133
|
+
return new TextEncoder().encode(encodeCanonicalJson(encoded))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export const correctionIdFor = (value: CorrectionIntentInput): Sha256Digest =>
|
|
137
|
+
sha256Digest(unsignedBytes(value))
|
|
138
|
+
|
|
139
|
+
export const makeCorrectionIntent = (value: CorrectionIntentInput): CorrectionIntent => {
|
|
140
|
+
const normalized = normalizeUnsigned(value)
|
|
141
|
+
const correctionId = correctionIdFor(normalized)
|
|
142
|
+
return CorrectionIntentV2.make({ ...normalized, correctionId })
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const equal = (left: Uint8Array, right: Uint8Array): boolean =>
|
|
146
|
+
left.length === right.length && left.every((byte, index) => byte === right[index])
|
|
147
|
+
|
|
148
|
+
export const encodeCorrectionIntent = (value: CorrectionIntent): Uint8Array => {
|
|
149
|
+
try {
|
|
150
|
+
const normalized = Schema.decodeUnknownSync(CorrectionIntentV2, { onExcessProperty: "error" })(value)
|
|
151
|
+
const expected = correctionIdFor(normalized)
|
|
152
|
+
if (!digestEquals(expected, normalized.correctionId)) {
|
|
153
|
+
throw new Error("Correction id does not match canonical unsigned V2 intent bytes.")
|
|
154
|
+
}
|
|
155
|
+
return new TextEncoder().encode(encodeCanonicalJson(Schema.encodeSync(CorrectionIntentV2)(normalized)))
|
|
156
|
+
} catch (cause) {
|
|
157
|
+
throw CorrectionIntentError.make({ reason: cause instanceof Error ? cause.message : String(cause) })
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export const decodeCorrectionIntent = (bytes: Uint8Array): CorrectionIntent => {
|
|
162
|
+
try {
|
|
163
|
+
const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes)
|
|
164
|
+
const value = Schema.decodeUnknownSync(CorrectionIntentV2, { onExcessProperty: "error" })(parseStrictJson(text))
|
|
165
|
+
const canonical = encodeCorrectionIntent(value)
|
|
166
|
+
if (!equal(canonical, bytes)) throw new Error("Correction intent bytes are not canonical.")
|
|
167
|
+
return value
|
|
168
|
+
} catch (cause) {
|
|
169
|
+
if (cause instanceof CorrectionIntentError) throw cause
|
|
170
|
+
throw CorrectionIntentError.make({ reason: cause instanceof Error ? cause.message : String(cause) })
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { gzipSync } from "node:zlib"
|
|
2
|
+
|
|
3
|
+
export interface ArchiveEntry {
|
|
4
|
+
readonly path: string
|
|
5
|
+
readonly data: Uint8Array
|
|
6
|
+
readonly mode: number
|
|
7
|
+
}
|
|
8
|
+
const text = (value: string) => new TextEncoder().encode(value)
|
|
9
|
+
const concat = (parts: ReadonlyArray<Uint8Array>): Uint8Array => {
|
|
10
|
+
const output = new Uint8Array(parts.reduce((total, part) => total + part.length, 0))
|
|
11
|
+
let offset = 0
|
|
12
|
+
for (const part of parts) {
|
|
13
|
+
output.set(part, offset)
|
|
14
|
+
offset += part.length
|
|
15
|
+
}
|
|
16
|
+
return output
|
|
17
|
+
}
|
|
18
|
+
const integer = (bytes: number, value: number): Uint8Array => {
|
|
19
|
+
const output = new Uint8Array(bytes)
|
|
20
|
+
const view = new DataView(output.buffer)
|
|
21
|
+
if (bytes === 2) view.setUint16(0, value, true)
|
|
22
|
+
else view.setUint32(0, value, true)
|
|
23
|
+
return output
|
|
24
|
+
}
|
|
25
|
+
const crcTable = Array.from({ length: 256 }, (_, value) => {
|
|
26
|
+
let crc = value
|
|
27
|
+
for (const bit of Array.from({ length: 8 }, (_, index) => index)) {
|
|
28
|
+
crc = (crc & 1) === 1 ? 0xedb88320 ^ (crc >>> 1) : crc >>> 1
|
|
29
|
+
}
|
|
30
|
+
return crc >>> 0
|
|
31
|
+
})
|
|
32
|
+
const crc32 = (data: Uint8Array): number => {
|
|
33
|
+
let crc = 0xffffffff
|
|
34
|
+
for (const byte of data) crc = (crc >>> 8) ^ crcTable[(crc ^ byte) & 0xff]!
|
|
35
|
+
return (crc ^ 0xffffffff) >>> 0
|
|
36
|
+
}
|
|
37
|
+
export const zip = (entries: ReadonlyArray<ArchiveEntry>): Uint8Array => {
|
|
38
|
+
const bodies: Array<Uint8Array> = []
|
|
39
|
+
const central: Array<Uint8Array> = []
|
|
40
|
+
let offset = 0
|
|
41
|
+
for (const entry of entries) {
|
|
42
|
+
const name = text(entry.path)
|
|
43
|
+
const crc = crc32(entry.data)
|
|
44
|
+
const local = concat([
|
|
45
|
+
integer(4, 0x04034b50), integer(2, 20), integer(2, 0x0800), integer(2, 0),
|
|
46
|
+
integer(2, 0), integer(2, 0), integer(4, crc), integer(4, entry.data.length),
|
|
47
|
+
integer(4, entry.data.length), integer(2, name.length), integer(2, 0), name
|
|
48
|
+
])
|
|
49
|
+
bodies.push(local, entry.data)
|
|
50
|
+
central.push(concat([
|
|
51
|
+
integer(4, 0x02014b50), integer(2, 0x0314), integer(2, 20), integer(2, 0x0800),
|
|
52
|
+
integer(2, 0), integer(2, 0), integer(2, 0), integer(4, crc),
|
|
53
|
+
integer(4, entry.data.length), integer(4, entry.data.length), integer(2, name.length),
|
|
54
|
+
integer(2, 0), integer(2, 0), integer(2, 0), integer(2, 0),
|
|
55
|
+
integer(4, entry.mode << 16), integer(4, offset), name
|
|
56
|
+
]))
|
|
57
|
+
offset += local.length + entry.data.length
|
|
58
|
+
}
|
|
59
|
+
const directory = concat(central)
|
|
60
|
+
return concat([
|
|
61
|
+
...bodies, directory, integer(4, 0x06054b50), integer(2, 0), integer(2, 0),
|
|
62
|
+
integer(2, entries.length), integer(2, entries.length), integer(4, directory.length),
|
|
63
|
+
integer(4, offset), integer(2, 0)
|
|
64
|
+
])
|
|
65
|
+
}
|
|
66
|
+
const ascii = (output: Uint8Array, offset: number, length: number, value: string): void => {
|
|
67
|
+
const bytes = text(value)
|
|
68
|
+
if (bytes.length > length) throw new Error(`Archive path is too long: ${value}`)
|
|
69
|
+
output.set(bytes, offset)
|
|
70
|
+
}
|
|
71
|
+
const octal = (output: Uint8Array, offset: number, length: number, value: number): void => {
|
|
72
|
+
ascii(output, offset, length - 2, value.toString(8).padStart(length - 2, "0"))
|
|
73
|
+
output[offset + length - 2] = 0
|
|
74
|
+
output[offset + length - 1] = 0x20
|
|
75
|
+
}
|
|
76
|
+
const tarHeader = (entry: ArchiveEntry): Uint8Array => {
|
|
77
|
+
const header = new Uint8Array(512)
|
|
78
|
+
ascii(header, 0, 100, entry.path)
|
|
79
|
+
octal(header, 100, 8, entry.mode & 0o777)
|
|
80
|
+
octal(header, 108, 8, 0)
|
|
81
|
+
octal(header, 116, 8, 0)
|
|
82
|
+
octal(header, 124, 12, entry.data.length)
|
|
83
|
+
octal(header, 136, 12, 0)
|
|
84
|
+
header.fill(0x20, 148, 156)
|
|
85
|
+
header[156] = 0x30
|
|
86
|
+
ascii(header, 257, 6, "ustar")
|
|
87
|
+
ascii(header, 263, 2, "00")
|
|
88
|
+
octal(header, 148, 8, header.reduce((sum, byte) => sum + byte, 0))
|
|
89
|
+
return header
|
|
90
|
+
}
|
|
91
|
+
// Level 9 is explicit because it is the level at which every zlib we ship on
|
|
92
|
+
// emits identical deflate bytes; the 10-byte header is then overwritten so no
|
|
93
|
+
// clock (MTIME) or platform (OS) leaks into an artifact.
|
|
94
|
+
export const tarGz = (entries: ReadonlyArray<ArchiveEntry>): Uint8Array => {
|
|
95
|
+
const packed = new Uint8Array(gzipSync(concat(entries.flatMap((entry) => [
|
|
96
|
+
tarHeader(entry), entry.data,
|
|
97
|
+
new Uint8Array(entry.data.length % 512 === 0 ? 0 : 512 - entry.data.length % 512)
|
|
98
|
+
]).concat([new Uint8Array(1024)])), { level: 9 }))
|
|
99
|
+
packed.fill(0, 4, 8)
|
|
100
|
+
packed[9] = 0xff
|
|
101
|
+
return packed
|
|
102
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { realpathSync } from "node:fs"
|
|
2
|
+
import { isAbsolute, relative, sep } from "node:path"
|
|
3
|
+
|
|
4
|
+
// Lexical: is `path` at or beneath `root`? Both must already be in the same
|
|
5
|
+
// canonical form (use assertContained with realpath when symlinks may be
|
|
6
|
+
// present).
|
|
7
|
+
export const contained = (root: string, path: string): boolean => {
|
|
8
|
+
const value = relative(root, path)
|
|
9
|
+
return value === "" || (value !== ".." && !value.startsWith(`..${sep}`) && !isAbsolute(value))
|
|
10
|
+
}
|
|
11
|
+
export const assertContained = (
|
|
12
|
+
root: string, path: string,
|
|
13
|
+
options: { readonly realpath: boolean, readonly what: string }
|
|
14
|
+
): string => {
|
|
15
|
+
const candidate = options.realpath ? realpathSync(path) : path
|
|
16
|
+
if (!contained(options.realpath ? realpathSync(root) : root, candidate)) {
|
|
17
|
+
throw new Error(`${options.what} escapes the workspace root.`)
|
|
18
|
+
}
|
|
19
|
+
return candidate
|
|
20
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as Config from "effect/Config"
|
|
2
|
+
import * as Effect from "effect/Effect"
|
|
3
|
+
import * as Option from "effect/Option"
|
|
4
|
+
|
|
5
|
+
// Environment is a host capability, so it is read through effect/Config rather
|
|
6
|
+
// than process.env. The default ConfigProvider reads the host environment; a
|
|
7
|
+
// test or an embedding host substitutes one with ConfigProvider.layer.
|
|
8
|
+
export const readOptionalEnv = (name: string): Effect.Effect<string | undefined> =>
|
|
9
|
+
Config.option(Config.string(name)).pipe(
|
|
10
|
+
Effect.map(Option.getOrUndefined),
|
|
11
|
+
Effect.orElseSucceed(() => undefined)
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
// Exec and command publish run with a closed environment: PATH plus the
|
|
15
|
+
// operation's named variables and nothing else. The parent environment is
|
|
16
|
+
// never inherited, so a plan states every variable a command can observe.
|
|
17
|
+
export const readEnvironment = (
|
|
18
|
+
names: ReadonlyArray<string>
|
|
19
|
+
): Effect.Effect<Record<string, string>> =>
|
|
20
|
+
Effect.forEach(["PATH", ...names], (name) =>
|
|
21
|
+
readOptionalEnv(name).pipe(Effect.map((value) => [name, value] as const))).pipe(
|
|
22
|
+
Effect.map((entries) => ({
|
|
23
|
+
...Object.fromEntries(entries.flatMap(([name, value]) => value === undefined ? [] : [[name, value]])),
|
|
24
|
+
TZ: "UTC",
|
|
25
|
+
LC_ALL: "C",
|
|
26
|
+
LANG: "C",
|
|
27
|
+
SOURCE_DATE_EPOCH: "0"
|
|
28
|
+
})))
|