@mysten-incubation/devstack 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/dashboard-ui/assets/grpc-a4usE3Nk.js +3 -0
- package/dashboard-ui/assets/index-B82Bh84P.css +1 -0
- package/dashboard-ui/assets/index-CRYJ4pru.js +1277 -0
- package/dashboard-ui/index.html +2 -2
- package/dist/api/define-capabilities.d.mts +1 -20
- package/dist/api/define-capabilities.mjs +15 -22
- package/dist/api/define-capabilities.mjs.map +1 -1
- package/dist/api/define-devstack-with.d.mts +7 -1
- package/dist/api/define-devstack-with.mjs.map +1 -1
- package/dist/api/define-devstack.mjs.map +1 -1
- package/dist/api/define-plugin.d.mts +2 -1
- package/dist/api/inference-network.d.mts +31 -17
- package/dist/api/inference-network.mjs +40 -43
- package/dist/api/inference-network.mjs.map +1 -1
- package/dist/api/load-config.mjs +88 -0
- package/dist/api/load-config.mjs.map +1 -0
- package/dist/api/mode-narrowed-factory.d.mts +1 -1
- package/dist/api/mode-narrowed-factory.mjs.map +1 -1
- package/dist/api/run-stack-internal.mjs +202 -0
- package/dist/api/run-stack-internal.mjs.map +1 -0
- package/dist/api/run-stack.d.mts +82 -31
- package/dist/api/run-stack.mjs +22 -141
- package/dist/api/run-stack.mjs.map +1 -1
- package/dist/build-integrations/dapp-kit/index.d.mts +61 -0
- package/dist/build-integrations/dapp-kit/index.mjs +58 -0
- package/dist/build-integrations/dapp-kit/index.mjs.map +1 -0
- package/dist/build-integrations/playwright/config.d.mts +1 -30
- package/dist/build-integrations/playwright/config.mjs +1 -27
- package/dist/build-integrations/playwright/config.mjs.map +1 -1
- package/dist/build-integrations/playwright/errors.d.mts +4 -7
- package/dist/build-integrations/playwright/errors.mjs.map +1 -1
- package/dist/build-integrations/playwright/global-setup.d.mts +42 -71
- package/dist/build-integrations/playwright/global-setup.mjs +142 -218
- package/dist/build-integrations/playwright/global-setup.mjs.map +1 -1
- package/dist/build-integrations/playwright/index.d.mts +2 -2
- package/dist/build-integrations/playwright/index.mjs +2 -2
- package/dist/build-integrations/playwright/stack-context.d.mts +3 -1
- package/dist/build-integrations/playwright/stack-context.mjs +4 -2
- package/dist/build-integrations/playwright/stack-context.mjs.map +1 -1
- package/dist/build-integrations/playwright/wallet-context.mjs +44 -19
- package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -1
- package/dist/build-integrations/runtime/cold-start-url.mjs.map +1 -1
- package/dist/build-integrations/runtime/conventional-routes.mjs.map +1 -1
- package/dist/build-integrations/runtime/dapp-kit-slot.mjs.map +1 -1
- package/dist/build-integrations/runtime/discover.d.mts +6 -4
- package/dist/build-integrations/runtime/discover.mjs +15 -4
- package/dist/build-integrations/runtime/discover.mjs.map +1 -1
- package/dist/build-integrations/runtime/endpoint-registry.mjs.map +1 -1
- package/dist/build-integrations/runtime/errors.mjs.map +1 -1
- package/dist/build-integrations/runtime/read-stack-context.mjs +3 -3
- package/dist/build-integrations/runtime/read-stack-context.mjs.map +1 -1
- package/dist/build-integrations/runtime/resolve-discovery-env.d.mts +23 -7
- package/dist/build-integrations/runtime/resolve-discovery-env.mjs +25 -12
- package/dist/build-integrations/runtime/resolve-discovery-env.mjs.map +1 -1
- package/dist/build-integrations/runtime/stack-context.d.mts +2 -2
- package/dist/build-integrations/vite/index.d.mts +84 -19
- package/dist/build-integrations/vite/index.mjs +203 -28
- package/dist/build-integrations/vite/index.mjs.map +1 -1
- package/dist/build-integrations/vitest/config.d.mts +20 -2
- package/dist/build-integrations/vitest/config.mjs +16 -4
- package/dist/build-integrations/vitest/config.mjs.map +1 -1
- package/dist/build-integrations/vitest/env.d.mts +18 -10
- package/dist/build-integrations/vitest/env.mjs +11 -10
- package/dist/build-integrations/vitest/env.mjs.map +1 -1
- package/dist/build-integrations/vitest/errors.d.mts +7 -11
- package/dist/build-integrations/vitest/errors.mjs +3 -4
- package/dist/build-integrations/vitest/errors.mjs.map +1 -1
- package/dist/build-integrations/vitest/global-setup.d.mts +35 -0
- package/dist/build-integrations/vitest/global-setup.mjs +156 -0
- package/dist/build-integrations/vitest/global-setup.mjs.map +1 -0
- package/dist/build-integrations/vitest/index.d.mts +3 -2
- package/dist/build-integrations/vitest/index.mjs +3 -2
- package/dist/build-integrations/vitest/setup.mjs +2 -2
- package/dist/build-integrations/vitest/setup.mjs.map +1 -1
- package/dist/build-integrations/vitest/stack-context.d.mts +5 -3
- package/dist/build-integrations/vitest/stack-context.mjs +3 -2
- package/dist/build-integrations/vitest/stack-context.mjs.map +1 -1
- package/dist/cli/bail.mjs.map +1 -1
- package/dist/cli/doctor-probes.mjs +1 -3
- package/dist/cli/doctor-probes.mjs.map +1 -1
- package/dist/cli/main.d.mts +23 -1
- package/dist/cli/main.mjs +63 -8
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/prune-direct.mjs.map +1 -1
- package/dist/cli/snapshot-reader.mjs.map +1 -1
- package/dist/cli/up-lifecycle.mjs.map +1 -1
- package/dist/cli/wirings/apply.mjs +28 -21
- package/dist/cli/wirings/apply.mjs.map +1 -1
- package/dist/cli/wirings/codegen.mjs +108 -0
- package/dist/cli/wirings/codegen.mjs.map +1 -0
- package/dist/cli/wirings/config-loader.mjs +12 -62
- package/dist/cli/wirings/config-loader.mjs.map +1 -1
- package/dist/cli/wirings/dump-ids.mjs +86 -0
- package/dist/cli/wirings/dump-ids.mjs.map +1 -0
- package/dist/cli/wirings/engine-command.mjs +1 -1
- package/dist/cli/wirings/engine-command.mjs.map +1 -1
- package/dist/cli/wirings/identity.mjs +2 -2
- package/dist/cli/wirings/identity.mjs.map +1 -1
- package/dist/cli/wirings/provide-file-system.mjs.map +1 -1
- package/dist/cli/wirings/read-devstack-version.mjs +17 -0
- package/dist/cli/wirings/read-devstack-version.mjs.map +1 -0
- package/dist/cli/wirings/snapshot.mjs +27 -34
- package/dist/cli/wirings/snapshot.mjs.map +1 -1
- package/dist/cli/wirings/up-ipc.mjs +147 -0
- package/dist/cli/wirings/up-ipc.mjs.map +1 -0
- package/dist/cli/wirings/up.mjs +218 -198
- package/dist/cli/wirings/up.mjs.map +1 -1
- package/dist/cli/wirings/wipe.mjs +1 -1
- package/dist/cli/wirings/wipe.mjs.map +1 -1
- package/dist/contracts/chain-probe.d.mts +1 -1
- package/dist/contracts/chain-probe.mjs +1 -1
- package/dist/contracts/chain-probe.mjs.map +1 -1
- package/dist/contracts/codegenable.d.mts +60 -6
- package/dist/contracts/codegenable.mjs +28 -0
- package/dist/contracts/codegenable.mjs.map +1 -0
- package/dist/contracts/config-bindings.mjs +162 -0
- package/dist/contracts/config-bindings.mjs.map +1 -0
- package/dist/contracts/container-runtime.d.mts +23 -17
- package/dist/contracts/plugin-expander.mjs.map +1 -1
- package/dist/contracts/snapshotable.d.mts +5 -7
- package/dist/contracts/wallet-protocol.d.mts +2 -5
- package/dist/contracts/wallet-protocol.mjs +2 -5
- package/dist/contracts/wallet-protocol.mjs.map +1 -1
- package/dist/index.d.mts +21 -26
- package/dist/index.mjs +8 -9
- package/dist/orchestrators/boot.d.mts +28 -0
- package/dist/orchestrators/boot.mjs +440 -0
- package/dist/orchestrators/boot.mjs.map +1 -0
- package/dist/orchestrators/codegen/bindings.d.mts +13 -1
- package/dist/orchestrators/codegen/bindings.mjs +23 -9
- package/dist/orchestrators/codegen/bindings.mjs.map +1 -1
- package/dist/orchestrators/codegen/config-runtime.mjs +208 -0
- package/dist/orchestrators/codegen/config-runtime.mjs.map +1 -0
- package/dist/orchestrators/codegen/emit.mjs +1 -1
- package/dist/orchestrators/codegen/emit.mjs.map +1 -1
- package/dist/orchestrators/codegen/errors.mjs +21 -1
- package/dist/orchestrators/codegen/errors.mjs.map +1 -1
- package/dist/orchestrators/codegen/format.mjs +2 -0
- package/dist/orchestrators/codegen/format.mjs.map +1 -1
- package/dist/orchestrators/codegen/gitignore.mjs +30 -11
- package/dist/orchestrators/codegen/gitignore.mjs.map +1 -1
- package/dist/orchestrators/codegen/id-config.mjs +107 -0
- package/dist/orchestrators/codegen/id-config.mjs.map +1 -0
- package/dist/orchestrators/codegen/output-location.mjs +23 -32
- package/dist/orchestrators/codegen/output-location.mjs.map +1 -1
- package/dist/orchestrators/codegen/paths.mjs +8 -1
- package/dist/orchestrators/codegen/paths.mjs.map +1 -1
- package/dist/orchestrators/codegen/permissions.mjs.map +1 -1
- package/dist/orchestrators/codegen/service.mjs +286 -58
- package/dist/orchestrators/codegen/service.mjs.map +1 -1
- package/dist/orchestrators/layers.mjs +31 -0
- package/dist/orchestrators/layers.mjs.map +1 -0
- package/dist/orchestrators/lifecycle-prune/errors.mjs.map +1 -1
- package/dist/orchestrators/lifecycle-prune/index.mjs +8 -10
- package/dist/orchestrators/lifecycle-prune/index.mjs.map +1 -1
- package/dist/orchestrators/network-options.d.mts +52 -0
- package/dist/orchestrators/network-options.mjs +43 -0
- package/dist/orchestrators/network-options.mjs.map +1 -0
- package/dist/orchestrators/router/cleanup.mjs.map +1 -1
- package/dist/orchestrators/router/cors.mjs.map +1 -1
- package/dist/orchestrators/router/entrypoints.mjs.map +1 -1
- package/dist/orchestrators/router/errors.mjs.map +1 -1
- package/dist/orchestrators/router/file-provider.mjs.map +1 -1
- package/dist/orchestrators/router/hostname.mjs.map +1 -1
- package/dist/orchestrators/router/profile.mjs +1 -1
- package/dist/orchestrators/router/profile.mjs.map +1 -1
- package/dist/orchestrators/router/service.d.mts +2 -2
- package/dist/orchestrators/router/service.mjs +12 -27
- package/dist/orchestrators/router/service.mjs.map +1 -1
- package/dist/orchestrators/router/traefik-container.mjs +1 -1
- package/dist/orchestrators/router/traefik-container.mjs.map +1 -1
- package/dist/orchestrators/snapshot/capture.mjs +171 -195
- package/dist/orchestrators/snapshot/capture.mjs.map +1 -1
- package/dist/orchestrators/snapshot/descriptor.d.mts +1 -1
- package/dist/orchestrators/snapshot/descriptor.mjs +53 -11
- package/dist/orchestrators/snapshot/descriptor.mjs.map +1 -1
- package/dist/orchestrators/snapshot/identity-guard.mjs +3 -5
- package/dist/orchestrators/snapshot/identity-guard.mjs.map +1 -1
- package/dist/orchestrators/snapshot/image-bundle-tags.mjs +32 -101
- package/dist/orchestrators/snapshot/image-bundle-tags.mjs.map +1 -1
- package/dist/orchestrators/snapshot/index.mjs +1 -4
- package/dist/orchestrators/snapshot/integrity.mjs.map +1 -1
- package/dist/orchestrators/snapshot/interrupted-restore.mjs +116 -0
- package/dist/orchestrators/snapshot/interrupted-restore.mjs.map +1 -0
- package/dist/orchestrators/snapshot/phase-error.mjs.map +1 -1
- package/dist/orchestrators/snapshot/prune.mjs +40 -31
- package/dist/orchestrators/snapshot/prune.mjs.map +1 -1
- package/dist/orchestrators/snapshot/restore.mjs +221 -196
- package/dist/orchestrators/snapshot/restore.mjs.map +1 -1
- package/dist/orchestrators/snapshot/service.mjs +72 -62
- package/dist/orchestrators/snapshot/service.mjs.map +1 -1
- package/dist/orchestrators/snapshot/wipe.mjs +67 -56
- package/dist/orchestrators/snapshot/wipe.mjs.map +1 -1
- package/dist/plugins/account/codegen.mjs +3 -0
- package/dist/plugins/account/codegen.mjs.map +1 -1
- package/dist/plugins/account/errors.d.mts +2 -2
- package/dist/plugins/account/errors.mjs +1 -4
- package/dist/plugins/account/errors.mjs.map +1 -1
- package/dist/plugins/account/funding.mjs +3 -27
- package/dist/plugins/account/funding.mjs.map +1 -1
- package/dist/plugins/account/index.d.mts +3 -8
- package/dist/plugins/account/index.mjs +71 -35
- package/dist/plugins/account/index.mjs.map +1 -1
- package/dist/plugins/account/keypair.d.mts +8 -7
- package/dist/plugins/account/keypair.mjs +5 -18
- package/dist/plugins/account/keypair.mjs.map +1 -1
- package/dist/plugins/account/lease.mjs.map +1 -1
- package/dist/plugins/account/registry.d.mts +1 -46
- package/dist/plugins/account/registry.mjs.map +1 -1
- package/dist/plugins/account/service.d.mts +43 -34
- package/dist/plugins/account/service.mjs +9 -54
- package/dist/plugins/account/service.mjs.map +1 -1
- package/dist/plugins/account/snapshot.mjs.map +1 -1
- package/dist/plugins/account/variants/ephemeral.mjs.map +1 -1
- package/dist/plugins/account/variants/impersonate.mjs.map +1 -1
- package/dist/plugins/account/variants/signer.mjs.map +1 -1
- package/dist/plugins/action/discriminator.mjs.map +1 -1
- package/dist/plugins/action/errors.mjs +1 -4
- package/dist/plugins/action/errors.mjs.map +1 -1
- package/dist/plugins/action/execute.mjs +3 -4
- package/dist/plugins/action/execute.mjs.map +1 -1
- package/dist/plugins/action/index.d.mts +2 -2
- package/dist/plugins/action/index.mjs +7 -11
- package/dist/plugins/action/index.mjs.map +1 -1
- package/dist/plugins/action/service.d.mts +5 -7
- package/dist/plugins/action/service.mjs +2 -16
- package/dist/plugins/action/service.mjs.map +1 -1
- package/dist/plugins/coin/address-resolution.mjs.map +1 -1
- package/dist/plugins/coin/codegen.mjs +97 -16
- package/dist/plugins/coin/codegen.mjs.map +1 -1
- package/dist/plugins/coin/discovery.mjs.map +1 -1
- package/dist/plugins/coin/errors.mjs +1 -4
- package/dist/plugins/coin/errors.mjs.map +1 -1
- package/dist/plugins/coin/index.d.mts +4 -34
- package/dist/plugins/coin/index.mjs +47 -28
- package/dist/plugins/coin/index.mjs.map +1 -1
- package/dist/plugins/coin/metadata.d.mts +1 -0
- package/dist/plugins/coin/metadata.mjs +8 -9
- package/dist/plugins/coin/metadata.mjs.map +1 -1
- package/dist/plugins/coin/mint.mjs +7 -21
- package/dist/plugins/coin/mint.mjs.map +1 -1
- package/dist/plugins/coin/registry.mjs +33 -20
- package/dist/plugins/coin/registry.mjs.map +1 -1
- package/dist/plugins/coin/service.d.mts +1 -1
- package/dist/plugins/coin/service.mjs +1 -8
- package/dist/plugins/coin/service.mjs.map +1 -1
- package/dist/plugins/coin/snapshot.mjs.map +1 -1
- package/dist/plugins/coin/type-strings.mjs.map +1 -1
- package/dist/plugins/dashboard/domain.mjs +106 -167
- package/dist/plugins/dashboard/domain.mjs.map +1 -1
- package/dist/plugins/dashboard/index.mjs +13 -13
- package/dist/plugins/dashboard/index.mjs.map +1 -1
- package/dist/plugins/dashboard/origin-policy.mjs +3 -3
- package/dist/plugins/dashboard/origin-policy.mjs.map +1 -1
- package/dist/plugins/dashboard/routable.mjs.map +1 -1
- package/dist/plugins/dashboard/schema/builder.mjs.map +1 -1
- package/dist/plugins/dashboard/schema/enums.mjs.map +1 -1
- package/dist/plugins/dashboard/schema/root.mjs +23 -44
- package/dist/plugins/dashboard/schema/root.mjs.map +1 -1
- package/dist/plugins/dashboard/schema/types.mjs +37 -91
- package/dist/plugins/dashboard/schema/types.mjs.map +1 -1
- package/dist/plugins/dashboard/schema.mjs.map +1 -1
- package/dist/plugins/dashboard/server.mjs.map +1 -1
- package/dist/plugins/deepbook/codegen.d.mts +12 -11
- package/dist/plugins/deepbook/codegen.mjs +108 -11
- package/dist/plugins/deepbook/codegen.mjs.map +1 -1
- package/dist/plugins/deepbook/deploy.mjs +7 -14
- package/dist/plugins/deepbook/deploy.mjs.map +1 -1
- package/dist/plugins/deepbook/errors.mjs +4 -2
- package/dist/plugins/deepbook/errors.mjs.map +1 -1
- package/dist/plugins/deepbook/faucet-strategy.mjs +4 -10
- package/dist/plugins/deepbook/faucet-strategy.mjs.map +1 -1
- package/dist/plugins/deepbook/hash.mjs.map +1 -1
- package/dist/plugins/deepbook/index.d.mts +38 -34
- package/dist/plugins/deepbook/index.mjs +133 -64
- package/dist/plugins/deepbook/index.mjs.map +1 -1
- package/dist/plugins/deepbook/plugin-key.mjs.map +1 -1
- package/dist/plugins/deepbook/pyth/index.mjs +4 -8
- package/dist/plugins/deepbook/pyth/index.mjs.map +1 -1
- package/dist/plugins/deepbook/snapshot.mjs +0 -1
- package/dist/plugins/deepbook/snapshot.mjs.map +1 -1
- package/dist/plugins/deepbook/type-strings.mjs.map +1 -1
- package/dist/plugins/deepbook/types.d.mts +2 -1
- package/dist/plugins/deepbook/types.mjs.map +1 -1
- package/dist/plugins/faucet/dispatcher.d.mts +3 -3
- package/dist/plugins/faucet/dispatcher.mjs.map +1 -1
- package/dist/plugins/faucet/errors.mjs.map +1 -1
- package/dist/plugins/faucet/http.mjs +2 -10
- package/dist/plugins/faucet/http.mjs.map +1 -1
- package/dist/plugins/faucet/index.mjs +0 -1
- package/dist/plugins/faucet/index.mjs.map +1 -1
- package/dist/plugins/host-service/errors.d.mts +1 -3
- package/dist/plugins/host-service/errors.mjs +1 -2
- package/dist/plugins/host-service/errors.mjs.map +1 -1
- package/dist/plugins/host-service/index.d.mts +1 -2
- package/dist/plugins/host-service/index.mjs +11 -11
- package/dist/plugins/host-service/index.mjs.map +1 -1
- package/dist/plugins/host-service/routable.mjs.map +1 -1
- package/dist/plugins/host-service/service.mjs +13 -13
- package/dist/plugins/host-service/service.mjs.map +1 -1
- package/dist/plugins/internal/acquire-on-chain-artifact.mjs.map +1 -1
- package/dist/plugins/internal/codegen-helpers.mjs +2 -0
- package/dist/plugins/internal/codegen-helpers.mjs.map +1 -1
- package/dist/plugins/internal/funding-failure-error.mjs.map +1 -1
- package/dist/plugins/{postgres → internal/postgres-sidecar}/connection.mjs +1 -1
- package/dist/plugins/internal/postgres-sidecar/connection.mjs.map +1 -0
- package/dist/plugins/{postgres → internal/postgres-sidecar}/db-ensure.mjs +30 -16
- package/dist/plugins/internal/postgres-sidecar/db-ensure.mjs.map +1 -0
- package/dist/plugins/internal/postgres-sidecar/errors.mjs +21 -0
- package/dist/plugins/internal/postgres-sidecar/errors.mjs.map +1 -0
- package/dist/{substrate/runtime/scoped-multimap → plugins/internal/postgres-sidecar}/index.mjs +1 -0
- package/dist/plugins/internal/postgres-sidecar/service.mjs +121 -0
- package/dist/plugins/internal/postgres-sidecar/service.mjs.map +1 -0
- package/dist/plugins/package/build.mjs +2 -2
- package/dist/plugins/package/build.mjs.map +1 -1
- package/dist/plugins/package/codegen.d.mts +7 -14
- package/dist/plugins/package/codegen.mjs +187 -59
- package/dist/plugins/package/codegen.mjs.map +1 -1
- package/dist/plugins/package/dep-resolution.mjs +51 -3
- package/dist/plugins/package/dep-resolution.mjs.map +1 -1
- package/dist/plugins/package/errors.mjs +1 -4
- package/dist/plugins/package/errors.mjs.map +1 -1
- package/dist/plugins/package/git-source.d.mts +18 -0
- package/dist/plugins/package/git-source.mjs +119 -0
- package/dist/plugins/package/git-source.mjs.map +1 -0
- package/dist/plugins/package/index.d.mts +28 -19
- package/dist/plugins/package/index.mjs +107 -32
- package/dist/plugins/package/index.mjs.map +1 -1
- package/dist/plugins/package/mode-known.mjs +2 -2
- package/dist/plugins/package/mode-known.mjs.map +1 -1
- package/dist/plugins/package/mode-local.mjs +18 -35
- package/dist/plugins/package/mode-local.mjs.map +1 -1
- package/dist/plugins/package/publish-executor.mjs +13 -8
- package/dist/plugins/package/publish-executor.mjs.map +1 -1
- package/dist/plugins/package/publish-output.d.mts +1 -11
- package/dist/plugins/package/publish-output.mjs +1 -9
- package/dist/plugins/package/publish-output.mjs.map +1 -1
- package/dist/plugins/package/registry.d.mts +1 -1
- package/dist/plugins/package/registry.mjs +26 -14
- package/dist/plugins/package/registry.mjs.map +1 -1
- package/dist/plugins/package/service.mjs.map +1 -1
- package/dist/plugins/package/snapshot.mjs.map +1 -1
- package/dist/plugins/router-entrypoints.mjs +1 -3
- package/dist/plugins/router-entrypoints.mjs.map +1 -1
- package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs +1 -2
- package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs.map +1 -1
- package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs +1 -6
- package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs.map +1 -1
- package/dist/plugins/seal/codegen.d.mts +1 -5
- package/dist/plugins/seal/codegen.mjs +83 -12
- package/dist/plugins/seal/codegen.mjs.map +1 -1
- package/dist/plugins/seal/config-render.mjs +2 -6
- package/dist/plugins/seal/config-render.mjs.map +1 -1
- package/dist/plugins/seal/deploy.mjs +18 -29
- package/dist/plugins/seal/deploy.mjs.map +1 -1
- package/dist/plugins/seal/errors.d.mts +4 -5
- package/dist/plugins/seal/errors.mjs +1 -8
- package/dist/plugins/seal/errors.mjs.map +1 -1
- package/dist/plugins/seal/index.d.mts +7 -28
- package/dist/plugins/seal/index.mjs +69 -47
- package/dist/plugins/seal/index.mjs.map +1 -1
- package/dist/plugins/seal/key-manager.mjs.map +1 -1
- package/dist/plugins/seal/key-server.mjs +1 -6
- package/dist/plugins/seal/key-server.mjs.map +1 -1
- package/dist/plugins/seal/keygen.mjs +2 -3
- package/dist/plugins/seal/keygen.mjs.map +1 -1
- package/dist/plugins/seal/mode/fork-known.mjs.map +1 -1
- package/dist/plugins/seal/mode/live.mjs +1 -1
- package/dist/plugins/seal/mode/live.mjs.map +1 -1
- package/dist/plugins/seal/mode/local-keygen.mjs +4 -10
- package/dist/plugins/seal/mode/local-keygen.mjs.map +1 -1
- package/dist/plugins/seal/plugin-key.mjs.map +1 -1
- package/dist/plugins/seal/registry-publish.mjs.map +1 -1
- package/dist/plugins/seal/routable.mjs.map +1 -1
- package/dist/plugins/seal/service.mjs.map +1 -1
- package/dist/plugins/seal/snapshot.mjs +1 -2
- package/dist/plugins/seal/snapshot.mjs.map +1 -1
- package/dist/plugins/sui/auto-tick.mjs +5 -5
- package/dist/plugins/sui/auto-tick.mjs.map +1 -1
- package/dist/plugins/sui/chain-build-container.mjs +2 -2
- package/dist/plugins/sui/chain-build-container.mjs.map +1 -1
- package/dist/plugins/sui/chain-probe.mjs +4 -4
- package/dist/plugins/sui/chain-probe.mjs.map +1 -1
- package/dist/plugins/sui/codegen.d.mts +9 -7
- package/dist/plugins/sui/codegen.mjs +49 -46
- package/dist/plugins/sui/codegen.mjs.map +1 -1
- package/dist/plugins/sui/errors.d.mts +1 -1
- package/dist/plugins/sui/errors.mjs +1 -9
- package/dist/plugins/sui/errors.mjs.map +1 -1
- package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/index.d.mts +1 -1
- package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/index.mjs +3 -7
- package/dist/plugins/sui/exec/index.mjs.map +1 -0
- package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/sign-and-dispatch.mjs +1 -1
- package/dist/plugins/sui/exec/sign-and-dispatch.mjs.map +1 -0
- package/dist/plugins/sui/fork-faucet-strategy.mjs +2 -6
- package/dist/plugins/sui/fork-faucet-strategy.mjs.map +1 -1
- package/dist/plugins/sui/fork-orchestration.mjs +13 -23
- package/dist/plugins/sui/fork-orchestration.mjs.map +1 -1
- package/dist/plugins/sui/fork-transaction.mjs.map +1 -1
- package/dist/plugins/sui/index.d.mts +34 -83
- package/dist/plugins/sui/index.mjs +200 -68
- package/dist/plugins/sui/index.mjs.map +1 -1
- package/dist/{substrate/runtime/sui-ledger → plugins/sui/ledger}/object-ref.mjs +1 -1
- package/dist/plugins/sui/ledger/object-ref.mjs.map +1 -0
- package/dist/plugins/sui/local-faucet-strategy.mjs +1 -5
- package/dist/plugins/sui/local-faucet-strategy.mjs.map +1 -1
- package/dist/plugins/sui/log-attrs.mjs +11 -0
- package/dist/plugins/sui/log-attrs.mjs.map +1 -0
- package/dist/plugins/sui/mode/external.mjs +4 -12
- package/dist/plugins/sui/mode/external.mjs.map +1 -1
- package/dist/plugins/sui/mode/fork.mjs +19 -16
- package/dist/plugins/sui/mode/fork.mjs.map +1 -1
- package/dist/plugins/sui/mode/live.mjs +4 -17
- package/dist/plugins/sui/mode/live.mjs.map +1 -1
- package/dist/plugins/sui/mode/local.d.mts +1 -0
- package/dist/plugins/sui/mode/local.mjs +203 -61
- package/dist/plugins/sui/mode/local.mjs.map +1 -1
- package/dist/plugins/sui/mode/shared-boot.mjs +18 -23
- package/dist/plugins/sui/mode/shared-boot.mjs.map +1 -1
- package/dist/plugins/sui/mode/shared.d.mts +6 -7
- package/dist/plugins/sui/mode/shared.mjs.map +1 -1
- package/dist/plugins/sui/mode/spec.d.mts +23 -6
- package/dist/{substrate/runtime/sui-move-build → plugins/sui/move}/index.mjs +28 -30
- package/dist/plugins/sui/move/index.mjs.map +1 -0
- package/dist/plugins/sui/move-summary-runner.mjs +8 -4
- package/dist/plugins/sui/move-summary-runner.mjs.map +1 -1
- package/dist/{substrate/network.d.mts → plugins/sui/network-config.d.mts} +11 -10
- package/dist/plugins/sui/network-resolver.d.mts +6 -6
- package/dist/plugins/sui/routable.mjs.map +1 -1
- package/dist/plugins/sui/service.mjs +10 -6
- package/dist/plugins/sui/service.mjs.map +1 -1
- package/dist/plugins/sui/snapshot.mjs +10 -5
- package/dist/plugins/sui/snapshot.mjs.map +1 -1
- package/dist/plugins/wallet/codegen.d.mts +8 -6
- package/dist/plugins/wallet/codegen.mjs +4 -10
- package/dist/plugins/wallet/codegen.mjs.map +1 -1
- package/dist/plugins/wallet/errors.mjs +1 -4
- package/dist/plugins/wallet/errors.mjs.map +1 -1
- package/dist/plugins/wallet/index.d.mts +3 -6
- package/dist/plugins/wallet/index.mjs +16 -21
- package/dist/plugins/wallet/index.mjs.map +1 -1
- package/dist/plugins/wallet/origin-policy.mjs +3 -3
- package/dist/plugins/wallet/origin-policy.mjs.map +1 -1
- package/dist/plugins/wallet/pairing.mjs +6 -24
- package/dist/plugins/wallet/pairing.mjs.map +1 -1
- package/dist/plugins/wallet/protocol.mjs.map +1 -1
- package/dist/plugins/wallet/routable.mjs.map +1 -1
- package/dist/plugins/wallet/server.mjs +17 -24
- package/dist/plugins/wallet/server.mjs.map +1 -1
- package/dist/plugins/wallet/service.d.mts +2 -2
- package/dist/plugins/wallet/service.mjs +4 -15
- package/dist/plugins/wallet/service.mjs.map +1 -1
- package/dist/plugins/wallet/snapshot.mjs.map +1 -1
- package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs +1 -5
- package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs.map +1 -1
- package/dist/plugins/walrus/codegen.d.mts +2 -13
- package/dist/plugins/walrus/codegen.mjs +93 -28
- package/dist/plugins/walrus/codegen.mjs.map +1 -1
- package/dist/plugins/walrus/deploy-paths.mjs +2 -3
- package/dist/plugins/walrus/deploy-paths.mjs.map +1 -1
- package/dist/plugins/walrus/deploy.mjs +26 -19
- package/dist/plugins/walrus/deploy.mjs.map +1 -1
- package/dist/plugins/walrus/errors.mjs +1 -8
- package/dist/plugins/walrus/errors.mjs.map +1 -1
- package/dist/plugins/walrus/faucet-strategy.mjs +15 -1
- package/dist/plugins/walrus/faucet-strategy.mjs.map +1 -1
- package/dist/plugins/walrus/index.d.mts +13 -44
- package/dist/plugins/walrus/index.mjs +119 -122
- package/dist/plugins/walrus/index.mjs.map +1 -1
- package/dist/plugins/walrus/mode/known-deploy.mjs +6 -6
- package/dist/plugins/walrus/mode/known-deploy.mjs.map +1 -1
- package/dist/plugins/walrus/mode/local-cluster.d.mts +2 -2
- package/dist/plugins/walrus/mode/local-cluster.mjs +2 -10
- package/dist/plugins/walrus/mode/local-cluster.mjs.map +1 -1
- package/dist/plugins/walrus/plugin-key.mjs.map +1 -1
- package/dist/plugins/walrus/registry-publish.mjs.map +1 -1
- package/dist/plugins/walrus/routable.mjs.map +1 -1
- package/dist/plugins/walrus/service.mjs.map +1 -1
- package/dist/plugins/walrus/snapshot.mjs +3 -3
- package/dist/plugins/walrus/snapshot.mjs.map +1 -1
- package/dist/plugins/walrus/storage-nodes.mjs +53 -14
- package/dist/plugins/walrus/storage-nodes.mjs.map +1 -1
- package/dist/plugins/walrus/wal-swap.mjs +4 -8
- package/dist/plugins/walrus/wal-swap.mjs.map +1 -1
- package/dist/primitives/artifact-publisher.mjs.map +1 -1
- package/dist/primitives/cache.d.mts +1 -1
- package/dist/runtime/docker/client.mjs +47 -3
- package/dist/runtime/docker/client.mjs.map +1 -1
- package/dist/runtime/docker/container.mjs +37 -57
- package/dist/runtime/docker/container.mjs.map +1 -1
- package/dist/runtime/docker/errors.mjs.map +1 -1
- package/dist/runtime/docker/exec.mjs +2 -2
- package/dist/runtime/docker/exec.mjs.map +1 -1
- package/dist/runtime/docker/image.mjs +10 -11
- package/dist/runtime/docker/image.mjs.map +1 -1
- package/dist/runtime/docker/index.mjs +0 -1
- package/dist/runtime/docker/inspect-and-decode.mjs.map +1 -1
- package/dist/runtime/docker/inventory.mjs +9 -9
- package/dist/runtime/docker/inventory.mjs.map +1 -1
- package/dist/runtime/docker/labels.mjs +12 -1
- package/dist/runtime/docker/labels.mjs.map +1 -1
- package/dist/runtime/docker/network.mjs +6 -6
- package/dist/runtime/docker/network.mjs.map +1 -1
- package/dist/runtime/docker/render-run-args.mjs.map +1 -1
- package/dist/runtime/docker/service.mjs +31 -45
- package/dist/runtime/docker/service.mjs.map +1 -1
- package/dist/runtime/docker/sweep.mjs +14 -64
- package/dist/runtime/docker/sweep.mjs.map +1 -1
- package/dist/runtime/docker/volume.mjs +1 -1
- package/dist/runtime/docker/volume.mjs.map +1 -1
- package/dist/runtime/docker/wrap.mjs +14 -2
- package/dist/runtime/docker/wrap.mjs.map +1 -1
- package/dist/substrate/brand.d.mts +2 -5
- package/dist/substrate/brand.mjs +3 -2
- package/dist/substrate/brand.mjs.map +1 -1
- package/dist/substrate/cross-process.mjs +2 -16
- package/dist/substrate/cross-process.mjs.map +1 -1
- package/dist/substrate/event-time.mjs +0 -2
- package/dist/substrate/event-time.mjs.map +1 -1
- package/dist/substrate/events.d.mts +46 -16
- package/dist/substrate/identity.d.mts +11 -5
- package/dist/substrate/manifest.d.mts +33 -26
- package/dist/substrate/manifest.mjs +8 -3
- package/dist/substrate/manifest.mjs.map +1 -1
- package/dist/substrate/options.d.mts +33 -4
- package/dist/substrate/plugin-ctx.d.mts +70 -0
- package/dist/substrate/plugin-ctx.mjs +66 -0
- package/dist/substrate/plugin-ctx.mjs.map +1 -0
- package/dist/substrate/plugin.d.mts +46 -38
- package/dist/substrate/plugin.mjs +13 -9
- package/dist/substrate/plugin.mjs.map +1 -1
- package/dist/substrate/projection.d.mts +1 -1
- package/dist/substrate/runtime/atomic-write.mjs +3 -4
- package/dist/substrate/runtime/atomic-write.mjs.map +1 -1
- package/dist/substrate/runtime/cache/schema.mjs.map +1 -1
- package/dist/substrate/runtime/cache/service.mjs +50 -20
- package/dist/substrate/runtime/cache/service.mjs.map +1 -1
- package/dist/substrate/runtime/config-validation.mjs.map +1 -1
- package/dist/substrate/runtime/control-plane/domain.mjs +34 -12
- package/dist/substrate/runtime/control-plane/domain.mjs.map +1 -1
- package/dist/substrate/runtime/control-plane/service.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/command-channel/channel.mjs +14 -17
- package/dist/substrate/runtime/cross-process/command-channel/channel.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/command-channel/runtime-control-lock.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/index.mjs +0 -2
- package/dist/substrate/runtime/cross-process/live-clock.mjs +1 -1
- package/dist/substrate/runtime/cross-process/live-clock.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/liveness.mjs +11 -18
- package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs +2 -2
- package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/roster.mjs +17 -127
- package/dist/substrate/runtime/cross-process/roster.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/self-pid.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/stack-lock.mjs +29 -28
- package/dist/substrate/runtime/cross-process/stack-lock.mjs.map +1 -1
- package/dist/substrate/runtime/current-plugin.mjs.map +1 -1
- package/dist/substrate/runtime/errors.mjs +1 -13
- package/dist/substrate/runtime/errors.mjs.map +1 -1
- package/dist/substrate/runtime/format-unknown-error.mjs.map +1 -1
- package/dist/substrate/runtime/host-bind-mount-owner.mjs.map +1 -1
- package/dist/substrate/runtime/host-gateway.mjs.map +1 -1
- package/dist/substrate/runtime/host-tree-tar/index.mjs +15 -163
- package/dist/substrate/runtime/host-tree-tar/index.mjs.map +1 -1
- package/dist/substrate/runtime/http-probe.d.mts +1 -3
- package/dist/substrate/runtime/http-probe.mjs.map +1 -1
- package/dist/substrate/runtime/index.mjs +1 -6
- package/dist/substrate/runtime/lease-broker/service.mjs +5 -23
- package/dist/substrate/runtime/lease-broker/service.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/dep-graph.mjs +3 -3
- package/dist/substrate/runtime/lifecycle/dep-graph.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/graph-input-id.d.mts +1 -0
- package/dist/substrate/runtime/lifecycle/graph-input-id.mjs +133 -0
- package/dist/substrate/runtime/lifecycle/graph-input-id.mjs.map +1 -0
- package/dist/substrate/runtime/lifecycle/index.mjs +2 -2
- package/dist/substrate/runtime/lifecycle/plugin-registry.d.mts +1 -1
- package/dist/substrate/runtime/lifecycle/plugin-registry.mjs +22 -1
- package/dist/substrate/runtime/lifecycle/plugin-registry.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/ready-gate.mjs +2 -15
- package/dist/substrate/runtime/lifecycle/ready-gate.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/selective-restart.mjs +29 -25
- package/dist/substrate/runtime/lifecycle/selective-restart.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/signals.mjs +1 -1
- package/dist/substrate/runtime/lifecycle/signals.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/state-machine.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/watch-attribution.d.mts +1 -1
- package/dist/substrate/runtime/lifecycle/watch-attribution.mjs +0 -1
- package/dist/substrate/runtime/lifecycle/watch-attribution.mjs.map +1 -1
- package/dist/substrate/runtime/managed-container.d.mts +5 -1
- package/dist/substrate/runtime/managed-container.mjs +8 -10
- package/dist/substrate/runtime/managed-container.mjs.map +1 -1
- package/dist/substrate/runtime/manifest/manifest.mjs +8 -30
- package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -1
- package/dist/substrate/runtime/mode-errors.d.mts +1 -2
- package/dist/substrate/runtime/mode-errors.mjs.map +1 -1
- package/dist/substrate/runtime/observability/cascade-formatter.mjs +1 -11
- package/dist/substrate/runtime/observability/cascade-formatter.mjs.map +1 -1
- package/dist/substrate/runtime/observability/ignore-with-log.mjs.map +1 -1
- package/dist/substrate/runtime/observability/index.d.mts +1 -1
- package/dist/substrate/runtime/observability/index.mjs +4 -6
- package/dist/substrate/runtime/observability/log-attrs.mjs +46 -0
- package/dist/substrate/runtime/observability/log-attrs.mjs.map +1 -0
- package/dist/substrate/runtime/observability/log-store.mjs.map +1 -1
- package/dist/substrate/runtime/observability/logger.mjs +3 -3
- package/dist/substrate/runtime/observability/logger.mjs.map +1 -1
- package/dist/substrate/runtime/observability/output-truncate.mjs.map +1 -1
- package/dist/substrate/runtime/observability/pretty-error.mjs.map +1 -1
- package/dist/substrate/runtime/observability/process-lines.mjs.map +1 -1
- package/dist/substrate/runtime/observability/redaction.mjs.map +1 -1
- package/dist/substrate/runtime/observability/subprocess-capture.mjs +2 -2
- package/dist/substrate/runtime/observability/subprocess-capture.mjs.map +1 -1
- package/dist/substrate/runtime/passthrough-or-wrap.mjs.map +1 -1
- package/dist/substrate/runtime/paths.mjs +27 -9
- package/dist/substrate/runtime/paths.mjs.map +1 -1
- package/dist/substrate/runtime/phase-preserving-produce.mjs.map +1 -1
- package/dist/substrate/runtime/port-broker/service.mjs +10 -27
- package/dist/substrate/runtime/port-broker/service.mjs.map +1 -1
- package/dist/substrate/runtime/post-acquire-tasks.mjs.map +1 -1
- package/dist/substrate/runtime/probes.d.mts +2 -3
- package/dist/substrate/runtime/probes.mjs.map +1 -1
- package/dist/substrate/runtime/process-supervisor.mjs.map +1 -1
- package/dist/substrate/runtime/projection/operational-endpoints.mjs.map +1 -1
- package/dist/substrate/runtime/projection/state-ref.mjs.map +1 -1
- package/dist/substrate/runtime/projection/update.mjs +87 -11
- package/dist/substrate/runtime/projection/update.mjs.map +1 -1
- package/dist/substrate/runtime/random-suffix.mjs.map +1 -1
- package/dist/substrate/runtime/reconcile/fs-plan.mjs +146 -0
- package/dist/substrate/runtime/reconcile/fs-plan.mjs.map +1 -0
- package/dist/substrate/runtime/reconcile/graph.mjs +19 -0
- package/dist/substrate/runtime/reconcile/graph.mjs.map +1 -0
- package/dist/substrate/runtime/reconcile/index.mjs +5 -0
- package/dist/substrate/runtime/reconcile/label.mjs +41 -0
- package/dist/substrate/runtime/reconcile/label.mjs.map +1 -0
- package/dist/substrate/runtime/reconcile/spec.mjs +15 -0
- package/dist/substrate/runtime/reconcile/spec.mjs.map +1 -0
- package/dist/substrate/runtime/retry-policy.d.mts +2 -3
- package/dist/substrate/runtime/retry-policy.mjs.map +1 -1
- package/dist/substrate/runtime/routed-url.mjs.map +1 -1
- package/dist/substrate/runtime/runtime-decode.mjs.map +1 -1
- package/dist/substrate/runtime/scoped-http-server.mjs.map +1 -1
- package/dist/substrate/runtime/stage-and-swap/index.mjs +49 -12
- package/dist/substrate/runtime/stage-and-swap/index.mjs.map +1 -1
- package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs.map +1 -1
- package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs.map +1 -1
- package/dist/substrate/runtime/strategy-registry/service.mjs +29 -17
- package/dist/substrate/runtime/strategy-registry/service.mjs.map +1 -1
- package/dist/substrate/runtime/subnet-broker.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/acquire-node.mjs +188 -56
- package/dist/substrate/runtime/supervisor/acquire-node.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/background-tasks.mjs +79 -65
- package/dist/substrate/runtime/supervisor/background-tasks.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/command-loop.mjs +82 -23
- package/dist/substrate/runtime/supervisor/command-loop.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/contribution-dispatcher.mjs +16 -0
- package/dist/substrate/runtime/supervisor/contribution-dispatcher.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/errors.mjs +11 -2
- package/dist/substrate/runtime/supervisor/errors.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/index.mjs +2 -0
- package/dist/substrate/runtime/supervisor/shutdown.mjs +6 -6
- package/dist/substrate/runtime/supervisor/shutdown.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/start-supervisor.mjs +60 -70
- package/dist/substrate/runtime/supervisor/start-supervisor.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/state.d.mts +1 -1
- package/dist/substrate/runtime/supervisor/state.mjs +1 -1
- package/dist/substrate/runtime/supervisor/state.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/teardown.mjs +10 -9
- package/dist/substrate/runtime/supervisor/teardown.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/wiring.mjs +6 -20
- package/dist/substrate/runtime/supervisor/wiring.mjs.map +1 -1
- package/dist/substrate/runtime/tar/reader.mjs +216 -0
- package/dist/substrate/runtime/tar/reader.mjs.map +1 -0
- package/dist/substrate/runtime/typed-env.mjs.map +1 -1
- package/dist/substrate/versioned-doc-schema.mjs.map +1 -1
- package/dist/substrate/versioned-doc-sync.mjs +5 -5
- package/dist/substrate/versioned-doc-sync.mjs.map +1 -1
- package/dist/surfaces/cli/command-tree.mjs +49 -7
- package/dist/surfaces/cli/command-tree.mjs.map +1 -1
- package/dist/surfaces/cli/commands/config.mjs +1 -1
- package/dist/surfaces/cli/commands/config.mjs.map +1 -1
- package/dist/surfaces/cli/commands/confirm-node.mjs.map +1 -1
- package/dist/surfaces/cli/commands/confirm.mjs.map +1 -1
- package/dist/surfaces/cli/commands/doctor.mjs +1 -1
- package/dist/surfaces/cli/commands/doctor.mjs.map +1 -1
- package/dist/surfaces/cli/commands/prune-picker-entry.mjs.map +1 -1
- package/dist/surfaces/cli/commands/prune-picker.mjs.map +1 -1
- package/dist/surfaces/cli/commands/prune.mjs +1 -1
- package/dist/surfaces/cli/commands/prune.mjs.map +1 -1
- package/dist/surfaces/cli/commands/snapshot.mjs +1 -1
- package/dist/surfaces/cli/commands/snapshot.mjs.map +1 -1
- package/dist/surfaces/cli/commands/status.mjs +1 -1
- package/dist/surfaces/cli/commands/status.mjs.map +1 -1
- package/dist/surfaces/cli/commands/supervisor-presence.mjs.map +1 -1
- package/dist/surfaces/cli/commands/wipe.mjs +3 -4
- package/dist/surfaces/cli/commands/wipe.mjs.map +1 -1
- package/dist/surfaces/cli/envelope.mjs.map +1 -1
- package/dist/surfaces/cli/errors.mjs.map +1 -1
- package/dist/surfaces/cli/flags.mjs.map +1 -1
- package/dist/surfaces/cli/index.mjs +45 -14
- package/dist/surfaces/cli/index.mjs.map +1 -1
- package/dist/surfaces/cli/output.mjs.map +1 -1
- package/dist/surfaces/cli/sysexits.mjs.map +1 -1
- package/dist/surfaces/tui/app.mjs +0 -21
- package/dist/surfaces/tui/app.mjs.map +1 -1
- package/dist/surfaces/tui/dashboard.mjs +0 -48
- package/dist/surfaces/tui/dashboard.mjs.map +1 -1
- package/dist/surfaces/tui/display-derivation.mjs +80 -16
- package/dist/surfaces/tui/display-derivation.mjs.map +1 -1
- package/dist/surfaces/tui/errors.mjs.map +1 -1
- package/dist/surfaces/tui/event-log.mjs +0 -9
- package/dist/surfaces/tui/event-log.mjs.map +1 -1
- package/dist/surfaces/tui/heartbeat.mjs.map +1 -1
- package/dist/surfaces/tui/index.mjs +2 -2
- package/dist/surfaces/tui/index.mjs.map +1 -1
- package/dist/surfaces/tui/input.mjs.map +1 -1
- package/dist/surfaces/tui/mode-detect.mjs.map +1 -1
- package/dist/surfaces/tui/mount-ink.mjs.map +1 -1
- package/dist/surfaces/tui/plain-renderer.mjs +26 -19
- package/dist/surfaces/tui/plain-renderer.mjs.map +1 -1
- package/dist/surfaces/tui/resource-table.mjs +8 -10
- package/dist/surfaces/tui/resource-table.mjs.map +1 -1
- package/images/postgres/Dockerfile +0 -1
- package/images/sui/Dockerfile +27 -54
- package/images/sui/entrypoint.sh +17 -179
- package/package.json +23 -15
- package/dashboard-ui/assets/index-Bmi1UtAg.js +0 -1356
- package/dashboard-ui/assets/index-D5EShVt4.js +0 -3
- package/dashboard-ui/assets/index-Deml9drg.css +0 -1
- package/dist/api/plugin-errors.d.mts +0 -7
- package/dist/api/plugin-errors.mjs +0 -10
- package/dist/api/plugin-errors.mjs.map +0 -1
- package/dist/cli/wirings/build-verb-layers.mjs +0 -42
- package/dist/cli/wirings/build-verb-layers.mjs.map +0 -1
- package/dist/contracts/capability-decl.d.mts +0 -40
- package/dist/orchestrators/built-in-plugin-layers.mjs +0 -54
- package/dist/orchestrators/built-in-plugin-layers.mjs.map +0 -1
- package/dist/orchestrators/run.mjs +0 -91
- package/dist/orchestrators/run.mjs.map +0 -1
- package/dist/orchestrators/runtime-composition.d.mts +0 -10
- package/dist/orchestrators/runtime-composition.mjs +0 -252
- package/dist/orchestrators/runtime-composition.mjs.map +0 -1
- package/dist/orchestrators/snapshot/capture-command.d.mts +0 -1
- package/dist/orchestrators/snapshot/capture-command.mjs +0 -25
- package/dist/orchestrators/snapshot/capture-command.mjs.map +0 -1
- package/dist/orchestrators/snapshot/pending-marker.mjs +0 -99
- package/dist/orchestrators/snapshot/pending-marker.mjs.map +0 -1
- package/dist/orchestrators/snapshot/recover-pending.mjs +0 -223
- package/dist/orchestrators/snapshot/recover-pending.mjs.map +0 -1
- package/dist/orchestrators/snapshot/state-document.d.mts +0 -1
- package/dist/orchestrators/snapshot/state-document.mjs +0 -58
- package/dist/orchestrators/snapshot/state-document.mjs.map +0 -1
- package/dist/plugins/account/spans.mjs +0 -16
- package/dist/plugins/account/spans.mjs.map +0 -1
- package/dist/plugins/account/variants/env.mjs +0 -24
- package/dist/plugins/account/variants/env.mjs.map +0 -1
- package/dist/plugins/account/variants/inline.mjs +0 -13
- package/dist/plugins/account/variants/inline.mjs.map +0 -1
- package/dist/plugins/account/variants/keystore.mjs +0 -91
- package/dist/plugins/account/variants/keystore.mjs.map +0 -1
- package/dist/plugins/action/spans.mjs +0 -11
- package/dist/plugins/action/spans.mjs.map +0 -1
- package/dist/plugins/coin/spans.mjs +0 -20
- package/dist/plugins/coin/spans.mjs.map +0 -1
- package/dist/plugins/deepbook/spans.mjs +0 -18
- package/dist/plugins/deepbook/spans.mjs.map +0 -1
- package/dist/plugins/faucet/spans.mjs +0 -12
- package/dist/plugins/faucet/spans.mjs.map +0 -1
- package/dist/plugins/package/spans.mjs +0 -14
- package/dist/plugins/package/spans.mjs.map +0 -1
- package/dist/plugins/postgres/codegen.mjs +0 -42
- package/dist/plugins/postgres/codegen.mjs.map +0 -1
- package/dist/plugins/postgres/connection.d.mts +0 -33
- package/dist/plugins/postgres/connection.mjs.map +0 -1
- package/dist/plugins/postgres/db-ensure.mjs.map +0 -1
- package/dist/plugins/postgres/errors.d.mts +0 -60
- package/dist/plugins/postgres/errors.mjs +0 -29
- package/dist/plugins/postgres/errors.mjs.map +0 -1
- package/dist/plugins/postgres/index.d.mts +0 -37
- package/dist/plugins/postgres/index.mjs +0 -68
- package/dist/plugins/postgres/index.mjs.map +0 -1
- package/dist/plugins/postgres/routable.mjs +0 -33
- package/dist/plugins/postgres/routable.mjs.map +0 -1
- package/dist/plugins/postgres/service.d.mts +0 -50
- package/dist/plugins/postgres/service.mjs +0 -179
- package/dist/plugins/postgres/service.mjs.map +0 -1
- package/dist/plugins/postgres/snapshot.mjs +0 -32
- package/dist/plugins/postgres/snapshot.mjs.map +0 -1
- package/dist/plugins/postgres/spans.mjs +0 -11
- package/dist/plugins/postgres/spans.mjs.map +0 -1
- package/dist/plugins/seal/spans.mjs +0 -18
- package/dist/plugins/seal/spans.mjs.map +0 -1
- package/dist/plugins/sui/spans.mjs +0 -17
- package/dist/plugins/sui/spans.mjs.map +0 -1
- package/dist/plugins/wallet/spans.mjs +0 -22
- package/dist/plugins/wallet/spans.mjs.map +0 -1
- package/dist/plugins/walrus/registry-publish.d.mts +0 -24
- package/dist/plugins/walrus/spans.mjs +0 -18
- package/dist/plugins/walrus/spans.mjs.map +0 -1
- package/dist/runtime/docker/logs.d.mts +0 -1
- package/dist/runtime/docker/logs.mjs +0 -34
- package/dist/runtime/docker/logs.mjs.map +0 -1
- package/dist/substrate/runtime/artifact-publisher/index.mjs +0 -86
- package/dist/substrate/runtime/artifact-publisher/index.mjs.map +0 -1
- package/dist/substrate/runtime/capability-sinks/index.d.mts +0 -1
- package/dist/substrate/runtime/capability-sinks/index.mjs +0 -3
- package/dist/substrate/runtime/capability-sinks/layer.d.mts +0 -1
- package/dist/substrate/runtime/capability-sinks/layer.mjs +0 -31
- package/dist/substrate/runtime/capability-sinks/layer.mjs.map +0 -1
- package/dist/substrate/runtime/capability-sinks/service.d.mts +0 -95
- package/dist/substrate/runtime/capability-sinks/service.mjs +0 -69
- package/dist/substrate/runtime/capability-sinks/service.mjs.map +0 -1
- package/dist/substrate/runtime/cross-process/lock.d.mts +0 -1
- package/dist/substrate/runtime/cross-process/lock.mjs +0 -23
- package/dist/substrate/runtime/cross-process/lock.mjs.map +0 -1
- package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs +0 -113
- package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs.map +0 -1
- package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs +0 -31
- package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs.map +0 -1
- package/dist/substrate/runtime/observability/formatter-registry.d.mts +0 -1
- package/dist/substrate/runtime/observability/formatter-registry.mjs +0 -48
- package/dist/substrate/runtime/observability/formatter-registry.mjs.map +0 -1
- package/dist/substrate/runtime/observability/span-store.d.mts +0 -1
- package/dist/substrate/runtime/observability/span-store.mjs +0 -110
- package/dist/substrate/runtime/observability/span-store.mjs.map +0 -1
- package/dist/substrate/runtime/observability/spans.d.mts +0 -1
- package/dist/substrate/runtime/observability/spans.mjs +0 -87
- package/dist/substrate/runtime/observability/spans.mjs.map +0 -1
- package/dist/substrate/runtime/projection/index.mjs +0 -4
- package/dist/substrate/runtime/projection/persisted.mjs +0 -213
- package/dist/substrate/runtime/projection/persisted.mjs.map +0 -1
- package/dist/substrate/runtime/scoped-multimap/service.mjs +0 -52
- package/dist/substrate/runtime/scoped-multimap/service.mjs.map +0 -1
- package/dist/substrate/runtime/scoped-ref-map/index.mjs +0 -2
- package/dist/substrate/runtime/scoped-ref-map/service.mjs +0 -83
- package/dist/substrate/runtime/scoped-ref-map/service.mjs.map +0 -1
- package/dist/substrate/runtime/state-store/index.mjs +0 -3
- package/dist/substrate/runtime/state-store/schema.d.mts +0 -1
- package/dist/substrate/runtime/state-store/schema.mjs +0 -41
- package/dist/substrate/runtime/state-store/schema.mjs.map +0 -1
- package/dist/substrate/runtime/state-store/service.d.mts +0 -1
- package/dist/substrate/runtime/state-store/service.mjs +0 -145
- package/dist/substrate/runtime/state-store/service.mjs.map +0 -1
- package/dist/substrate/runtime/sui-execute/index.mjs.map +0 -1
- package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs.map +0 -1
- package/dist/substrate/runtime/sui-ledger/object-ref.mjs.map +0 -1
- package/dist/substrate/runtime/sui-move-build/index.mjs.map +0 -1
- package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs +0 -151
- package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs.map +0 -1
- package/dist/substrate/state-store.d.mts +0 -1
- /package/dist/orchestrators/{snapshot/pending-marker.d.mts → codegen/id-config.d.mts} +0 -0
- /package/dist/orchestrators/snapshot/{recover-pending.d.mts → interrupted-restore.d.mts} +0 -0
- /package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/sign-and-dispatch.d.mts +0 -0
- /package/dist/plugins/{postgres/snapshot.d.mts → sui/ledger/object-ref.d.mts} +0 -0
- /package/dist/{substrate/runtime/sui-move-build → plugins/sui/move}/index.d.mts +0 -0
- /package/dist/substrate/runtime/{state-store/index.d.mts → reconcile/graph.d.mts} +0 -0
- /package/dist/substrate/runtime/{cross-process/snapshot-reservation.d.mts → supervisor/contribution-dispatcher.d.mts} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-bind-mount-owner.mjs","names":[],"sources":["../../../src/substrate/runtime/host-bind-mount-owner.ts"],"sourcesContent":["// Host UID:GID accessor for bind-mount ownership.\n//\n// When the substrate runs a container with a host bind mount, the\n// container's writes land on the host filesystem as the container's\n// effective UID. On native Linux Docker that defaults to root, which\n// makes the resulting files un-deletable from the developer's shell\n// without `sudo`. Plugins thread `${UID}:${GID}` into the container's\n// `--user` flag (or via `DEVSTACK_HOST_UID_GID` for entrypoints that\n// re-exec) so the writes land owned by the invoking user.\n//\n// Returns `undefined` on platforms where `process.getuid/getgid` are\n// not available (Windows, browser). Callers omit the user/env stamp in\n// that case — Docker Desktop handles the ownership mapping itself.\n\nexport const hostBindMountOwner = (): string | undefined => {\n\tconst process = (\n\t\tglobalThis as {\n\t\t\tprocess?: { getuid?: () => number; getgid?: () => number };\n\t\t}\n\t).process;\n\tif (typeof process?.getuid !== 'function' || typeof process.getgid !== 'function') {\n\t\treturn undefined;\n\t}\n\treturn `${process.getuid()}:${process.getgid()}`;\n};\n"],"mappings":";AAcA,MAAa,2BAA+C;CAC3D,MAAM,UACL,WAGC;
|
|
1
|
+
{"version":3,"file":"host-bind-mount-owner.mjs","names":[],"sources":["../../../src/substrate/runtime/host-bind-mount-owner.ts"],"sourcesContent":["// Host UID:GID accessor for bind-mount ownership.\n//\n// When the substrate runs a container with a host bind mount, the\n// container's writes land on the host filesystem as the container's\n// effective UID. On native Linux Docker that defaults to root, which\n// makes the resulting files un-deletable from the developer's shell\n// without `sudo`. Plugins thread `${UID}:${GID}` into the container's\n// `--user` flag (or via `DEVSTACK_HOST_UID_GID` for entrypoints that\n// re-exec) so the writes land owned by the invoking user.\n//\n// Returns `undefined` on platforms where `process.getuid/getgid` are\n// not available (Windows, browser). Callers omit the user/env stamp in\n// that case — Docker Desktop handles the ownership mapping itself.\n\nexport const hostBindMountOwner = (): string | undefined => {\n\tconst process = (\n\t\tglobalThis as {\n\t\t\tprocess?: { getuid?: () => number; getgid?: () => number };\n\t\t}\n\t).process;\n\tif (typeof process?.getuid !== 'function' || typeof process.getgid !== 'function') {\n\t\treturn undefined;\n\t}\n\treturn `${process.getuid()}:${process.getgid()}`;\n};\n"],"mappings":";AAcA,MAAa,2BAA+C;CAC3D,MAAM,UACL,WAGC;CACF,IAAI,OAAO,SAAS,WAAW,cAAc,OAAO,QAAQ,WAAW,YACtE;CAED,OAAO,GAAG,QAAQ,OAAO,EAAE,GAAG,QAAQ,OAAO;AAC9C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-gateway.mjs","names":[],"sources":["../../../src/substrate/runtime/host-gateway.ts"],"sourcesContent":["// Host-gateway `extraHosts` — ONE canonical constant.\n//\n// A container that needs to reach a service on the host's loopback\n// (e.g. Docker-published ports from a sibling container, or a host\n// process bound to `127.0.0.1`) does it by name via\n// `host.docker.internal`. Docker Desktop on macOS/Windows wires this\n// automatically; native Linux Docker does NOT and requires an\n// explicit `--add-host host.docker.internal:host-gateway` per\n// container (the literal string `host-gateway` is a Docker token\n// that resolves to the gateway IP of the container's network).\n//\n// This constant is the `extraHosts:` value passed to\n// `EnsureContainerSpec` / `runOneShot`. Use it instead of inlining\n// `{ 'host.docker.internal': 'host-gateway' }` at every dial-host\n// container site so a future change (e.g. additional aliases, or\n// platform-conditional mapping) lives in one place.\n\n/** Container `extraHosts` mapping that resolves\n * `host.docker.internal` to the host gateway on native Linux\n * Docker. Pass as `extraHosts:` on container specs that need to\n * dial host-bound services. */\nexport const HOST_GATEWAY_EXTRA_HOSTS: Readonly<Record<string, string>> = {\n\t'host.docker.internal': 'host-gateway',\n};\n\n/** Same fact as `HOST_GATEWAY_EXTRA_HOSTS`, encoded for the\n * `docker run --add-host <host>:<gateway>` CLI flag (the value\n * passed as the argv after `--add-host`). Derived from the record\n * so a future change to the alias set lives in one place.\n *\n * If the record ever grows past one entry, the router's traefik\n * bootstrap will need to emit one `--add-host <pair>` per entry;\n * today it stamps exactly one pair, matching this record's single\n * member. */\nexport const HOST_GATEWAY_ADD_HOST_FLAGS: ReadonlyArray<string> = Object.entries(\n\tHOST_GATEWAY_EXTRA_HOSTS,\n).map(([host, gateway]) => `${host}:${gateway}`);\n"],"mappings":";;;;;AAqBA,MAAa,2BAA6D,EACzE,wBAAwB,
|
|
1
|
+
{"version":3,"file":"host-gateway.mjs","names":[],"sources":["../../../src/substrate/runtime/host-gateway.ts"],"sourcesContent":["// Host-gateway `extraHosts` — ONE canonical constant.\n//\n// A container that needs to reach a service on the host's loopback\n// (e.g. Docker-published ports from a sibling container, or a host\n// process bound to `127.0.0.1`) does it by name via\n// `host.docker.internal`. Docker Desktop on macOS/Windows wires this\n// automatically; native Linux Docker does NOT and requires an\n// explicit `--add-host host.docker.internal:host-gateway` per\n// container (the literal string `host-gateway` is a Docker token\n// that resolves to the gateway IP of the container's network).\n//\n// This constant is the `extraHosts:` value passed to\n// `EnsureContainerSpec` / `runOneShot`. Use it instead of inlining\n// `{ 'host.docker.internal': 'host-gateway' }` at every dial-host\n// container site so a future change (e.g. additional aliases, or\n// platform-conditional mapping) lives in one place.\n\n/** Container `extraHosts` mapping that resolves\n * `host.docker.internal` to the host gateway on native Linux\n * Docker. Pass as `extraHosts:` on container specs that need to\n * dial host-bound services. */\nexport const HOST_GATEWAY_EXTRA_HOSTS: Readonly<Record<string, string>> = {\n\t'host.docker.internal': 'host-gateway',\n};\n\n/** Same fact as `HOST_GATEWAY_EXTRA_HOSTS`, encoded for the\n * `docker run --add-host <host>:<gateway>` CLI flag (the value\n * passed as the argv after `--add-host`). Derived from the record\n * so a future change to the alias set lives in one place.\n *\n * If the record ever grows past one entry, the router's traefik\n * bootstrap will need to emit one `--add-host <pair>` per entry;\n * today it stamps exactly one pair, matching this record's single\n * member. */\nexport const HOST_GATEWAY_ADD_HOST_FLAGS: ReadonlyArray<string> = Object.entries(\n\tHOST_GATEWAY_EXTRA_HOSTS,\n).map(([host, gateway]) => `${host}:${gateway}`);\n"],"mappings":";;;;;AAqBA,MAAa,2BAA6D,EACzE,wBAAwB,eACzB;;;;;;;;;;AAWA,MAAa,8BAAqD,OAAO,QACxE,wBACD,CAAC,CAAC,KAAK,CAAC,MAAM,aAAa,GAAG,KAAK,GAAG,SAAS"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { awaitProcessExit, describeProcessExitStatus } from "../process-supervisor.mjs";
|
|
2
|
+
import { finishTarReader, isSafeArchivePath, makeTarReaderState, processTarChunk, skipEntry } from "../tar/reader.mjs";
|
|
2
3
|
import { Effect, Schema, Stream } from "effect";
|
|
3
4
|
import { spawn } from "node:child_process";
|
|
4
5
|
//#region src/substrate/runtime/host-tree-tar/index.ts
|
|
@@ -77,169 +78,26 @@ const spawnTar = (operation, args, stdio) => Effect.gen(function* () {
|
|
|
77
78
|
stderrChunks
|
|
78
79
|
};
|
|
79
80
|
});
|
|
80
|
-
const TAR_BLOCK_SIZE = 512;
|
|
81
|
-
const MAX_TAR_EXTENDED_PATH_BYTES = 1024 * 1024;
|
|
82
|
-
const bytesToString = (bytes) => {
|
|
83
|
-
const nul = bytes.indexOf(0);
|
|
84
|
-
const end = nul === -1 ? bytes.length : nul;
|
|
85
|
-
return Buffer.from(bytes.subarray(0, end)).toString("utf8");
|
|
86
|
-
};
|
|
87
|
-
const concatBytes = (a, b) => {
|
|
88
|
-
if (a.length === 0) return b;
|
|
89
|
-
if (b.length === 0) return a;
|
|
90
|
-
const out = new Uint8Array(a.length + b.length);
|
|
91
|
-
out.set(a, 0);
|
|
92
|
-
out.set(b, a.length);
|
|
93
|
-
return out;
|
|
94
|
-
};
|
|
95
|
-
const consumeBytes = (buffer, count) => buffer.subarray(count);
|
|
96
|
-
const isZeroBlock = (block) => block.every((byte) => byte === 0);
|
|
97
|
-
const parseTarSize = (header) => {
|
|
98
|
-
const raw = bytesToString(header.subarray(124, 136)).trim();
|
|
99
|
-
if (raw === "") return 0;
|
|
100
|
-
if (!/^[0-7]+$/.test(raw)) return null;
|
|
101
|
-
const parsed = Number.parseInt(raw, 8);
|
|
102
|
-
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : null;
|
|
103
|
-
};
|
|
104
|
-
const tarPathFromHeader = (header) => {
|
|
105
|
-
const name = bytesToString(header.subarray(0, 100));
|
|
106
|
-
const prefix = bytesToString(header.subarray(345, 500));
|
|
107
|
-
return prefix === "" ? name : `${prefix}/${name}`;
|
|
108
|
-
};
|
|
109
|
-
const tarLinkPathFromHeader = (header) => bytesToString(header.subarray(157, 257));
|
|
110
|
-
const trimExtendedPath = (bytes) => {
|
|
111
|
-
const value = Buffer.from(bytes).toString("utf8").replace(/\n$/g, "");
|
|
112
|
-
let end = value.length;
|
|
113
|
-
while (end > 0 && value.charCodeAt(end - 1) === 0) end -= 1;
|
|
114
|
-
return value.slice(0, end);
|
|
115
|
-
};
|
|
116
|
-
const isSafeArchivePath = (entryPath) => {
|
|
117
|
-
if (entryPath === "" || entryPath === "." || entryPath.includes("\0") || entryPath.includes("\\") || entryPath.startsWith("/") || /^[A-Za-z]:/.test(entryPath)) return false;
|
|
118
|
-
const meaningfulSegments = entryPath.split("/").filter((segment) => segment !== "" && segment !== ".");
|
|
119
|
-
return meaningfulSegments.length > 0 && !meaningfulSegments.includes("..");
|
|
120
|
-
};
|
|
121
|
-
const parsePaxRecords = (bytes) => {
|
|
122
|
-
const text = Buffer.from(bytes).toString("utf8");
|
|
123
|
-
const records = {};
|
|
124
|
-
let offset = 0;
|
|
125
|
-
while (offset < text.length) {
|
|
126
|
-
const space = text.indexOf(" ", offset);
|
|
127
|
-
if (space === -1) break;
|
|
128
|
-
const lengthText = text.slice(offset, space);
|
|
129
|
-
const recordLength = Number.parseInt(lengthText, 10);
|
|
130
|
-
if (!Number.isSafeInteger(recordLength) || recordLength <= 0) break;
|
|
131
|
-
const record = text.slice(space + 1, offset + recordLength).replace(/\n$/g, "");
|
|
132
|
-
const eq = record.indexOf("=");
|
|
133
|
-
if (eq > 0) records[record.slice(0, eq)] = record.slice(eq + 1);
|
|
134
|
-
offset += recordLength;
|
|
135
|
-
}
|
|
136
|
-
return records;
|
|
137
|
-
};
|
|
138
81
|
const tarValidationError = (detail) => new HostTreeTarError({
|
|
139
82
|
stage: "entry-validation",
|
|
140
83
|
operation: "untar",
|
|
141
84
|
detail
|
|
142
85
|
});
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
return
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
state.pendingLinkPath = trimExtendedPath(bytes);
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
const records = parsePaxRecords(bytes);
|
|
154
|
-
if (content.kind === "pax-global" && (records.path !== void 0 || records.linkpath !== void 0)) return tarValidationError("global pax path/linkpath records are not supported in snapshots");
|
|
155
|
-
if (content.kind === "pax-local") {
|
|
156
|
-
if (records.path !== void 0) state.pendingPath = records.path;
|
|
157
|
-
if (records.linkpath !== void 0) state.pendingLinkPath = records.linkpath;
|
|
158
|
-
}
|
|
159
|
-
return null;
|
|
160
|
-
};
|
|
161
|
-
const processTarValidationChunk = (state, chunk) => {
|
|
162
|
-
if (state.content === null && state.skipRemaining >= chunk.length && state.buffer.length === 0) {
|
|
163
|
-
state.skipRemaining -= chunk.length;
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
state.buffer = concatBytes(state.buffer, chunk);
|
|
167
|
-
while (state.buffer.length > 0) {
|
|
168
|
-
if (state.content !== null) {
|
|
169
|
-
const content = state.content;
|
|
170
|
-
const remaining = content.paddedSize - content.totalBytesRead;
|
|
171
|
-
const take = Math.min(remaining, state.buffer.length);
|
|
172
|
-
const contentTake = Math.max(0, Math.min(take, content.size - content.contentBytesRead));
|
|
173
|
-
if (contentTake > 0) content.chunks.push(state.buffer.subarray(0, contentTake));
|
|
174
|
-
state.content = {
|
|
175
|
-
...content,
|
|
176
|
-
contentBytesRead: content.contentBytesRead + contentTake,
|
|
177
|
-
totalBytesRead: content.totalBytesRead + take
|
|
178
|
-
};
|
|
179
|
-
state.buffer = consumeBytes(state.buffer, take);
|
|
180
|
-
if (state.content.totalBytesRead === state.content.paddedSize) {
|
|
181
|
-
const completed = state.content;
|
|
182
|
-
state.content = null;
|
|
183
|
-
const error = applyExtendedContent(state, completed);
|
|
184
|
-
if (error !== null) return error;
|
|
185
|
-
}
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
if (state.skipRemaining > 0) {
|
|
189
|
-
const take = Math.min(state.skipRemaining, state.buffer.length);
|
|
190
|
-
state.skipRemaining -= take;
|
|
191
|
-
state.buffer = consumeBytes(state.buffer, take);
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
if (state.buffer.length < TAR_BLOCK_SIZE) return null;
|
|
195
|
-
const header = state.buffer.subarray(0, TAR_BLOCK_SIZE);
|
|
196
|
-
state.buffer = consumeBytes(state.buffer, TAR_BLOCK_SIZE);
|
|
197
|
-
if (isZeroBlock(header)) continue;
|
|
198
|
-
const size = parseTarSize(header);
|
|
199
|
-
if (size === null) return tarValidationError("tar entry has an invalid size header");
|
|
200
|
-
const paddedSize = Math.ceil(size / TAR_BLOCK_SIZE) * TAR_BLOCK_SIZE;
|
|
201
|
-
const typeflag = String.fromCharCode(header[156] ?? 0).replace("\0", "");
|
|
202
|
-
if (typeflag === "x" || typeflag === "g" || typeflag === "L" || typeflag === "K") {
|
|
203
|
-
if (size > MAX_TAR_EXTENDED_PATH_BYTES) return tarValidationError("tar extended path record is too large");
|
|
204
|
-
state.content = {
|
|
205
|
-
kind: typeflag === "x" ? "pax-local" : typeflag === "g" ? "pax-global" : typeflag === "L" ? "gnu-path" : "gnu-link",
|
|
206
|
-
size,
|
|
207
|
-
paddedSize,
|
|
208
|
-
chunks: [],
|
|
209
|
-
contentBytesRead: 0,
|
|
210
|
-
totalBytesRead: 0
|
|
211
|
-
};
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
const entryPath = state.pendingPath ?? tarPathFromHeader(header);
|
|
215
|
-
state.pendingPath = null;
|
|
216
|
-
if (!isSafeArchivePath(entryPath)) return tarValidationError(`unsafe tar entry path: ${entryPath}`);
|
|
217
|
-
const linkPath = state.pendingLinkPath ?? tarLinkPathFromHeader(header);
|
|
218
|
-
state.pendingLinkPath = null;
|
|
219
|
-
if ((typeflag === "1" || typeflag === "2") && !isSafeArchivePath(linkPath)) return tarValidationError(`unsafe tar link target: ${linkPath}`);
|
|
220
|
-
state.skipRemaining = paddedSize;
|
|
221
|
-
}
|
|
222
|
-
return null;
|
|
223
|
-
};
|
|
224
|
-
const finishTarValidation = (state) => {
|
|
225
|
-
if (state.content !== null) return tarValidationError("tar ended inside an extended header");
|
|
226
|
-
if (state.skipRemaining !== 0) return tarValidationError("tar ended inside file content");
|
|
227
|
-
if (state.buffer.length !== 0 && !isZeroBlock(state.buffer)) return tarValidationError("tar ended with a partial header");
|
|
228
|
-
return null;
|
|
86
|
+
const hostTreeValidationHooks = {
|
|
87
|
+
onEntry: (entry) => {
|
|
88
|
+
if (!isSafeArchivePath(entry.path)) return tarValidationError(`unsafe tar entry path: ${entry.path}`);
|
|
89
|
+
if ((entry.typeflag === "1" || entry.typeflag === "2") && !isSafeArchivePath(entry.linkPath)) return tarValidationError(`unsafe tar link target: ${entry.linkPath}`);
|
|
90
|
+
return skipEntry();
|
|
91
|
+
},
|
|
92
|
+
onExtendedError: (detail) => tarValidationError(detail === "global pax path/linkpath records are not supported" ? "global pax path/linkpath records are not supported in snapshots" : detail)
|
|
229
93
|
};
|
|
230
94
|
const validateHostTreeTarEntries = (stream) => Effect.gen(function* () {
|
|
231
|
-
const state =
|
|
232
|
-
buffer: new Uint8Array(0),
|
|
233
|
-
skipRemaining: 0,
|
|
234
|
-
content: null,
|
|
235
|
-
pendingPath: null,
|
|
236
|
-
pendingLinkPath: null
|
|
237
|
-
};
|
|
95
|
+
const state = makeTarReaderState();
|
|
238
96
|
yield* Stream.runForEach(stream, (chunk) => {
|
|
239
|
-
const error =
|
|
97
|
+
const error = processTarChunk(state, chunk, hostTreeValidationHooks);
|
|
240
98
|
return error === null ? Effect.void : Effect.fail(error);
|
|
241
99
|
});
|
|
242
|
-
const finalError =
|
|
100
|
+
const finalError = finishTarReader(state, tarValidationError);
|
|
243
101
|
if (finalError !== null) return yield* Effect.fail(finalError);
|
|
244
102
|
});
|
|
245
103
|
/**
|
|
@@ -323,13 +181,7 @@ const untarHostTree = (stream, spec) => Effect.gen(function* () {
|
|
|
323
181
|
operation: "untar",
|
|
324
182
|
detail: `'tar' subprocess produced no stdin pipe`
|
|
325
183
|
}));
|
|
326
|
-
const validationState =
|
|
327
|
-
buffer: new Uint8Array(0),
|
|
328
|
-
skipRemaining: 0,
|
|
329
|
-
content: null,
|
|
330
|
-
pendingPath: null,
|
|
331
|
-
pendingLinkPath: null
|
|
332
|
-
};
|
|
184
|
+
const validationState = makeTarReaderState();
|
|
333
185
|
const writeChunk = (chunk) => Effect.callback((resume) => {
|
|
334
186
|
let settled = false;
|
|
335
187
|
const settle = (effect) => {
|
|
@@ -354,7 +206,7 @@ const untarHostTree = (stream, spec) => Effect.gen(function* () {
|
|
|
354
206
|
});
|
|
355
207
|
});
|
|
356
208
|
yield* Stream.runForEach(stream, (chunk) => Effect.gen(function* () {
|
|
357
|
-
const validationError =
|
|
209
|
+
const validationError = processTarChunk(validationState, chunk, hostTreeValidationHooks);
|
|
358
210
|
if (validationError !== null) return yield* Effect.fail(validationError);
|
|
359
211
|
yield* writeChunk(chunk);
|
|
360
212
|
})).pipe(Effect.catch((cause) => cause instanceof HostTreeTarError ? Effect.fail(cause) : Effect.fail(new HostTreeTarError({
|
|
@@ -363,12 +215,12 @@ const untarHostTree = (stream, spec) => Effect.gen(function* () {
|
|
|
363
215
|
detail: `source stream raised before EOF`,
|
|
364
216
|
cause
|
|
365
217
|
}))));
|
|
366
|
-
const finalValidationError =
|
|
218
|
+
const finalValidationError = finishTarReader(validationState, tarValidationError);
|
|
367
219
|
if (finalValidationError !== null) return yield* Effect.fail(finalValidationError);
|
|
368
220
|
yield* Effect.sync(() => stdin.end());
|
|
369
221
|
const status = yield* Effect.promise(() => awaitProcessExit(child));
|
|
370
222
|
if (status.code !== 0 || status.signal !== null) return yield* Effect.fail(tarExitError("untar", status, stderrChunks));
|
|
371
|
-
})
|
|
223
|
+
});
|
|
372
224
|
//#endregion
|
|
373
225
|
export { HostTreeTarError, tarHostTree, untarHostTree, validateHostTreeTarEntries };
|
|
374
226
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/substrate/runtime/host-tree-tar/index.ts"],"sourcesContent":["// host-tree-tar — stream a host filesystem subtree as a tar archive.\n//\n// Architecture §\"Substrate primitives roster\" (Open slot O21):\n// snapshot orchestrator hard-fails today on the `tarHostTree` /\n// `untarHostTree` stubs. This primitive fills the slot.\n//\n// Design:\n// - Stream-based — `tarHostTree` returns a `Stream<Uint8Array>`;\n// `untarHostTree` consumes one. The entire tree NEVER materialises\n// in memory (snapshot trees can hold gigabytes of cache state).\n// - Mode-preserving — the snapshot orchestrator carries 0o600 /\n// 0o700 mode bits on secret material (postgres keys, account\n// keystores). System `tar -p` (extract) and `tar --no-recursion`\n// (control) preserve them natively. We do NOT roll our own\n// archive writer (a TypeScript-side reimplementation would have\n// to track POSIX mode flags + extended attributes; system tar is\n// correct by default).\n// - Subprocess-based — spawn `tar` (BSD-tar on macOS, GNU-tar on\n// Linux); both honour the flags used here (`-c` / `-x` / `-f -`).\n// Exit code != 0 surfaces as `TarSpawnFailed`.\n//\n// Boundary:\n// - Substrate-level: this is a generic filesystem primitive; no\n// plugin names referenced.\n// - The caller controls which subtrees are archived (snapshot\n// orchestrator passes a list of relative paths under a parent\n// dir). Permission preservation is unconditional.\n\nimport { spawn, type ChildProcess, type StdioOptions } from 'node:child_process';\n\nimport { Effect, Schema, Scope, Stream } from 'effect';\n\nimport {\n\tawaitProcessExit,\n\tdescribeProcessExitStatus,\n\ttype ManagedProcessChild,\n\ttype ManagedProcessExitStatus,\n} from '../process-supervisor.ts';\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\n/** Tagged failure during tar / untar. `stage` discriminates the\n * precise step (spawn vs stream-read vs exit-code).\n *\n * `cause` carries the underlying error verbatim; the cascade\n * formatter walks it. */\nexport class HostTreeTarError extends Schema.TaggedErrorClass<HostTreeTarError>()(\n\t'HostTreeTarError',\n\t{\n\t\tstage: Schema.Literals([\n\t\t\t'spawn',\n\t\t\t'stream-stdout',\n\t\t\t'stream-stdin',\n\t\t\t'exit-code',\n\t\t\t'entry-validation',\n\t\t\t'no-subtrees',\n\t\t]),\n\t\toperation: Schema.Literals(['tar', 'untar']),\n\t\tdetail: Schema.String,\n\t\t/** Captured stderr from the `tar` subprocess (last ~4 KB). */\n\t\tstderrTail: Schema.optional(Schema.String),\n\t\texitCode: Schema.optional(Schema.Number),\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n// ---------------------------------------------------------------------------\n// Tar one or more subtrees → Stream<Uint8Array>\n// ---------------------------------------------------------------------------\n\n/** Spec for `tarHostTree`.\n *\n * - `parentDir`: directory the relative paths are anchored under.\n * `tar -C <parentDir>` runs the archive with this as cwd, so the\n * archive entries are stored relative to `parentDir`.\n * - `relPaths`: subtree paths relative to `parentDir`. Empty list\n * fails fast (`stage: 'no-subtrees'`) — the caller is responsible\n * for the host-tree-included guard. */\nexport interface TarHostTreeSpec {\n\treadonly parentDir: string;\n\treadonly relPaths: ReadonlyArray<string>;\n}\n\nconst TAR_STDERR_TAIL_BYTES = 4096;\n\nconst collectStderrTail = (chunks: Array<Uint8Array>): string => {\n\tif (chunks.length === 0) return '';\n\tlet total = 0;\n\tfor (let i = chunks.length - 1; i >= 0; i--) {\n\t\ttotal += chunks[i]!.length;\n\t\tif (total >= TAR_STDERR_TAIL_BYTES) break;\n\t}\n\tconst bytes = Buffer.concat(chunks);\n\treturn bytes.subarray(Math.max(0, bytes.length - TAR_STDERR_TAIL_BYTES)).toString('utf8');\n};\n\nconst tarExitError = (\n\toperation: 'tar' | 'untar',\n\tstatus: ManagedProcessExitStatus,\n\tstderrChunks: Array<Uint8Array>,\n): HostTreeTarError =>\n\tnew HostTreeTarError({\n\t\tstage: 'exit-code',\n\t\toperation,\n\t\texitCode: status.code ?? -1,\n\t\tdetail: `'tar' exited with ${describeProcessExitStatus(status)}`,\n\t\tstderrTail: collectStderrTail(stderrChunks),\n\t});\n\n/** Spawn `tar` with a bounded stderr ring buffer and a SIGKILL-on-\n * scope-close finalizer. Both `tarHostTree` and `untarHostTree` share\n * this body verbatim — only the args, stdio direction (which pipe is\n * the data channel), and the `operation` tag differ. The data pipe\n * (`stdout` for tar, `stdin` for untar) is read off the returned\n * `child` by the caller; stderr is always piped so the exit-code\n * failure can carry the captured tail.\n *\n * The stderr listener is removed and a live child is killed on scope\n * close — the finalizer binds to the surrounding Scope (the stream's\n * lifecycle for tar, the consuming Effect's for untar). */\nconst spawnTar = (\n\toperation: 'tar' | 'untar',\n\targs: ReadonlyArray<string>,\n\tstdio: StdioOptions,\n): Effect.Effect<\n\t{ readonly child: ChildProcess; readonly stderrChunks: Array<Uint8Array> },\n\tHostTreeTarError,\n\tScope.Scope\n> =>\n\tEffect.gen(function* () {\n\t\tconst child = yield* Effect.try({\n\t\t\ttry: () => spawn('tar', [...args], { stdio }),\n\t\t\tcatch: (cause) =>\n\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\tstage: 'spawn',\n\t\t\t\t\toperation,\n\t\t\t\t\tdetail: `failed to spawn 'tar ${args.join(' ')}'`,\n\t\t\t\t\tcause,\n\t\t\t\t}),\n\t\t});\n\n\t\t// Stderr collection lives outside the data stream — we accumulate\n\t\t// the tail so an exit-code failure carries actionable context. The\n\t\t// stderr listener is removed on scope close.\n\t\tconst stderrChunks: Array<Uint8Array> = [];\n\t\tconst onStderr = (chunk: Buffer): void => {\n\t\t\tstderrChunks.push(chunk);\n\t\t\tif (stderrChunks.length > 256) stderrChunks.splice(0, stderrChunks.length - 256);\n\t\t};\n\t\tchild.stderr?.on('data', onStderr);\n\n\t\t// Finalise the subprocess on scope close: kill if alive, drop\n\t\t// listeners either way.\n\t\tyield* Effect.addFinalizer(() =>\n\t\t\tEffect.sync(() => {\n\t\t\t\tchild.stderr?.off('data', onStderr);\n\t\t\t\tif (child.exitCode === null && child.signalCode === null) {\n\t\t\t\t\tchild.kill('SIGKILL');\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\n\t\treturn { child, stderrChunks };\n\t});\n\nconst TAR_BLOCK_SIZE = 512;\nconst MAX_TAR_EXTENDED_PATH_BYTES = 1024 * 1024;\n\nconst bytesToString = (bytes: Uint8Array): string => {\n\tconst nul = bytes.indexOf(0);\n\tconst end = nul === -1 ? bytes.length : nul;\n\treturn Buffer.from(bytes.subarray(0, end)).toString('utf8');\n};\n\nconst concatBytes = (a: Uint8Array, b: Uint8Array): Uint8Array => {\n\tif (a.length === 0) return b;\n\tif (b.length === 0) return a;\n\tconst out = new Uint8Array(a.length + b.length);\n\tout.set(a, 0);\n\tout.set(b, a.length);\n\treturn out;\n};\n\nconst consumeBytes = (buffer: Uint8Array, count: number): Uint8Array => buffer.subarray(count);\n\nconst isZeroBlock = (block: Uint8Array): boolean => block.every((byte) => byte === 0);\n\nconst parseTarSize = (header: Uint8Array): number | null => {\n\tconst raw = bytesToString(header.subarray(124, 136)).trim();\n\tif (raw === '') return 0;\n\tif (!/^[0-7]+$/.test(raw)) return null;\n\tconst parsed = Number.parseInt(raw, 8);\n\treturn Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : null;\n};\n\nconst tarPathFromHeader = (header: Uint8Array): string => {\n\tconst name = bytesToString(header.subarray(0, 100));\n\tconst prefix = bytesToString(header.subarray(345, 500));\n\treturn prefix === '' ? name : `${prefix}/${name}`;\n};\n\nconst tarLinkPathFromHeader = (header: Uint8Array): string =>\n\tbytesToString(header.subarray(157, 257));\n\nconst trimExtendedPath = (bytes: Uint8Array): string => {\n\tconst value = Buffer.from(bytes).toString('utf8').replace(/\\n$/g, '');\n\tlet end = value.length;\n\twhile (end > 0 && value.charCodeAt(end - 1) === 0) end -= 1;\n\treturn value.slice(0, end);\n};\n\nconst isSafeArchivePath = (entryPath: string): boolean => {\n\tif (\n\t\tentryPath === '' ||\n\t\tentryPath === '.' ||\n\t\tentryPath.includes('\\0') ||\n\t\tentryPath.includes('\\\\') ||\n\t\tentryPath.startsWith('/') ||\n\t\t/^[A-Za-z]:/.test(entryPath)\n\t) {\n\t\treturn false;\n\t}\n\tconst meaningfulSegments = entryPath\n\t\t.split('/')\n\t\t.filter((segment) => segment !== '' && segment !== '.');\n\treturn meaningfulSegments.length > 0 && !meaningfulSegments.includes('..');\n};\n\nconst parsePaxRecords = (bytes: Uint8Array): Record<string, string> => {\n\tconst text = Buffer.from(bytes).toString('utf8');\n\tconst records: Record<string, string> = {};\n\tlet offset = 0;\n\twhile (offset < text.length) {\n\t\tconst space = text.indexOf(' ', offset);\n\t\tif (space === -1) break;\n\t\tconst lengthText = text.slice(offset, space);\n\t\tconst recordLength = Number.parseInt(lengthText, 10);\n\t\tif (!Number.isSafeInteger(recordLength) || recordLength <= 0) break;\n\t\tconst record = text.slice(space + 1, offset + recordLength).replace(/\\n$/g, '');\n\t\tconst eq = record.indexOf('=');\n\t\tif (eq > 0) {\n\t\t\trecords[record.slice(0, eq)] = record.slice(eq + 1);\n\t\t}\n\t\toffset += recordLength;\n\t}\n\treturn records;\n};\n\ninterface TarContentState {\n\treadonly kind: 'pax-local' | 'pax-global' | 'gnu-path' | 'gnu-link';\n\treadonly size: number;\n\treadonly paddedSize: number;\n\treadonly chunks: Array<Uint8Array>;\n\treadonly contentBytesRead: number;\n\treadonly totalBytesRead: number;\n}\n\ninterface TarValidationState {\n\tbuffer: Uint8Array;\n\tskipRemaining: number;\n\tcontent: TarContentState | null;\n\tpendingPath: string | null;\n\tpendingLinkPath: string | null;\n}\n\nconst tarValidationError = (detail: string): HostTreeTarError =>\n\tnew HostTreeTarError({\n\t\tstage: 'entry-validation',\n\t\toperation: 'untar',\n\t\tdetail,\n\t});\n\nconst applyExtendedContent = (\n\tstate: TarValidationState,\n\tcontent: TarContentState,\n): HostTreeTarError | null => {\n\tconst bytes =\n\t\tcontent.chunks.length === 1\n\t\t\t? content.chunks[0]!\n\t\t\t: Buffer.concat(content.chunks.map((chunk) => Buffer.from(chunk)));\n\tif (content.kind === 'gnu-path') {\n\t\tstate.pendingPath = trimExtendedPath(bytes);\n\t\treturn null;\n\t}\n\tif (content.kind === 'gnu-link') {\n\t\tstate.pendingLinkPath = trimExtendedPath(bytes);\n\t\treturn null;\n\t}\n\tconst records = parsePaxRecords(bytes);\n\tif (\n\t\tcontent.kind === 'pax-global' &&\n\t\t(records.path !== undefined || records.linkpath !== undefined)\n\t) {\n\t\treturn tarValidationError('global pax path/linkpath records are not supported in snapshots');\n\t}\n\tif (content.kind === 'pax-local') {\n\t\tif (records.path !== undefined) state.pendingPath = records.path;\n\t\tif (records.linkpath !== undefined) state.pendingLinkPath = records.linkpath;\n\t}\n\treturn null;\n};\n\nconst processTarValidationChunk = (\n\tstate: TarValidationState,\n\tchunk: Uint8Array,\n): HostTreeTarError | null => {\n\t// Fast path: we're mid-skip over a large file body and this chunk is\n\t// fully consumed by the skip. Avoid `concatBytes` (O(N) per call →\n\t// O(N²) across a multi-MB body streamed in 64KB chunks). The buffer\n\t// must be empty so we don't re-order bytes relative to the parser.\n\tif (\n\t\tstate.content === null &&\n\t\tstate.skipRemaining >= chunk.length &&\n\t\tstate.buffer.length === 0\n\t) {\n\t\tstate.skipRemaining -= chunk.length;\n\t\treturn null;\n\t}\n\tstate.buffer = concatBytes(state.buffer, chunk);\n\twhile (state.buffer.length > 0) {\n\t\tif (state.content !== null) {\n\t\t\tconst content = state.content;\n\t\t\tconst remaining = content.paddedSize - content.totalBytesRead;\n\t\t\tconst take = Math.min(remaining, state.buffer.length);\n\t\t\tconst contentTake = Math.max(0, Math.min(take, content.size - content.contentBytesRead));\n\t\t\tif (contentTake > 0) {\n\t\t\t\tcontent.chunks.push(state.buffer.subarray(0, contentTake));\n\t\t\t}\n\t\t\tstate.content = {\n\t\t\t\t...content,\n\t\t\t\tcontentBytesRead: content.contentBytesRead + contentTake,\n\t\t\t\ttotalBytesRead: content.totalBytesRead + take,\n\t\t\t};\n\t\t\tstate.buffer = consumeBytes(state.buffer, take);\n\t\t\tif (state.content.totalBytesRead === state.content.paddedSize) {\n\t\t\t\tconst completed = state.content;\n\t\t\t\tstate.content = null;\n\t\t\t\tconst error = applyExtendedContent(state, completed);\n\t\t\t\tif (error !== null) return error;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (state.skipRemaining > 0) {\n\t\t\tconst take = Math.min(state.skipRemaining, state.buffer.length);\n\t\t\tstate.skipRemaining -= take;\n\t\t\tstate.buffer = consumeBytes(state.buffer, take);\n\t\t\tcontinue;\n\t\t}\n\t\tif (state.buffer.length < TAR_BLOCK_SIZE) return null;\n\t\tconst header = state.buffer.subarray(0, TAR_BLOCK_SIZE);\n\t\tstate.buffer = consumeBytes(state.buffer, TAR_BLOCK_SIZE);\n\t\tif (isZeroBlock(header)) continue;\n\n\t\tconst size = parseTarSize(header);\n\t\tif (size === null) return tarValidationError('tar entry has an invalid size header');\n\t\tconst paddedSize = Math.ceil(size / TAR_BLOCK_SIZE) * TAR_BLOCK_SIZE;\n\t\tconst typeflag = String.fromCharCode(header[156] ?? 0).replace('\\0', '');\n\t\tif (typeflag === 'x' || typeflag === 'g' || typeflag === 'L' || typeflag === 'K') {\n\t\t\tif (size > MAX_TAR_EXTENDED_PATH_BYTES) {\n\t\t\t\treturn tarValidationError('tar extended path record is too large');\n\t\t\t}\n\t\t\tstate.content = {\n\t\t\t\tkind:\n\t\t\t\t\ttypeflag === 'x'\n\t\t\t\t\t\t? 'pax-local'\n\t\t\t\t\t\t: typeflag === 'g'\n\t\t\t\t\t\t\t? 'pax-global'\n\t\t\t\t\t\t\t: typeflag === 'L'\n\t\t\t\t\t\t\t\t? 'gnu-path'\n\t\t\t\t\t\t\t\t: 'gnu-link',\n\t\t\t\tsize,\n\t\t\t\tpaddedSize,\n\t\t\t\tchunks: [],\n\t\t\t\tcontentBytesRead: 0,\n\t\t\t\ttotalBytesRead: 0,\n\t\t\t};\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst entryPath = state.pendingPath ?? tarPathFromHeader(header);\n\t\tstate.pendingPath = null;\n\t\tif (!isSafeArchivePath(entryPath)) {\n\t\t\treturn tarValidationError(`unsafe tar entry path: ${entryPath}`);\n\t\t}\n\t\tconst linkPath = state.pendingLinkPath ?? tarLinkPathFromHeader(header);\n\t\tstate.pendingLinkPath = null;\n\t\tif ((typeflag === '1' || typeflag === '2') && !isSafeArchivePath(linkPath)) {\n\t\t\treturn tarValidationError(`unsafe tar link target: ${linkPath}`);\n\t\t}\n\t\tstate.skipRemaining = paddedSize;\n\t}\n\treturn null;\n};\n\nconst finishTarValidation = (state: TarValidationState): HostTreeTarError | null => {\n\tif (state.content !== null) return tarValidationError('tar ended inside an extended header');\n\tif (state.skipRemaining !== 0) return tarValidationError('tar ended inside file content');\n\tif (state.buffer.length !== 0 && !isZeroBlock(state.buffer)) {\n\t\treturn tarValidationError('tar ended with a partial header');\n\t}\n\treturn null;\n};\n\nexport const validateHostTreeTarEntries = <R>(\n\tstream: Stream.Stream<Uint8Array, HostTreeTarError, R>,\n): Effect.Effect<void, HostTreeTarError, R> =>\n\tEffect.gen(function* () {\n\t\tconst state: TarValidationState = {\n\t\t\tbuffer: new Uint8Array(0),\n\t\t\tskipRemaining: 0,\n\t\t\tcontent: null,\n\t\t\tpendingPath: null,\n\t\t\tpendingLinkPath: null,\n\t\t};\n\t\tyield* Stream.runForEach(stream, (chunk) => {\n\t\t\tconst error = processTarValidationChunk(state, chunk);\n\t\t\treturn error === null ? Effect.void : Effect.fail(error);\n\t\t});\n\t\tconst finalError = finishTarValidation(state);\n\t\tif (finalError !== null) return yield* Effect.fail(finalError);\n\t});\n\n/**\n * Stream the tar archive of `relPaths` (relative to `parentDir`) as a\n * `Stream<Uint8Array>`. Mode bits are preserved.\n *\n * The subprocess is bound to the consuming Scope: closing the scope\n * before the stream finishes kills the subprocess and surfaces\n * `stream-stdout` if the consumer was mid-read.\n *\n * Caller consumes via `Stream.run(...)` — e.g. pipe to a file via\n * `Stream.run(stream, Sink.fromWritable(() => createWriteStream(...)))`\n * or to an in-memory chunk array for the integrity hash.\n */\nexport const tarHostTree = (\n\tspec: TarHostTreeSpec,\n): Stream.Stream<Uint8Array, HostTreeTarError, never> =>\n\t// `Stream.unwrap` provides the channel's surrounding scope to the\n\t// inner Effect (see `effect/src/Channel.ts:7918` —\n\t// `Scope.provide(scope)`), so `Effect.addFinalizer` below binds to\n\t// the stream's lifecycle: the subprocess kill-on-close fires when\n\t// the stream terminates. The R-channel `Exclude<R, Scope>` reads\n\t// \"scope satisfied by the stream\" — not \"scope dropped\".\n\tStream.unwrap(\n\t\tEffect.gen(function* () {\n\t\t\tif (spec.relPaths.length === 0) {\n\t\t\t\treturn Stream.fail(\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'no-subtrees',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: 'tarHostTree called with empty relPaths list',\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Producer-boundary path defense — mirror the extraction-side\n\t\t\t// `isSafeArchivePath`. The caller controls `relPaths`, and they\n\t\t\t// are spliced straight into `tar -C <parentDir> … relPaths`. An\n\t\t\t// absolute path or a `..` segment would archive a subtree\n\t\t\t// outside `parentDir` (and on extraction escape `target`). We\n\t\t\t// reject at the producer rather than relying on the consumer's\n\t\t\t// entry validation alone.\n\t\t\tconst unsafe = spec.relPaths.find((relPath) => !isSafeArchivePath(relPath));\n\t\t\tif (unsafe !== undefined) {\n\t\t\t\treturn Stream.fail(\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'entry-validation',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: `unsafe tar relPath (absolute or contains '..'): ${unsafe}`,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// `-c` create, `-f -` write to stdout, `-C <dir>` cd-before,\n\t\t\t// `-p` preserve permissions (matches BSD-tar + GNU-tar). The\n\t\t\t// subprocess emits its own bytes; we don't apply any\n\t\t\t// transcoding.\n\t\t\tconst args = ['-c', '-f', '-', '-C', spec.parentDir, '-p', ...spec.relPaths];\n\t\t\tconst { child, stderrChunks } = yield* spawnTar('tar', args, ['ignore', 'pipe', 'pipe']);\n\n\t\t\t// Wrap stdout (a Node Readable) as a Stream<Uint8Array>.\n\t\t\t// `Stream.fromReadable` is the substrate-blessed shape.\n\t\t\tconst stdout = child.stdout;\n\t\t\tif (stdout === null) {\n\t\t\t\treturn Stream.fail(\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'spawn',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: `'tar' subprocess produced no stdout pipe`,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Node `Readable` is an `AsyncIterable<Buffer>`; `Buffer`\n\t\t\t// extends `Uint8Array` structurally. The cast asserts the\n\t\t\t// nominal-vs-structural gap at the Node boundary and stays\n\t\t\t// off the user-facing API.\n\t\t\tconst dataStream = Stream.fromAsyncIterable(\n\t\t\t\tstdout as unknown as AsyncIterable<Uint8Array>,\n\t\t\t\t(cause) =>\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'stream-stdout',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: `read from tar stdout failed`,\n\t\t\t\t\t\tcause,\n\t\t\t\t\t}),\n\t\t\t);\n\n\t\t\t// After stdout closes, await the subprocess exit and surface\n\t\t\t// non-zero codes with the captured stderr tail. This appends\n\t\t\t// a \"tail\" sub-stream that emits zero bytes but can fail —\n\t\t\t// the consumer's `Stream.run` sees the exit-code failure\n\t\t\t// uniformly with stream-read failures.\n\t\t\tconst exitGate: Stream.Stream<Uint8Array, HostTreeTarError, never> = Stream.fromEffect(\n\t\t\t\tEffect.promise(() => awaitProcessExit(child as ManagedProcessChild)).pipe(\n\t\t\t\t\tEffect.flatMap((status) =>\n\t\t\t\t\t\tstatus.code === 0 && status.signal === null\n\t\t\t\t\t\t\t? Effect.succeed(new Uint8Array(0))\n\t\t\t\t\t\t\t: Effect.fail(tarExitError('tar', status, stderrChunks)),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t).pipe(Stream.filter((bytes) => bytes.length > 0));\n\n\t\t\treturn Stream.concat(dataStream, exitGate);\n\t\t}),\n\t);\n\n// ---------------------------------------------------------------------------\n// Untar a Stream<Uint8Array> into a target directory\n// ---------------------------------------------------------------------------\n\nexport interface UntarHostTreeSpec {\n\treadonly target: string;\n}\n\n/**\n * Extract a tar `Stream<Uint8Array>` into `target`, preserving mode\n * bits. The target directory MUST already exist; the caller is\n * responsible (stage-and-swap creates the staging directory before\n * calling).\n *\n * The subprocess consumes from the stream and writes into `target`;\n * scope close kills the subprocess. The Effect resolves when `tar`\n * exits cleanly (code 0).\n */\nexport const untarHostTree = <R>(\n\tstream: Stream.Stream<Uint8Array, HostTreeTarError, R>,\n\tspec: UntarHostTreeSpec,\n): Effect.Effect<void, HostTreeTarError, R | Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\tconst args = ['-x', '-f', '-', '-C', spec.target, '-p'];\n\t\tconst { child, stderrChunks } = yield* spawnTar('untar', args, ['pipe', 'ignore', 'pipe']);\n\n\t\tconst stdin = child.stdin;\n\t\tif (stdin === null) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\tstage: 'spawn',\n\t\t\t\t\toperation: 'untar',\n\t\t\t\t\tdetail: `'tar' subprocess produced no stdin pipe`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst validationState: TarValidationState = {\n\t\t\tbuffer: new Uint8Array(0),\n\t\t\tskipRemaining: 0,\n\t\t\tcontent: null,\n\t\t\tpendingPath: null,\n\t\t\tpendingLinkPath: null,\n\t\t};\n\n\t\tconst writeChunk = (chunk: Uint8Array): Effect.Effect<void, HostTreeTarError> =>\n\t\t\tEffect.callback<void, HostTreeTarError>((resume) => {\n\t\t\t\tlet settled = false;\n\t\t\t\tconst settle = (effect: Effect.Effect<void, HostTreeTarError>): void => {\n\t\t\t\t\tif (!settled) {\n\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\tresume(effect);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tconst onDrain = (): void => settle(Effect.void);\n\t\t\t\tconst ok = stdin.write(chunk, (cause) => {\n\t\t\t\t\tstdin.off('drain', onDrain);\n\t\t\t\t\tif (cause) {\n\t\t\t\t\t\tsettle(\n\t\t\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\t\t\t\tstage: 'stream-stdin',\n\t\t\t\t\t\t\t\t\toperation: 'untar',\n\t\t\t\t\t\t\t\t\tdetail: `write to tar stdin failed`,\n\t\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsettle(Effect.void);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif (!ok) {\n\t\t\t\t\tstdin.once('drain', onDrain);\n\t\t\t\t}\n\t\t\t\t// Interrupt while parked on backpressure must remove the\n\t\t\t\t// drain listener so it can't resume an interrupted fiber.\n\t\t\t\treturn Effect.sync(() => {\n\t\t\t\t\tstdin.off('drain', onDrain);\n\t\t\t\t});\n\t\t\t});\n\n\t\t// Pump the source stream into tar's stdin. Each chunk is\n\t\t// validated before it is handed to the tar subprocess, so unsafe\n\t\t// archive paths are rejected at the extraction boundary even if a\n\t\t// caller skipped any earlier preflight validation.\n\t\tyield* Stream.runForEach(stream, (chunk) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst validationError = processTarValidationChunk(validationState, chunk);\n\t\t\t\tif (validationError !== null) {\n\t\t\t\t\treturn yield* Effect.fail(validationError);\n\t\t\t\t}\n\t\t\t\tyield* writeChunk(chunk);\n\t\t\t}),\n\t\t).pipe(\n\t\t\tEffect.catch((cause) =>\n\t\t\t\tcause instanceof HostTreeTarError\n\t\t\t\t\t? Effect.fail(cause)\n\t\t\t\t\t: Effect.fail(\n\t\t\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\t\t\tstage: 'stream-stdin',\n\t\t\t\t\t\t\t\toperation: 'untar',\n\t\t\t\t\t\t\t\tdetail: `source stream raised before EOF`,\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t),\n\t\t);\n\n\t\tconst finalValidationError = finishTarValidation(validationState);\n\t\tif (finalValidationError !== null) {\n\t\t\treturn yield* Effect.fail(finalValidationError);\n\t\t}\n\n\t\t// Close stdin so tar sees EOF and exits.\n\t\tyield* Effect.sync(() => stdin.end());\n\n\t\t// Await exit; non-zero surfaces with stderr tail.\n\t\tconst status = yield* Effect.promise(() => awaitProcessExit(child as ManagedProcessChild));\n\t\tif (status.code !== 0 || status.signal !== null) {\n\t\t\treturn yield* Effect.fail(tarExitError('untar', status, stderrChunks));\n\t\t}\n\t}).pipe(Effect.withSpan('substrate.host-tree-tar.untar'));\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/substrate/runtime/host-tree-tar/index.ts"],"sourcesContent":["// host-tree-tar — stream a host filesystem subtree as a tar archive.\n//\n// Architecture §\"Substrate primitives roster\" (Open slot O21):\n// snapshot orchestrator hard-fails today on the `tarHostTree` /\n// `untarHostTree` stubs. This primitive fills the slot.\n//\n// Design:\n// - Stream-based — `tarHostTree` returns a `Stream<Uint8Array>`;\n// `untarHostTree` consumes one. The entire tree NEVER materialises\n// in memory (snapshot trees can hold gigabytes of cache state).\n// - Mode-preserving — the snapshot orchestrator carries 0o600 /\n// 0o700 mode bits on secret material (postgres keys, account\n// keystores). System `tar -p` (extract) and `tar --no-recursion`\n// (control) preserve them natively. We do NOT roll our own\n// archive writer (a TypeScript-side reimplementation would have\n// to track POSIX mode flags + extended attributes; system tar is\n// correct by default).\n// - Subprocess-based — spawn `tar` (BSD-tar on macOS, GNU-tar on\n// Linux); both honour the flags used here (`-c` / `-x` / `-f -`).\n// Exit code != 0 surfaces as `TarSpawnFailed`.\n//\n// Boundary:\n// - Substrate-level: this is a generic filesystem primitive; no\n// plugin names referenced.\n// - The caller controls which subtrees are archived (snapshot\n// orchestrator passes a list of relative paths under a parent\n// dir). Permission preservation is unconditional.\n\nimport { spawn, type ChildProcess, type StdioOptions } from 'node:child_process';\n\nimport { Effect, Schema, Scope, Stream } from 'effect';\n\nimport {\n\tawaitProcessExit,\n\tdescribeProcessExitStatus,\n\ttype ManagedProcessChild,\n\ttype ManagedProcessExitStatus,\n} from '../process-supervisor.ts';\nimport {\n\tfinishTarReader,\n\tisSafeArchivePath,\n\tmakeTarReaderState,\n\tprocessTarChunk,\n\tskipEntry,\n\ttype TarEntry,\n\ttype TarEntryDirective,\n} from '../tar/reader.ts';\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\n/** Tagged failure during tar / untar. `stage` discriminates the\n * precise step (spawn vs stream-read vs exit-code).\n *\n * `cause` carries the underlying error verbatim; the cascade\n * formatter walks it. */\nexport class HostTreeTarError extends Schema.TaggedErrorClass<HostTreeTarError>()(\n\t'HostTreeTarError',\n\t{\n\t\tstage: Schema.Literals([\n\t\t\t'spawn',\n\t\t\t'stream-stdout',\n\t\t\t'stream-stdin',\n\t\t\t'exit-code',\n\t\t\t'entry-validation',\n\t\t\t'no-subtrees',\n\t\t]),\n\t\toperation: Schema.Literals(['tar', 'untar']),\n\t\tdetail: Schema.String,\n\t\t/** Captured stderr from the `tar` subprocess (last ~4 KB). */\n\t\tstderrTail: Schema.optional(Schema.String),\n\t\texitCode: Schema.optional(Schema.Number),\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n// ---------------------------------------------------------------------------\n// Tar one or more subtrees → Stream<Uint8Array>\n// ---------------------------------------------------------------------------\n\n/** Spec for `tarHostTree`.\n *\n * - `parentDir`: directory the relative paths are anchored under.\n * `tar -C <parentDir>` runs the archive with this as cwd, so the\n * archive entries are stored relative to `parentDir`.\n * - `relPaths`: subtree paths relative to `parentDir`. Empty list\n * fails fast (`stage: 'no-subtrees'`) — the caller is responsible\n * for the host-tree-included guard. */\nexport interface TarHostTreeSpec {\n\treadonly parentDir: string;\n\treadonly relPaths: ReadonlyArray<string>;\n}\n\nconst TAR_STDERR_TAIL_BYTES = 4096;\n\nconst collectStderrTail = (chunks: Array<Uint8Array>): string => {\n\tif (chunks.length === 0) return '';\n\tlet total = 0;\n\tfor (let i = chunks.length - 1; i >= 0; i--) {\n\t\ttotal += chunks[i]!.length;\n\t\tif (total >= TAR_STDERR_TAIL_BYTES) break;\n\t}\n\tconst bytes = Buffer.concat(chunks);\n\treturn bytes.subarray(Math.max(0, bytes.length - TAR_STDERR_TAIL_BYTES)).toString('utf8');\n};\n\nconst tarExitError = (\n\toperation: 'tar' | 'untar',\n\tstatus: ManagedProcessExitStatus,\n\tstderrChunks: Array<Uint8Array>,\n): HostTreeTarError =>\n\tnew HostTreeTarError({\n\t\tstage: 'exit-code',\n\t\toperation,\n\t\texitCode: status.code ?? -1,\n\t\tdetail: `'tar' exited with ${describeProcessExitStatus(status)}`,\n\t\tstderrTail: collectStderrTail(stderrChunks),\n\t});\n\n/** Spawn `tar` with a bounded stderr ring buffer and a SIGKILL-on-\n * scope-close finalizer. Both `tarHostTree` and `untarHostTree` share\n * this body verbatim — only the args, stdio direction (which pipe is\n * the data channel), and the `operation` tag differ. The data pipe\n * (`stdout` for tar, `stdin` for untar) is read off the returned\n * `child` by the caller; stderr is always piped so the exit-code\n * failure can carry the captured tail.\n *\n * The stderr listener is removed and a live child is killed on scope\n * close — the finalizer binds to the surrounding Scope (the stream's\n * lifecycle for tar, the consuming Effect's for untar). */\nconst spawnTar = (\n\toperation: 'tar' | 'untar',\n\targs: ReadonlyArray<string>,\n\tstdio: StdioOptions,\n): Effect.Effect<\n\t{ readonly child: ChildProcess; readonly stderrChunks: Array<Uint8Array> },\n\tHostTreeTarError,\n\tScope.Scope\n> =>\n\tEffect.gen(function* () {\n\t\tconst child = yield* Effect.try({\n\t\t\ttry: () => spawn('tar', [...args], { stdio }),\n\t\t\tcatch: (cause) =>\n\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\tstage: 'spawn',\n\t\t\t\t\toperation,\n\t\t\t\t\tdetail: `failed to spawn 'tar ${args.join(' ')}'`,\n\t\t\t\t\tcause,\n\t\t\t\t}),\n\t\t});\n\n\t\t// Stderr collection lives outside the data stream — we accumulate\n\t\t// the tail so an exit-code failure carries actionable context. The\n\t\t// stderr listener is removed on scope close.\n\t\tconst stderrChunks: Array<Uint8Array> = [];\n\t\tconst onStderr = (chunk: Buffer): void => {\n\t\t\tstderrChunks.push(chunk);\n\t\t\tif (stderrChunks.length > 256) stderrChunks.splice(0, stderrChunks.length - 256);\n\t\t};\n\t\tchild.stderr?.on('data', onStderr);\n\n\t\t// Finalise the subprocess on scope close: kill if alive, drop\n\t\t// listeners either way.\n\t\tyield* Effect.addFinalizer(() =>\n\t\t\tEffect.sync(() => {\n\t\t\t\tchild.stderr?.off('data', onStderr);\n\t\t\t\tif (child.exitCode === null && child.signalCode === null) {\n\t\t\t\t\tchild.kill('SIGKILL');\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\n\t\treturn { child, stderrChunks };\n\t});\n\nconst tarValidationError = (detail: string): HostTreeTarError =>\n\tnew HostTreeTarError({\n\t\tstage: 'entry-validation',\n\t\toperation: 'untar',\n\t\tdetail,\n\t});\n\n// Host-tree-specific validation layer on top of the shared tar reader.\n// The reader resolves entries (pax/gnu paths applied); this hook\n// rejects unsafe entry paths and unsafe hardlink/symlink targets, then\n// skips the body. The reader owns the block discipline and pax/gnu\n// parsing; the validator never reads file content.\nconst hostTreeValidationHooks = {\n\tonEntry: (entry: TarEntry): TarEntryDirective | HostTreeTarError => {\n\t\tif (!isSafeArchivePath(entry.path)) {\n\t\t\treturn tarValidationError(`unsafe tar entry path: ${entry.path}`);\n\t\t}\n\t\tif ((entry.typeflag === '1' || entry.typeflag === '2') && !isSafeArchivePath(entry.linkPath)) {\n\t\t\treturn tarValidationError(`unsafe tar link target: ${entry.linkPath}`);\n\t\t}\n\t\treturn skipEntry();\n\t},\n\tonExtendedError: (detail: string): HostTreeTarError =>\n\t\ttarValidationError(\n\t\t\tdetail === 'global pax path/linkpath records are not supported'\n\t\t\t\t? 'global pax path/linkpath records are not supported in snapshots'\n\t\t\t\t: detail,\n\t\t),\n} as const;\n\nexport const validateHostTreeTarEntries = <R>(\n\tstream: Stream.Stream<Uint8Array, HostTreeTarError, R>,\n): Effect.Effect<void, HostTreeTarError, R> =>\n\tEffect.gen(function* () {\n\t\tconst state = makeTarReaderState();\n\t\tyield* Stream.runForEach(stream, (chunk) => {\n\t\t\tconst error = processTarChunk(state, chunk, hostTreeValidationHooks);\n\t\t\treturn error === null ? Effect.void : Effect.fail(error);\n\t\t});\n\t\tconst finalError = finishTarReader(state, tarValidationError);\n\t\tif (finalError !== null) return yield* Effect.fail(finalError);\n\t});\n\n/**\n * Stream the tar archive of `relPaths` (relative to `parentDir`) as a\n * `Stream<Uint8Array>`. Mode bits are preserved.\n *\n * The subprocess is bound to the consuming Scope: closing the scope\n * before the stream finishes kills the subprocess and surfaces\n * `stream-stdout` if the consumer was mid-read.\n *\n * Caller consumes via `Stream.run(...)` — e.g. pipe to a file via\n * `Stream.run(stream, Sink.fromWritable(() => createWriteStream(...)))`\n * or to an in-memory chunk array for the integrity hash.\n */\nexport const tarHostTree = (\n\tspec: TarHostTreeSpec,\n): Stream.Stream<Uint8Array, HostTreeTarError, never> =>\n\t// `Stream.unwrap` provides the channel's surrounding scope to the\n\t// inner Effect (see `effect/src/Channel.ts:7918` —\n\t// `Scope.provide(scope)`), so `Effect.addFinalizer` below binds to\n\t// the stream's lifecycle: the subprocess kill-on-close fires when\n\t// the stream terminates. The R-channel `Exclude<R, Scope>` reads\n\t// \"scope satisfied by the stream\" — not \"scope dropped\".\n\tStream.unwrap(\n\t\tEffect.gen(function* () {\n\t\t\tif (spec.relPaths.length === 0) {\n\t\t\t\treturn Stream.fail(\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'no-subtrees',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: 'tarHostTree called with empty relPaths list',\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Producer-boundary path defense — mirror the extraction-side\n\t\t\t// `isSafeArchivePath`. The caller controls `relPaths`, and they\n\t\t\t// are spliced straight into `tar -C <parentDir> … relPaths`. An\n\t\t\t// absolute path or a `..` segment would archive a subtree\n\t\t\t// outside `parentDir` (and on extraction escape `target`). We\n\t\t\t// reject at the producer rather than relying on the consumer's\n\t\t\t// entry validation alone.\n\t\t\tconst unsafe = spec.relPaths.find((relPath) => !isSafeArchivePath(relPath));\n\t\t\tif (unsafe !== undefined) {\n\t\t\t\treturn Stream.fail(\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'entry-validation',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: `unsafe tar relPath (absolute or contains '..'): ${unsafe}`,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// `-c` create, `-f -` write to stdout, `-C <dir>` cd-before,\n\t\t\t// `-p` preserve permissions (matches BSD-tar + GNU-tar). The\n\t\t\t// subprocess emits its own bytes; we don't apply any\n\t\t\t// transcoding.\n\t\t\tconst args = ['-c', '-f', '-', '-C', spec.parentDir, '-p', ...spec.relPaths];\n\t\t\tconst { child, stderrChunks } = yield* spawnTar('tar', args, ['ignore', 'pipe', 'pipe']);\n\n\t\t\t// Wrap stdout (a Node Readable) as a Stream<Uint8Array>.\n\t\t\t// `Stream.fromReadable` is the substrate-blessed shape.\n\t\t\tconst stdout = child.stdout;\n\t\t\tif (stdout === null) {\n\t\t\t\treturn Stream.fail(\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'spawn',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: `'tar' subprocess produced no stdout pipe`,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Node `Readable` is an `AsyncIterable<Buffer>`; `Buffer`\n\t\t\t// extends `Uint8Array` structurally. The cast asserts the\n\t\t\t// nominal-vs-structural gap at the Node boundary and stays\n\t\t\t// off the user-facing API.\n\t\t\tconst dataStream = Stream.fromAsyncIterable(\n\t\t\t\tstdout as unknown as AsyncIterable<Uint8Array>,\n\t\t\t\t(cause) =>\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'stream-stdout',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: `read from tar stdout failed`,\n\t\t\t\t\t\tcause,\n\t\t\t\t\t}),\n\t\t\t);\n\n\t\t\t// After stdout closes, await the subprocess exit and surface\n\t\t\t// non-zero codes with the captured stderr tail. This appends\n\t\t\t// a \"tail\" sub-stream that emits zero bytes but can fail —\n\t\t\t// the consumer's `Stream.run` sees the exit-code failure\n\t\t\t// uniformly with stream-read failures.\n\t\t\tconst exitGate: Stream.Stream<Uint8Array, HostTreeTarError, never> = Stream.fromEffect(\n\t\t\t\tEffect.promise(() => awaitProcessExit(child as ManagedProcessChild)).pipe(\n\t\t\t\t\tEffect.flatMap((status) =>\n\t\t\t\t\t\tstatus.code === 0 && status.signal === null\n\t\t\t\t\t\t\t? Effect.succeed(new Uint8Array(0))\n\t\t\t\t\t\t\t: Effect.fail(tarExitError('tar', status, stderrChunks)),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t).pipe(Stream.filter((bytes) => bytes.length > 0));\n\n\t\t\treturn Stream.concat(dataStream, exitGate);\n\t\t}),\n\t);\n\n// ---------------------------------------------------------------------------\n// Untar a Stream<Uint8Array> into a target directory\n// ---------------------------------------------------------------------------\n\nexport interface UntarHostTreeSpec {\n\treadonly target: string;\n}\n\n/**\n * Extract a tar `Stream<Uint8Array>` into `target`, preserving mode\n * bits. The target directory MUST already exist; the caller is\n * responsible (stage-and-swap creates the staging directory before\n * calling).\n *\n * The subprocess consumes from the stream and writes into `target`;\n * scope close kills the subprocess. The Effect resolves when `tar`\n * exits cleanly (code 0).\n */\nexport const untarHostTree = <R>(\n\tstream: Stream.Stream<Uint8Array, HostTreeTarError, R>,\n\tspec: UntarHostTreeSpec,\n): Effect.Effect<void, HostTreeTarError, R | Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\tconst args = ['-x', '-f', '-', '-C', spec.target, '-p'];\n\t\tconst { child, stderrChunks } = yield* spawnTar('untar', args, ['pipe', 'ignore', 'pipe']);\n\n\t\tconst stdin = child.stdin;\n\t\tif (stdin === null) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\tstage: 'spawn',\n\t\t\t\t\toperation: 'untar',\n\t\t\t\t\tdetail: `'tar' subprocess produced no stdin pipe`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst validationState = makeTarReaderState();\n\n\t\tconst writeChunk = (chunk: Uint8Array): Effect.Effect<void, HostTreeTarError> =>\n\t\t\tEffect.callback<void, HostTreeTarError>((resume) => {\n\t\t\t\tlet settled = false;\n\t\t\t\tconst settle = (effect: Effect.Effect<void, HostTreeTarError>): void => {\n\t\t\t\t\tif (!settled) {\n\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\tresume(effect);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tconst onDrain = (): void => settle(Effect.void);\n\t\t\t\tconst ok = stdin.write(chunk, (cause) => {\n\t\t\t\t\tstdin.off('drain', onDrain);\n\t\t\t\t\tif (cause) {\n\t\t\t\t\t\tsettle(\n\t\t\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\t\t\t\tstage: 'stream-stdin',\n\t\t\t\t\t\t\t\t\toperation: 'untar',\n\t\t\t\t\t\t\t\t\tdetail: `write to tar stdin failed`,\n\t\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsettle(Effect.void);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif (!ok) {\n\t\t\t\t\tstdin.once('drain', onDrain);\n\t\t\t\t}\n\t\t\t\t// Interrupt while parked on backpressure must remove the\n\t\t\t\t// drain listener so it can't resume an interrupted fiber.\n\t\t\t\treturn Effect.sync(() => {\n\t\t\t\t\tstdin.off('drain', onDrain);\n\t\t\t\t});\n\t\t\t});\n\n\t\t// Pump the source stream into tar's stdin. Each chunk is\n\t\t// validated before it is handed to the tar subprocess, so unsafe\n\t\t// archive paths are rejected at the extraction boundary even if a\n\t\t// caller skipped any earlier preflight validation.\n\t\tyield* Stream.runForEach(stream, (chunk) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst validationError = processTarChunk(validationState, chunk, hostTreeValidationHooks);\n\t\t\t\tif (validationError !== null) {\n\t\t\t\t\treturn yield* Effect.fail(validationError);\n\t\t\t\t}\n\t\t\t\tyield* writeChunk(chunk);\n\t\t\t}),\n\t\t).pipe(\n\t\t\tEffect.catch((cause) =>\n\t\t\t\tcause instanceof HostTreeTarError\n\t\t\t\t\t? Effect.fail(cause)\n\t\t\t\t\t: Effect.fail(\n\t\t\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\t\t\tstage: 'stream-stdin',\n\t\t\t\t\t\t\t\toperation: 'untar',\n\t\t\t\t\t\t\t\tdetail: `source stream raised before EOF`,\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t),\n\t\t);\n\n\t\tconst finalValidationError = finishTarReader(validationState, tarValidationError);\n\t\tif (finalValidationError !== null) {\n\t\t\treturn yield* Effect.fail(finalValidationError);\n\t\t}\n\n\t\t// Close stdin so tar sees EOF and exits.\n\t\tyield* Effect.sync(() => stdin.end());\n\n\t\t// Await exit; non-zero surfaces with stderr tail.\n\t\tconst status = yield* Effect.promise(() => awaitProcessExit(child as ManagedProcessChild));\n\t\tif (status.code !== 0 || status.signal !== null) {\n\t\t\treturn yield* Effect.fail(tarExitError('untar', status, stderrChunks));\n\t\t}\n\t});\n"],"mappings":"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"}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import * as _$effect_Cause0 from "effect/Cause";
|
|
3
|
-
import * as _$effect_Types0 from "effect/Types";
|
|
4
2
|
|
|
5
3
|
//#region src/substrate/runtime/http-probe.d.ts
|
|
6
4
|
declare namespace http_probe_d_exports {
|
|
@@ -17,7 +15,7 @@ interface HttpProbeOptions {
|
|
|
17
15
|
readonly validate?: HttpProbeValidator;
|
|
18
16
|
readonly fetch?: HttpProbeFetch;
|
|
19
17
|
}
|
|
20
|
-
declare const HttpProbeError_base: new <A extends Record<string, any> = {}>(args:
|
|
18
|
+
declare const HttpProbeError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & {
|
|
21
19
|
readonly _tag: "HttpProbeError";
|
|
22
20
|
} & Readonly<A>;
|
|
23
21
|
declare class HttpProbeError extends HttpProbeError_base<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-probe.mjs","names":[],"sources":["../../../src/substrate/runtime/http-probe.ts"],"sourcesContent":["import { Data, Effect } from 'effect';\n\nimport { ProbeTimeoutError, waitForProbe } from './probes.ts';\n\nexport type HttpProbeFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\nexport type HttpProbeValidator = (response: Response) => boolean | Promise<boolean>;\n\nexport interface HttpProbeOptions {\n\treadonly endpoint: string | URL;\n\treadonly timeoutMs: number;\n\treadonly intervalMs?: number;\n\treadonly requestTimeoutMs?: number;\n\treadonly requestInit?: Omit<RequestInit, 'signal'>;\n\treadonly validate?: HttpProbeValidator;\n\treadonly fetch?: HttpProbeFetch;\n}\n\nexport class HttpProbeError extends Data.TaggedError('HttpProbeError')<{\n\treadonly endpoint: string;\n\treadonly timeoutMs: number;\n\treadonly intervalMs: number;\n\treadonly requestTimeoutMs: number;\n\treadonly message: string;\n\treadonly lastStatus?: number;\n\treadonly lastError?: unknown;\n}> {}\n\nconst DEFAULT_INTERVAL_MS = 250;\n\nexport const waitForHttpEndpoint = (\n\toptions: HttpProbeOptions,\n): Effect.Effect<void, HttpProbeError> =>\n\tEffect.gen(function* () {\n\t\tconst endpoint = String(options.endpoint);\n\t\tconst timeoutMs = options.timeoutMs;\n\t\tconst intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;\n\t\tconst requestTimeoutMs = options.requestTimeoutMs ?? intervalMs;\n\t\tconst validate = options.validate ?? ((response: Response) => response.ok);\n\t\tconst fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);\n\t\tlet lastStatus: number | undefined;\n\n\t\treturn yield* waitForProbe({\n\t\t\tlabel: endpoint,\n\t\t\ttimeoutMs,\n\t\t\tintervalMs,\n\t\t\tattemptTimeoutMs: requestTimeoutMs,\n\t\t\tprobe: () =>\n\t\t\t\tEffect.tryPromise({\n\t\t\t\t\ttry: (signal) =>\n\t\t\t\t\t\tfetchImpl(options.endpoint, {\n\t\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\t\t...options.requestInit,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t}),\n\t\t\t\t\tcatch: (cause) => cause,\n\t\t\t\t}).pipe(\n\t\t\t\t\tEffect.flatMap((response) => {\n\t\t\t\t\t\tlastStatus = response.status;\n\t\t\t\t\t\treturn Effect.tryPromise({\n\t\t\t\t\t\t\ttry: () => Promise.resolve(validate(response)),\n\t\t\t\t\t\t\tcatch: (cause) => cause,\n\t\t\t\t\t\t}).pipe(\n\t\t\t\t\t\t\tEffect.map((ready) =>\n\t\t\t\t\t\t\t\tready ? true : { ready: false, detail: { status: response.status } },\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t}).pipe(\n\t\t\tEffect.mapError((cause) => {\n\t\t\t\tif (cause instanceof ProbeTimeoutError) {\n\t\t\t\t\treturn new HttpProbeError({\n\t\t\t\t\t\tendpoint,\n\t\t\t\t\t\ttimeoutMs,\n\t\t\t\t\t\tintervalMs,\n\t\t\t\t\t\trequestTimeoutMs,\n\t\t\t\t\t\tmessage: `HTTP endpoint ${endpoint} did not become ready within ${timeoutMs}ms`,\n\t\t\t\t\t\t...(lastStatus === undefined ? {} : { lastStatus }),\n\t\t\t\t\t\t...(cause.lastError === undefined ? {} : { lastError: cause.lastError }),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn new HttpProbeError({\n\t\t\t\t\tendpoint,\n\t\t\t\t\ttimeoutMs,\n\t\t\t\t\tintervalMs,\n\t\t\t\t\trequestTimeoutMs,\n\t\t\t\t\tmessage: `HTTP endpoint ${endpoint} probe failed`,\n\t\t\t\t\t...(lastStatus === undefined ? {} : { lastStatus }),\n\t\t\t\t\tlastError: cause,\n\t\t\t\t});\n\t\t\t}),\n\t\t);\n\t});\n"],"mappings":";;;;;;;;AAkBA,IAAa,iBAAb,cAAoC,KAAK,YAAY,
|
|
1
|
+
{"version":3,"file":"http-probe.mjs","names":[],"sources":["../../../src/substrate/runtime/http-probe.ts"],"sourcesContent":["import { Data, Effect } from 'effect';\n\nimport { ProbeTimeoutError, waitForProbe } from './probes.ts';\n\nexport type HttpProbeFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\nexport type HttpProbeValidator = (response: Response) => boolean | Promise<boolean>;\n\nexport interface HttpProbeOptions {\n\treadonly endpoint: string | URL;\n\treadonly timeoutMs: number;\n\treadonly intervalMs?: number;\n\treadonly requestTimeoutMs?: number;\n\treadonly requestInit?: Omit<RequestInit, 'signal'>;\n\treadonly validate?: HttpProbeValidator;\n\treadonly fetch?: HttpProbeFetch;\n}\n\nexport class HttpProbeError extends Data.TaggedError('HttpProbeError')<{\n\treadonly endpoint: string;\n\treadonly timeoutMs: number;\n\treadonly intervalMs: number;\n\treadonly requestTimeoutMs: number;\n\treadonly message: string;\n\treadonly lastStatus?: number;\n\treadonly lastError?: unknown;\n}> {}\n\nconst DEFAULT_INTERVAL_MS = 250;\n\nexport const waitForHttpEndpoint = (\n\toptions: HttpProbeOptions,\n): Effect.Effect<void, HttpProbeError> =>\n\tEffect.gen(function* () {\n\t\tconst endpoint = String(options.endpoint);\n\t\tconst timeoutMs = options.timeoutMs;\n\t\tconst intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;\n\t\tconst requestTimeoutMs = options.requestTimeoutMs ?? intervalMs;\n\t\tconst validate = options.validate ?? ((response: Response) => response.ok);\n\t\tconst fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);\n\t\tlet lastStatus: number | undefined;\n\n\t\treturn yield* waitForProbe({\n\t\t\tlabel: endpoint,\n\t\t\ttimeoutMs,\n\t\t\tintervalMs,\n\t\t\tattemptTimeoutMs: requestTimeoutMs,\n\t\t\tprobe: () =>\n\t\t\t\tEffect.tryPromise({\n\t\t\t\t\ttry: (signal) =>\n\t\t\t\t\t\tfetchImpl(options.endpoint, {\n\t\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\t\t...options.requestInit,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t}),\n\t\t\t\t\tcatch: (cause) => cause,\n\t\t\t\t}).pipe(\n\t\t\t\t\tEffect.flatMap((response) => {\n\t\t\t\t\t\tlastStatus = response.status;\n\t\t\t\t\t\treturn Effect.tryPromise({\n\t\t\t\t\t\t\ttry: () => Promise.resolve(validate(response)),\n\t\t\t\t\t\t\tcatch: (cause) => cause,\n\t\t\t\t\t\t}).pipe(\n\t\t\t\t\t\t\tEffect.map((ready) =>\n\t\t\t\t\t\t\t\tready ? true : { ready: false, detail: { status: response.status } },\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t}).pipe(\n\t\t\tEffect.mapError((cause) => {\n\t\t\t\tif (cause instanceof ProbeTimeoutError) {\n\t\t\t\t\treturn new HttpProbeError({\n\t\t\t\t\t\tendpoint,\n\t\t\t\t\t\ttimeoutMs,\n\t\t\t\t\t\tintervalMs,\n\t\t\t\t\t\trequestTimeoutMs,\n\t\t\t\t\t\tmessage: `HTTP endpoint ${endpoint} did not become ready within ${timeoutMs}ms`,\n\t\t\t\t\t\t...(lastStatus === undefined ? {} : { lastStatus }),\n\t\t\t\t\t\t...(cause.lastError === undefined ? {} : { lastError: cause.lastError }),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn new HttpProbeError({\n\t\t\t\t\tendpoint,\n\t\t\t\t\ttimeoutMs,\n\t\t\t\t\tintervalMs,\n\t\t\t\t\trequestTimeoutMs,\n\t\t\t\t\tmessage: `HTTP endpoint ${endpoint} probe failed`,\n\t\t\t\t\t...(lastStatus === undefined ? {} : { lastStatus }),\n\t\t\t\t\tlastError: cause,\n\t\t\t\t});\n\t\t\t}),\n\t\t);\n\t});\n"],"mappings":";;;;;;;;AAkBA,IAAa,iBAAb,cAAoC,KAAK,YAAY,gBAAgB,CAAC,CAQnE,CAAC;AAEJ,MAAM,sBAAsB;AAE5B,MAAa,uBACZ,YAEA,OAAO,IAAI,aAAa;CACvB,MAAM,WAAW,OAAO,QAAQ,QAAQ;CACxC,MAAM,YAAY,QAAQ;CAC1B,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,mBAAmB,QAAQ,oBAAoB;CACrD,MAAM,WAAW,QAAQ,cAAc,aAAuB,SAAS;CACvE,MAAM,YAAY,QAAQ,SAAS,WAAW,MAAM,KAAK,UAAU;CACnE,IAAI;CAEJ,OAAO,OAAO,aAAa;EAC1B,OAAO;EACP;EACA;EACA,kBAAkB;EAClB,aACC,OAAO,WAAW;GACjB,MAAM,WACL,UAAU,QAAQ,UAAU;IAC3B,QAAQ;IACR,GAAG,QAAQ;IACX;GACD,CAAC;GACF,QAAQ,UAAU;EACnB,CAAC,CAAC,CAAC,KACF,OAAO,SAAS,aAAa;GAC5B,aAAa,SAAS;GACtB,OAAO,OAAO,WAAW;IACxB,WAAW,QAAQ,QAAQ,SAAS,QAAQ,CAAC;IAC7C,QAAQ,UAAU;GACnB,CAAC,CAAC,CAAC,KACF,OAAO,KAAK,UACX,QAAQ,OAAO;IAAE,OAAO;IAAO,QAAQ,EAAE,QAAQ,SAAS,OAAO;GAAE,CACpE,CACD;EACD,CAAC,CACF;CACF,CAAC,CAAC,CAAC,KACF,OAAO,UAAU,UAAU;EAC1B,IAAI,iBAAiB,mBACpB,OAAO,IAAI,eAAe;GACzB;GACA;GACA;GACA;GACA,SAAS,iBAAiB,SAAS,+BAA+B,UAAU;GAC5E,GAAI,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW;GACjD,GAAI,MAAM,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW,MAAM,UAAU;EACvE,CAAC;EAEF,OAAO,IAAI,eAAe;GACzB;GACA;GACA;GACA;GACA,SAAS,iBAAiB,SAAS;GACnC,GAAI,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW;GACjD,WAAW;EACZ,CAAC;CACF,CAAC,CACF;AACD,CAAC"}
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import "./capability-sinks/service.mjs";
|
|
2
|
-
import "./observability/formatter-registry.mjs";
|
|
3
|
-
import "./capability-sinks/layer.mjs";
|
|
4
|
-
import "./capability-sinks/index.mjs";
|
|
5
1
|
import "./projection/state-ref.mjs";
|
|
6
|
-
import "./projection/persisted.mjs";
|
|
7
2
|
import "./cross-process/command-channel/channel.mjs";
|
|
8
3
|
import "./cross-process/command-channel/index.mjs";
|
|
9
4
|
import "./cross-process/roster.mjs";
|
|
5
|
+
import "./supervisor/contribution-dispatcher.mjs";
|
|
10
6
|
import "./observability/logger.mjs";
|
|
11
|
-
import "./observability/index.mjs";
|
|
12
7
|
import "./supervisor/start-supervisor.mjs";
|
|
13
8
|
import "./supervisor/index.mjs";
|
|
14
9
|
export {};
|
|
@@ -80,7 +80,7 @@ const layerLeaseBroker = Layer.effect(LeaseBrokerService, Effect.gen(function* (
|
|
|
80
80
|
owner,
|
|
81
81
|
signal
|
|
82
82
|
};
|
|
83
|
-
|
|
83
|
+
if (!(yield* Ref.modify(state, (current) => {
|
|
84
84
|
const entry = current.get(key);
|
|
85
85
|
if (!entry) {
|
|
86
86
|
const next = new Map(current);
|
|
@@ -96,19 +96,13 @@ const layerLeaseBroker = Layer.effect(LeaseBrokerService, Effect.gen(function* (
|
|
|
96
96
|
waiters: [...entry.waiters, waiter]
|
|
97
97
|
});
|
|
98
98
|
return [false, next];
|
|
99
|
-
});
|
|
100
|
-
if (!becameHolder) yield* restore(Deferred.await(signal)).pipe(Effect.onInterrupt(() => cleanupCancelledWait(key, waiter).pipe(Effect.uninterruptible)));
|
|
99
|
+
}))) yield* restore(Deferred.await(signal)).pipe(Effect.onInterrupt(() => cleanupCancelledWait(key, waiter).pipe(Effect.uninterruptible)));
|
|
101
100
|
yield* installReleaseFinalizer(key);
|
|
102
|
-
yield* Effect.annotateCurrentSpan({
|
|
103
|
-
"leaseBroker.key": key,
|
|
104
|
-
"leaseBroker.owner": owner,
|
|
105
|
-
"leaseBroker.contended": !becameHolder
|
|
106
|
-
});
|
|
107
101
|
return {
|
|
108
102
|
key,
|
|
109
103
|
owner
|
|
110
104
|
};
|
|
111
|
-
}))
|
|
105
|
+
}));
|
|
112
106
|
const tryAcquire = (key, owner) => Effect.gen(function* () {
|
|
113
107
|
if (!(yield* Ref.modify(state, (current) => {
|
|
114
108
|
if (current.has(key)) return [false, current];
|
|
@@ -118,25 +112,13 @@ const layerLeaseBroker = Layer.effect(LeaseBrokerService, Effect.gen(function* (
|
|
|
118
112
|
waiters: []
|
|
119
113
|
});
|
|
120
114
|
return [true, next];
|
|
121
|
-
})))
|
|
122
|
-
yield* Effect.annotateCurrentSpan({
|
|
123
|
-
"leaseBroker.key": key,
|
|
124
|
-
"leaseBroker.owner": owner,
|
|
125
|
-
"leaseBroker.claimed": false
|
|
126
|
-
});
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
115
|
+
}))) return null;
|
|
129
116
|
yield* installReleaseFinalizer(key);
|
|
130
|
-
yield* Effect.annotateCurrentSpan({
|
|
131
|
-
"leaseBroker.key": key,
|
|
132
|
-
"leaseBroker.owner": owner,
|
|
133
|
-
"leaseBroker.claimed": true
|
|
134
|
-
});
|
|
135
117
|
return {
|
|
136
118
|
key,
|
|
137
119
|
owner
|
|
138
120
|
};
|
|
139
|
-
}).pipe(Effect.uninterruptible
|
|
121
|
+
}).pipe(Effect.uninterruptible);
|
|
140
122
|
const holders = () => Effect.gen(function* () {
|
|
141
123
|
const current = yield* Ref.get(state);
|
|
142
124
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.mjs","names":[],"sources":["../../../../src/substrate/runtime/lease-broker/service.ts"],"sourcesContent":["// Lease broker — substrate-level service interface.\n//\n// Architecture (`notes/redesign/architecture.md` § \"L0 substrate\n// primitives\"): the substrate owns a generic lease primitive keyed by\n// an opaque resource identifier. Any caller that needs at-most-one-\n// in-flight serialization on a resource yields this service and calls\n// `acquire(key, owner)`. The first canonical consumer is the per-\n// address sequence-number lock (`plugins/account/address-lock.ts`,\n// today plugin-local; PR3 lifts it to consume this primitive);\n// callers in the same shape — sign+execute pipelines, per-connection\n// gate, per-slot work queue — share the same seam.\n//\n// Substrate name-blindness: the broker has no concept of what a\n// `LeaseKey` represents. The plugin author chooses the key shape; the\n// broker treats it as an opaque string with a brand. A wallet might\n// key by `account:<address>`, a GPU-bound plugin by `gpu:<slot>` —\n// the broker doesn't care.\n//\n// Lifetime: scope-bound. `acquire` returns a `Lease` handle whose\n// release fires when the surrounding `Effect.Scope` closes. There is\n// no `release()` method on `Lease` — the only release path is the\n// scope finalizer, which makes \"I forgot to release\" structurally\n// impossible.\n//\n// Mechanics: per-key state in a single `Ref<State>`. The blocking\n// queue is a FIFO of `Waiter`s carrying a `Deferred` that the\n// outgoing holder's finalizer resolves AFTER atomically installing\n// the next holder. `tryAcquire` does the same atomic-claim CAS as\n// `acquire`'s first step, but skips the wait — returning `null` if\n// the key is held.\n\nimport { Context, Deferred, Effect, Layer, Ref, Scope } from 'effect';\n\nimport type { Brand } from '../../brand.ts';\n\n// ----------------------------------------------------------------------\n// Public shape\n// ----------------------------------------------------------------------\n\n/** Opaque resource identifier. Substrate-blind: the broker treats\n * this as a string for map keying and never inspects it. Plugin\n * authors choose the encoding (`account:<addr>`, `gpu:<slot>`, etc.). */\nexport type LeaseKey = Brand<string, 'LeaseKey'>;\n\n/** Construct a `LeaseKey` from a free-form string. The boundary at\n * which unbranded strings become branded; downstream code only sees\n * branded values. */\nexport const leaseKey = (s: string): LeaseKey => s as LeaseKey;\n\n/** Free-form owner identity, used for diagnostics (`holders()` output,\n * span attributes). The broker does NOT use this for re-entrancy:\n * two `acquire` calls from the same owner against the same key will\n * deadlock the second call. */\nexport type Owner = string;\n\n/** Lease handle. Carries only the diagnostic pair; the release is the\n * scope finalizer, not a method.\n *\n * Why no `release()`: every consumer we have today wraps the lease\n * in `Effect.scoped` and lets the scope finalizer fire. Exposing an\n * explicit `release()` would invite double-release, release-while-\n * scope-still-open, and the \"I released early but forgot to clear\n * the variable\" footgun. The scope IS the lifetime. */\nexport interface Lease {\n\treadonly key: LeaseKey;\n\treadonly owner: Owner;\n}\n\n/** Service shape — what plugins yield from Context. */\nexport interface LeaseBroker {\n\t/**\n\t * Acquire the lease for `key`, attributed to `owner`. Blocks if the\n\t * lease is already held, waiting in FIFO order behind earlier\n\t * waiters. Cancellable: an interrupt while waiting unwinds without\n\t * acquiring.\n\t *\n\t * Scope-bound release: the broker installs an uninterruptible\n\t * finalizer on the surrounding scope. Calling `Effect.scoped` over\n\t * an `acquire(...)` is the canonical shape.\n\t *\n\t * Non-reentrant: `acquire` from the same owner against a key the\n\t * owner already holds will deadlock. Per-address sequence-number\n\t * semantics require at-most-one in-flight; nesting is the caller's\n\t * bug.\n\t */\n\treadonly acquire: (key: LeaseKey, owner: Owner) => Effect.Effect<Lease, never, Scope.Scope>;\n\n\t/**\n\t * Non-blocking variant. Returns `null` if the lease is held by any\n\t * owner (including the calling owner); otherwise atomically claims\n\t * the lease and returns the handle. The atomic-claim CAS is the\n\t * same as `acquire`'s first step — the only difference is the\n\t * `null` return vs. enqueue.\n\t *\n\t * Scope-bound release: same finalizer install as `acquire` when\n\t * the claim wins.\n\t */\n\treadonly tryAcquire: (\n\t\tkey: LeaseKey,\n\t\towner: Owner,\n\t) => Effect.Effect<Lease | null, never, Scope.Scope>;\n\n\t/**\n\t * Snapshot of current holders, keyed by `LeaseKey`. For diagnostics\n\t * (renderer, debug logs); the result is a point-in-time read and\n\t * may be stale by the time it's inspected.\n\t */\n\treadonly holders: () => Effect.Effect<ReadonlyMap<LeaseKey, Owner>>;\n}\n\n// ----------------------------------------------------------------------\n// Internal state\n// ----------------------------------------------------------------------\n\n/** A fiber parked on `Deferred.await` waiting to become the next\n * holder. The outgoing holder's finalizer transfers ownership by\n * setting `holder = waiter.owner` on the per-key entry AND resolving\n * the deferred — the awoken waiter doesn't re-CAS.\n *\n * `Waiter` reference identity is the cancellation key: an interrupted\n * waiter removes itself by reference-equality. */\ninterface Waiter {\n\treadonly owner: Owner;\n\treadonly signal: Deferred.Deferred<void>;\n}\n\ninterface KeyEntry {\n\treadonly holder: Owner;\n\treadonly waiters: ReadonlyArray<Waiter>;\n}\n\ntype State = ReadonlyMap<LeaseKey, KeyEntry>;\n\n// ----------------------------------------------------------------------\n// Service tag + Layer\n// ----------------------------------------------------------------------\n\nexport class LeaseBrokerService extends Context.Service<LeaseBrokerService, LeaseBroker>()(\n\t'@devstack/substrate/LeaseBroker',\n) {}\n\n/**\n * In-process Layer. One broker per stack scope; closing the layer's\n * scope drops every entry. Parallel stacks each get their own broker\n * — the broker is name-blind, so cross-stack coordination on the\n * same logical key (e.g. an address that two stacks share) is NOT\n * provided here. A cross-process layer can be slotted in later as a\n * sibling factory; today's consumers are in-process only.\n */\nexport const layerLeaseBroker: Layer.Layer<LeaseBrokerService> = Layer.effect(\n\tLeaseBrokerService,\n\tEffect.gen(function* () {\n\t\tconst state = yield* Ref.make<State>(new Map());\n\n\t\t/** Atomic finalizer body: clear `key`'s holder slot and, if any\n\t\t * waiter is queued, promote the head waiter to holder. Returns\n\t\t * the promoted waiter (so the caller can resolve their signal)\n\t\t * or `null` when the key is fully released. Uninterruptible by\n\t\t * construction — invoked from inside `Effect.addFinalizer` which\n\t\t * the runtime wraps uninterruptibly. */\n\t\tconst releaseAndPromote = (key: LeaseKey): Effect.Effect<Waiter | null> =>\n\t\t\tRef.modify<State, Waiter | null>(state, (current) => {\n\t\t\t\tconst entry = current.get(key);\n\t\t\t\tif (!entry) return [null, current];\n\t\t\t\tconst head = entry.waiters[0];\n\t\t\t\tif (head === undefined) {\n\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\tnext.delete(key);\n\t\t\t\t\treturn [null, next];\n\t\t\t\t}\n\t\t\t\tconst rest = entry.waiters.slice(1);\n\t\t\t\tconst next = new Map(current);\n\t\t\t\tnext.set(key, { holder: head.owner, waiters: rest });\n\t\t\t\treturn [head, next];\n\t\t\t});\n\n\t\t/** Remove `waiter` from `key`'s waiter list. Called when an\n\t\t * enqueued waiter is interrupted while parked on its signal. */\n\t\tconst removeWaiterIfQueued = (key: LeaseKey, waiter: Waiter): Effect.Effect<void> =>\n\t\t\tRef.update(state, (current) => {\n\t\t\t\tconst entry = current.get(key);\n\t\t\t\tif (!entry) return current;\n\t\t\t\tconst filtered = entry.waiters.filter((w) => w !== waiter);\n\t\t\t\tif (filtered.length === entry.waiters.length) return current;\n\t\t\t\tconst next = new Map(current);\n\t\t\t\tnext.set(key, { holder: entry.holder, waiters: filtered });\n\t\t\t\treturn next;\n\t\t\t});\n\n\t\t/** Cleanup on cancellation of a waiting `acquire`: if we were\n\t\t * still queued, drop ourselves; if we were already promoted to\n\t\t * holder by a release that raced with our interrupt, release on\n\t\t * our behalf so the lease doesn't leak. */\n\t\tconst cleanupCancelledWait = (key: LeaseKey, waiter: Waiter): Effect.Effect<void> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst becameHolder = yield* Ref.modify<State, boolean>(state, (current) => {\n\t\t\t\t\tconst entry = current.get(key);\n\t\t\t\t\tif (!entry) return [false, current];\n\t\t\t\t\tif (entry.holder === waiter.owner && entry.waiters.indexOf(waiter) === -1) {\n\t\t\t\t\t\t// Promoted-but-not-awaited: we were handed the lease\n\t\t\t\t\t\t// in releaseAndPromote but interrupted before the\n\t\t\t\t\t\t// `Deferred.await` returned. Take responsibility for\n\t\t\t\t\t\t// releasing it.\n\t\t\t\t\t\treturn [true, current];\n\t\t\t\t\t}\n\t\t\t\t\treturn [false, current];\n\t\t\t\t});\n\t\t\t\tif (becameHolder) {\n\t\t\t\t\tconst promoted = yield* releaseAndPromote(key);\n\t\t\t\t\tif (promoted !== null) {\n\t\t\t\t\t\tyield* Deferred.succeed(promoted.signal, undefined);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tyield* removeWaiterIfQueued(key, waiter);\n\t\t\t\t}\n\t\t\t});\n\n\t\t/** Install the scope-bound release finalizer. Uninterruptible —\n\t\t * losing the release on a Ctrl-C double-tap would leak the\n\t\t * lease and stall everything queued behind it. */\n\t\tconst installReleaseFinalizer = (key: LeaseKey): Effect.Effect<void, never, Scope.Scope> =>\n\t\t\tEffect.addFinalizer(() =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst promoted = yield* releaseAndPromote(key);\n\t\t\t\t\tif (promoted !== null) {\n\t\t\t\t\t\tyield* Deferred.succeed(promoted.signal, undefined);\n\t\t\t\t\t}\n\t\t\t\t}).pipe(Effect.uninterruptible),\n\t\t\t);\n\n\t\tconst acquire: LeaseBroker['acquire'] = (key, owner) =>\n\t\t\tEffect.uninterruptibleMask((restore) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst signal = yield* Deferred.make<void>();\n\t\t\t\t\tconst waiter: Waiter = { owner, signal };\n\n\t\t\t\t\tconst becameHolder = yield* Ref.modify<State, boolean>(state, (current) => {\n\t\t\t\t\t\tconst entry = current.get(key);\n\t\t\t\t\t\tif (!entry) {\n\t\t\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\t\t\tnext.set(key, { holder: owner, waiters: [] });\n\t\t\t\t\t\t\treturn [true, next];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\t\tnext.set(key, {\n\t\t\t\t\t\t\tholder: entry.holder,\n\t\t\t\t\t\t\twaiters: [...entry.waiters, waiter],\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn [false, next];\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!becameHolder) {\n\t\t\t\t\t\tyield* restore(Deferred.await(signal)).pipe(\n\t\t\t\t\t\t\tEffect.onInterrupt(() =>\n\t\t\t\t\t\t\t\tcleanupCancelledWait(key, waiter).pipe(Effect.uninterruptible),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tyield* installReleaseFinalizer(key);\n\t\t\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t\t\t'leaseBroker.key': key,\n\t\t\t\t\t\t'leaseBroker.owner': owner,\n\t\t\t\t\t\t'leaseBroker.contended': !becameHolder,\n\t\t\t\t\t});\n\t\t\t\t\treturn { key, owner } satisfies Lease;\n\t\t\t\t}),\n\t\t\t).pipe(Effect.withSpan('substrate.leaseBroker.acquire'));\n\n\t\tconst tryAcquire: LeaseBroker['tryAcquire'] = (key, owner) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst claimed = yield* Ref.modify<State, boolean>(state, (current) => {\n\t\t\t\t\tif (current.has(key)) return [false, current];\n\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\tnext.set(key, { holder: owner, waiters: [] });\n\t\t\t\t\treturn [true, next];\n\t\t\t\t});\n\t\t\t\tif (!claimed) {\n\t\t\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t\t\t'leaseBroker.key': key,\n\t\t\t\t\t\t'leaseBroker.owner': owner,\n\t\t\t\t\t\t'leaseBroker.claimed': false,\n\t\t\t\t\t});\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tyield* installReleaseFinalizer(key);\n\t\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t\t'leaseBroker.key': key,\n\t\t\t\t\t'leaseBroker.owner': owner,\n\t\t\t\t\t'leaseBroker.claimed': true,\n\t\t\t\t});\n\t\t\t\treturn { key, owner } satisfies Lease;\n\t\t\t}).pipe(\n\t\t\t\t// Make the claim-and-install-finalizer pair atomic w.r.t.\n\t\t\t\t// interruption: an interrupt arriving AFTER `Ref.modify`\n\t\t\t\t// claims the slot but BEFORE the finalizer is wired would\n\t\t\t\t// leak the lease. `Ref.modify` itself is a pure-data\n\t\t\t\t// transition, so wrapping the whole pipeline uninterruptibly\n\t\t\t\t// is safe.\n\t\t\t\tEffect.uninterruptible,\n\t\t\t\tEffect.withSpan('substrate.leaseBroker.tryAcquire'),\n\t\t\t);\n\n\t\tconst holders: LeaseBroker['holders'] = () =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst current = yield* Ref.get(state);\n\t\t\t\tconst out = new Map<LeaseKey, Owner>();\n\t\t\t\tfor (const [k, entry] of current) out.set(k, entry.holder);\n\t\t\t\treturn out;\n\t\t\t});\n\n\t\treturn LeaseBrokerService.of({ acquire, tryAcquire, holders });\n\t}),\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"service.mjs","names":[],"sources":["../../../../src/substrate/runtime/lease-broker/service.ts"],"sourcesContent":["// Lease broker — substrate-level service interface.\n//\n// Generic lease primitive keyed by an opaque resource identifier. Any caller\n// that needs at-most-one-in-flight serialization on a resource yields this\n// service and calls `acquire(key, owner)`. The first canonical consumer is the\n// per-address sequence-number lock (`plugins/account/address-lock.ts`);\n// callers in the same shape — sign+execute pipelines, per-connection gate,\n// per-slot work queue — share the same seam.\n//\n// Substrate name-blindness: the broker has no concept of what a\n// `LeaseKey` represents. The plugin author chooses the key shape; the\n// broker treats it as an opaque string with a brand. A wallet might\n// key by `account:<address>`, a GPU-bound plugin by `gpu:<slot>` —\n// the broker doesn't care.\n//\n// Lifetime: scope-bound. `acquire` returns a `Lease` handle whose\n// release fires when the surrounding `Effect.Scope` closes. There is\n// no `release()` method on `Lease` — the only release path is the\n// scope finalizer, which makes \"I forgot to release\" structurally\n// impossible.\n//\n// Mechanics: per-key state in a single `Ref<State>`. The blocking\n// queue is a FIFO of `Waiter`s carrying a `Deferred` that the\n// outgoing holder's finalizer resolves AFTER atomically installing\n// the next holder. `tryAcquire` does the same atomic-claim CAS as\n// `acquire`'s first step, but skips the wait — returning `null` if\n// the key is held.\n\nimport { Context, Deferred, Effect, Layer, Ref, Scope } from 'effect';\n\nimport type { Brand } from '../../brand.ts';\n\n// ----------------------------------------------------------------------\n// Public shape\n// ----------------------------------------------------------------------\n\n/** Opaque resource identifier. Substrate-blind: the broker treats\n * this as a string for map keying and never inspects it. Plugin\n * authors choose the encoding (`account:<addr>`, `gpu:<slot>`, etc.). */\nexport type LeaseKey = Brand<string, 'LeaseKey'>;\n\n/** Construct a `LeaseKey` from a free-form string. The boundary at\n * which unbranded strings become branded; downstream code only sees\n * branded values. */\nexport const leaseKey = (s: string): LeaseKey => s as LeaseKey;\n\n/** Free-form owner identity, used for diagnostics (`holders()` output,\n * span attributes). The broker does NOT use this for re-entrancy:\n * two `acquire` calls from the same owner against the same key will\n * deadlock the second call. */\nexport type Owner = string;\n\n/** Lease handle. Carries only the diagnostic pair; the release is the\n * scope finalizer, not a method.\n *\n * Why no `release()`: every consumer we have today wraps the lease\n * in `Effect.scoped` and lets the scope finalizer fire. Exposing an\n * explicit `release()` would invite double-release, release-while-\n * scope-still-open, and the \"I released early but forgot to clear\n * the variable\" footgun. The scope IS the lifetime. */\nexport interface Lease {\n\treadonly key: LeaseKey;\n\treadonly owner: Owner;\n}\n\n/** Service shape — what plugins yield from Context. */\nexport interface LeaseBroker {\n\t/**\n\t * Acquire the lease for `key`, attributed to `owner`. Blocks if the\n\t * lease is already held, waiting in FIFO order behind earlier\n\t * waiters. Cancellable: an interrupt while waiting unwinds without\n\t * acquiring.\n\t *\n\t * Scope-bound release: the broker installs an uninterruptible\n\t * finalizer on the surrounding scope. Calling `Effect.scoped` over\n\t * an `acquire(...)` is the canonical shape.\n\t *\n\t * Non-reentrant: `acquire` from the same owner against a key the\n\t * owner already holds will deadlock. Per-address sequence-number\n\t * semantics require at-most-one in-flight; nesting is the caller's\n\t * bug.\n\t */\n\treadonly acquire: (key: LeaseKey, owner: Owner) => Effect.Effect<Lease, never, Scope.Scope>;\n\n\t/**\n\t * Non-blocking variant. Returns `null` if the lease is held by any\n\t * owner (including the calling owner); otherwise atomically claims\n\t * the lease and returns the handle. The atomic-claim CAS is the\n\t * same as `acquire`'s first step — the only difference is the\n\t * `null` return vs. enqueue.\n\t *\n\t * Scope-bound release: same finalizer install as `acquire` when\n\t * the claim wins.\n\t */\n\treadonly tryAcquire: (\n\t\tkey: LeaseKey,\n\t\towner: Owner,\n\t) => Effect.Effect<Lease | null, never, Scope.Scope>;\n\n\t/**\n\t * Snapshot of current holders, keyed by `LeaseKey`. For diagnostics\n\t * (renderer, debug logs); the result is a point-in-time read and\n\t * may be stale by the time it's inspected.\n\t */\n\treadonly holders: () => Effect.Effect<ReadonlyMap<LeaseKey, Owner>>;\n}\n\n// ----------------------------------------------------------------------\n// Internal state\n// ----------------------------------------------------------------------\n\n/** A fiber parked on `Deferred.await` waiting to become the next\n * holder. The outgoing holder's finalizer transfers ownership by\n * setting `holder = waiter.owner` on the per-key entry AND resolving\n * the deferred — the awoken waiter doesn't re-CAS.\n *\n * `Waiter` reference identity is the cancellation key: an interrupted\n * waiter removes itself by reference-equality. */\ninterface Waiter {\n\treadonly owner: Owner;\n\treadonly signal: Deferred.Deferred<void>;\n}\n\ninterface KeyEntry {\n\treadonly holder: Owner;\n\treadonly waiters: ReadonlyArray<Waiter>;\n}\n\ntype State = ReadonlyMap<LeaseKey, KeyEntry>;\n\n// ----------------------------------------------------------------------\n// Service tag + Layer\n// ----------------------------------------------------------------------\n\nexport class LeaseBrokerService extends Context.Service<LeaseBrokerService, LeaseBroker>()(\n\t'@devstack/substrate/LeaseBroker',\n) {}\n\n/**\n * In-process Layer. One broker per stack scope; closing the layer's\n * scope drops every entry. Parallel stacks each get their own broker\n * — the broker is name-blind, so cross-stack coordination on the\n * same logical key (e.g. an address that two stacks share) is NOT\n * provided here. A cross-process layer can be slotted in later as a\n * sibling factory; today's consumers are in-process only.\n */\nexport const layerLeaseBroker: Layer.Layer<LeaseBrokerService> = Layer.effect(\n\tLeaseBrokerService,\n\tEffect.gen(function* () {\n\t\tconst state = yield* Ref.make<State>(new Map());\n\n\t\t/** Atomic finalizer body: clear `key`'s holder slot and, if any\n\t\t * waiter is queued, promote the head waiter to holder. Returns\n\t\t * the promoted waiter (so the caller can resolve their signal)\n\t\t * or `null` when the key is fully released. Uninterruptible by\n\t\t * construction — invoked from inside `Effect.addFinalizer` which\n\t\t * the runtime wraps uninterruptibly. */\n\t\tconst releaseAndPromote = (key: LeaseKey): Effect.Effect<Waiter | null> =>\n\t\t\tRef.modify<State, Waiter | null>(state, (current) => {\n\t\t\t\tconst entry = current.get(key);\n\t\t\t\tif (!entry) return [null, current];\n\t\t\t\tconst head = entry.waiters[0];\n\t\t\t\tif (head === undefined) {\n\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\tnext.delete(key);\n\t\t\t\t\treturn [null, next];\n\t\t\t\t}\n\t\t\t\tconst rest = entry.waiters.slice(1);\n\t\t\t\tconst next = new Map(current);\n\t\t\t\tnext.set(key, { holder: head.owner, waiters: rest });\n\t\t\t\treturn [head, next];\n\t\t\t});\n\n\t\t/** Remove `waiter` from `key`'s waiter list. Called when an\n\t\t * enqueued waiter is interrupted while parked on its signal. */\n\t\tconst removeWaiterIfQueued = (key: LeaseKey, waiter: Waiter): Effect.Effect<void> =>\n\t\t\tRef.update(state, (current) => {\n\t\t\t\tconst entry = current.get(key);\n\t\t\t\tif (!entry) return current;\n\t\t\t\tconst filtered = entry.waiters.filter((w) => w !== waiter);\n\t\t\t\tif (filtered.length === entry.waiters.length) return current;\n\t\t\t\tconst next = new Map(current);\n\t\t\t\tnext.set(key, { holder: entry.holder, waiters: filtered });\n\t\t\t\treturn next;\n\t\t\t});\n\n\t\t/** Cleanup on cancellation of a waiting `acquire`: if we were\n\t\t * still queued, drop ourselves; if we were already promoted to\n\t\t * holder by a release that raced with our interrupt, release on\n\t\t * our behalf so the lease doesn't leak. */\n\t\tconst cleanupCancelledWait = (key: LeaseKey, waiter: Waiter): Effect.Effect<void> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst becameHolder = yield* Ref.modify<State, boolean>(state, (current) => {\n\t\t\t\t\tconst entry = current.get(key);\n\t\t\t\t\tif (!entry) return [false, current];\n\t\t\t\t\tif (entry.holder === waiter.owner && entry.waiters.indexOf(waiter) === -1) {\n\t\t\t\t\t\t// Promoted-but-not-awaited: we were handed the lease\n\t\t\t\t\t\t// in releaseAndPromote but interrupted before the\n\t\t\t\t\t\t// `Deferred.await` returned. Take responsibility for\n\t\t\t\t\t\t// releasing it.\n\t\t\t\t\t\treturn [true, current];\n\t\t\t\t\t}\n\t\t\t\t\treturn [false, current];\n\t\t\t\t});\n\t\t\t\tif (becameHolder) {\n\t\t\t\t\tconst promoted = yield* releaseAndPromote(key);\n\t\t\t\t\tif (promoted !== null) {\n\t\t\t\t\t\tyield* Deferred.succeed(promoted.signal, undefined);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tyield* removeWaiterIfQueued(key, waiter);\n\t\t\t\t}\n\t\t\t});\n\n\t\t/** Install the scope-bound release finalizer. Uninterruptible —\n\t\t * losing the release on a Ctrl-C double-tap would leak the\n\t\t * lease and stall everything queued behind it. */\n\t\tconst installReleaseFinalizer = (key: LeaseKey): Effect.Effect<void, never, Scope.Scope> =>\n\t\t\tEffect.addFinalizer(() =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst promoted = yield* releaseAndPromote(key);\n\t\t\t\t\tif (promoted !== null) {\n\t\t\t\t\t\tyield* Deferred.succeed(promoted.signal, undefined);\n\t\t\t\t\t}\n\t\t\t\t}).pipe(Effect.uninterruptible),\n\t\t\t);\n\n\t\tconst acquire: LeaseBroker['acquire'] = (key, owner) =>\n\t\t\tEffect.uninterruptibleMask((restore) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst signal = yield* Deferred.make<void>();\n\t\t\t\t\tconst waiter: Waiter = { owner, signal };\n\n\t\t\t\t\tconst becameHolder = yield* Ref.modify<State, boolean>(state, (current) => {\n\t\t\t\t\t\tconst entry = current.get(key);\n\t\t\t\t\t\tif (!entry) {\n\t\t\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\t\t\tnext.set(key, { holder: owner, waiters: [] });\n\t\t\t\t\t\t\treturn [true, next];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\t\tnext.set(key, {\n\t\t\t\t\t\t\tholder: entry.holder,\n\t\t\t\t\t\t\twaiters: [...entry.waiters, waiter],\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn [false, next];\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!becameHolder) {\n\t\t\t\t\t\tyield* restore(Deferred.await(signal)).pipe(\n\t\t\t\t\t\t\tEffect.onInterrupt(() =>\n\t\t\t\t\t\t\t\tcleanupCancelledWait(key, waiter).pipe(Effect.uninterruptible),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tyield* installReleaseFinalizer(key);\n\t\t\t\t\treturn { key, owner } satisfies Lease;\n\t\t\t\t}),\n\t\t\t);\n\n\t\tconst tryAcquire: LeaseBroker['tryAcquire'] = (key, owner) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst claimed = yield* Ref.modify<State, boolean>(state, (current) => {\n\t\t\t\t\tif (current.has(key)) return [false, current];\n\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\tnext.set(key, { holder: owner, waiters: [] });\n\t\t\t\t\treturn [true, next];\n\t\t\t\t});\n\t\t\t\tif (!claimed) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tyield* installReleaseFinalizer(key);\n\t\t\t\treturn { key, owner } satisfies Lease;\n\t\t\t}).pipe(\n\t\t\t\t// Make the claim-and-install-finalizer pair atomic w.r.t.\n\t\t\t\t// interruption: an interrupt arriving AFTER `Ref.modify`\n\t\t\t\t// claims the slot but BEFORE the finalizer is wired would\n\t\t\t\t// leak the lease. `Ref.modify` itself is a pure-data\n\t\t\t\t// transition, so wrapping the whole pipeline uninterruptibly\n\t\t\t\t// is safe.\n\t\t\t\tEffect.uninterruptible,\n\t\t\t);\n\n\t\tconst holders: LeaseBroker['holders'] = () =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst current = yield* Ref.get(state);\n\t\t\t\tconst out = new Map<LeaseKey, Owner>();\n\t\t\t\tfor (const [k, entry] of current) out.set(k, entry.holder);\n\t\t\t\treturn out;\n\t\t\t});\n\n\t\treturn LeaseBrokerService.of({ acquire, tryAcquire, holders });\n\t}),\n);\n"],"mappings":"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"}
|
|
@@ -57,7 +57,6 @@ const expand = (members) => {
|
|
|
57
57
|
* supervisor's acquire spans.
|
|
58
58
|
*/
|
|
59
59
|
const resolveGraph = (members) => Effect.gen(function* () {
|
|
60
|
-
yield* Effect.annotateCurrentSpan({ "devstack.dep-graph.memberCount": members.length });
|
|
61
60
|
const named = expand(members);
|
|
62
61
|
const providerByResourceId = /* @__PURE__ */ new Map();
|
|
63
62
|
for (const { key, member } of named) {
|
|
@@ -93,7 +92,8 @@ const resolveGraph = (members) => Effect.gen(function* () {
|
|
|
93
92
|
key,
|
|
94
93
|
member,
|
|
95
94
|
upstreamResources,
|
|
96
|
-
upstreamKeys
|
|
95
|
+
upstreamKeys,
|
|
96
|
+
keepAliveOnRestore: member.keepAliveOnRestore === true
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
const levels = [];
|
|
@@ -122,7 +122,7 @@ const resolveGraph = (members) => Effect.gen(function* () {
|
|
|
122
122
|
levels,
|
|
123
123
|
downstream: downstreamFrozen
|
|
124
124
|
};
|
|
125
|
-
})
|
|
125
|
+
});
|
|
126
126
|
/**
|
|
127
127
|
* Compute the closure of nodes downstream of `root` (inclusive).
|
|
128
128
|
* Used by the supervisor to tear down a subgraph in reverse-dep order
|