@mysten-incubation/devstack 0.0.1 → 0.1.1
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 +8 -4
- package/dashboard-ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dashboard-ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dashboard-ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dashboard-ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-cyrillic-ext-wght-normal-I4S5GZfc.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-cyrillic-wght-normal-BmXc_FBt.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-latin-ext-wght-normal-DrnZ1wKl.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-latin-wght-normal-B_7UjwxQ.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-symbols2-wght-normal-GZpp1pK2.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-vietnamese-wght-normal-D8KDMBhC.woff2 +0 -0
- package/dashboard-ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dashboard-ui/assets/index-Bmi1UtAg.js +1356 -0
- package/dashboard-ui/assets/index-D5EShVt4.js +3 -0
- package/dashboard-ui/assets/index-Deml9drg.css +1 -0
- package/dashboard-ui/index.html +13 -0
- package/dist/api/define-capabilities.d.mts +14 -3
- package/dist/api/define-capabilities.mjs +18 -1
- package/dist/api/define-capabilities.mjs.map +1 -1
- package/dist/api/define-devstack.d.mts +1 -1
- package/dist/api/define-devstack.mjs +6 -26
- package/dist/api/define-devstack.mjs.map +1 -1
- package/dist/api/define-plugin.d.mts +1 -0
- package/dist/api/define-plugin.mjs +2 -0
- package/dist/api/inference-network.d.mts +132 -0
- package/dist/api/inference-network.mjs +105 -16
- package/dist/api/inference-network.mjs.map +1 -1
- package/dist/api/plugin-errors.d.mts +7 -0
- package/dist/api/run-stack.d.mts +20 -9
- package/dist/api/run-stack.mjs +56 -29
- package/dist/api/run-stack.mjs.map +1 -1
- package/dist/build-integrations/playwright/config.d.mts +6 -4
- package/dist/build-integrations/playwright/config.mjs +5 -3
- package/dist/build-integrations/playwright/config.mjs.map +1 -1
- package/dist/build-integrations/playwright/errors.d.mts +1 -1
- package/dist/build-integrations/playwright/global-setup.d.mts +43 -17
- package/dist/build-integrations/playwright/global-setup.mjs +250 -13
- package/dist/build-integrations/playwright/global-setup.mjs.map +1 -1
- package/dist/build-integrations/playwright/index.d.mts +5 -3
- package/dist/build-integrations/playwright/index.mjs +4 -2
- package/dist/build-integrations/playwright/stack-context.d.mts +0 -1
- package/dist/build-integrations/playwright/stack-context.mjs +24 -63
- package/dist/build-integrations/playwright/stack-context.mjs.map +1 -1
- package/dist/build-integrations/playwright/wallet-context.d.mts +7 -10
- package/dist/build-integrations/playwright/wallet-context.mjs +8 -13
- package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -1
- package/dist/build-integrations/runtime/cold-start-url.d.mts +5 -0
- package/dist/build-integrations/runtime/cold-start-url.mjs +4 -3
- package/dist/build-integrations/runtime/cold-start-url.mjs.map +1 -1
- package/dist/build-integrations/runtime/conventional-routes.d.mts +51 -0
- package/dist/build-integrations/runtime/conventional-routes.mjs +92 -0
- package/dist/build-integrations/runtime/conventional-routes.mjs.map +1 -0
- package/dist/build-integrations/runtime/discover.d.mts +38 -30
- package/dist/build-integrations/runtime/discover.mjs +73 -70
- package/dist/build-integrations/runtime/discover.mjs.map +1 -1
- package/dist/build-integrations/runtime/endpoint-registry.d.mts +3 -1
- package/dist/build-integrations/runtime/endpoint-registry.mjs +3 -1
- package/dist/build-integrations/runtime/endpoint-registry.mjs.map +1 -1
- package/dist/build-integrations/runtime/index.d.mts +7 -2
- package/dist/build-integrations/runtime/index.mjs +9 -2
- package/dist/build-integrations/runtime/manifest-types.d.mts +2 -0
- package/dist/build-integrations/runtime/manifest-types.mjs +3 -0
- package/dist/build-integrations/runtime/playwright-stack-context-slot.d.mts +29 -0
- package/dist/build-integrations/runtime/playwright-stack-context-slot.mjs +10 -0
- package/dist/build-integrations/runtime/playwright-stack-context-slot.mjs.map +1 -0
- package/dist/build-integrations/runtime/read-stack-context.d.mts +1 -1
- package/dist/build-integrations/runtime/read-stack-context.mjs +10 -4
- package/dist/build-integrations/runtime/read-stack-context.mjs.map +1 -1
- package/dist/build-integrations/runtime/resolve-discovery-env.d.mts +48 -0
- package/dist/build-integrations/runtime/resolve-discovery-env.mjs +42 -0
- package/dist/build-integrations/runtime/resolve-discovery-env.mjs.map +1 -0
- package/dist/build-integrations/runtime/stack-context.d.mts +1 -1
- package/dist/build-integrations/runtime/wallet-paths.mjs +2 -0
- package/dist/build-integrations/vite/index.d.mts +51 -0
- package/dist/build-integrations/vite/index.mjs +73 -0
- package/dist/build-integrations/vite/index.mjs.map +1 -0
- package/dist/build-integrations/vitest/env.d.mts +4 -3
- package/dist/build-integrations/vitest/env.mjs +6 -4
- package/dist/build-integrations/vitest/env.mjs.map +1 -1
- package/dist/build-integrations/vitest/errors.d.mts +25 -4
- package/dist/build-integrations/vitest/errors.mjs +15 -2
- package/dist/build-integrations/vitest/errors.mjs.map +1 -1
- package/dist/build-integrations/vitest/index.d.mts +2 -2
- package/dist/build-integrations/vitest/index.mjs +3 -3
- package/dist/build-integrations/vitest/setup.d.mts +4 -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 +0 -1
- package/dist/build-integrations/vitest/stack-context.mjs +33 -13
- package/dist/build-integrations/vitest/stack-context.mjs.map +1 -1
- package/dist/cli/bail.mjs +23 -0
- package/dist/cli/bail.mjs.map +1 -0
- package/dist/{surfaces/cli/commands → cli}/doctor-probes.mjs +26 -16
- package/dist/cli/doctor-probes.mjs.map +1 -0
- package/dist/cli/main.d.mts +15 -1
- package/dist/cli/main.mjs +166 -482
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/prune-direct.mjs +31 -201
- package/dist/cli/prune-direct.mjs.map +1 -1
- package/dist/cli/snapshot-reader.mjs +14 -11
- package/dist/cli/snapshot-reader.mjs.map +1 -1
- package/dist/cli/up-lifecycle.mjs +4 -1
- package/dist/cli/up-lifecycle.mjs.map +1 -1
- package/dist/cli/wirings/apply.mjs +95 -0
- package/dist/cli/wirings/apply.mjs.map +1 -0
- package/dist/cli/wirings/build-verb-layers.mjs +42 -0
- package/dist/cli/wirings/build-verb-layers.mjs.map +1 -0
- package/dist/cli/wirings/config-loader.mjs +69 -0
- package/dist/cli/wirings/config-loader.mjs.map +1 -0
- package/dist/cli/wirings/engine-command.mjs +30 -0
- package/dist/cli/wirings/engine-command.mjs.map +1 -0
- package/dist/cli/wirings/identity.mjs +72 -0
- package/dist/cli/wirings/identity.mjs.map +1 -0
- package/dist/cli/wirings/provide-file-system.mjs +7 -0
- package/dist/cli/wirings/provide-file-system.mjs.map +1 -0
- package/dist/cli/wirings/snapshot.mjs +177 -0
- package/dist/cli/wirings/snapshot.mjs.map +1 -0
- package/dist/cli/wirings/up.mjs +287 -0
- package/dist/cli/wirings/up.mjs.map +1 -0
- package/dist/cli/wirings/wipe.mjs +40 -0
- package/dist/cli/wirings/wipe.mjs.map +1 -0
- package/dist/contracts/capability-decl.d.mts +0 -2
- package/dist/contracts/codegenable.d.mts +43 -0
- package/dist/contracts/container-runtime.d.mts +26 -1
- package/dist/contracts/faucet-strategy.d.mts +25 -0
- package/dist/contracts/funding-strategy.d.mts +42 -0
- package/dist/contracts/plugin-expander.mjs +60 -0
- package/dist/contracts/plugin-expander.mjs.map +1 -0
- package/dist/contracts/projection.d.mts +5 -3
- package/dist/contracts/renderer.d.mts +23 -0
- package/dist/contracts/routable.d.mts +14 -1
- package/dist/contracts/snapshotable.d.mts +25 -3
- package/dist/contracts/wallet-protocol.d.mts +52 -0
- package/dist/contracts/wallet-protocol.mjs +52 -0
- package/dist/contracts/wallet-protocol.mjs.map +1 -0
- package/dist/index.d.mts +26 -25
- package/dist/index.mjs +20 -13
- package/dist/{runtime → orchestrators}/built-in-plugin-layers.mjs +10 -5
- package/dist/orchestrators/built-in-plugin-layers.mjs.map +1 -0
- package/dist/orchestrators/codegen/bindings.d.mts +1 -2
- package/dist/orchestrators/codegen/bindings.mjs +52 -186
- package/dist/orchestrators/codegen/bindings.mjs.map +1 -1
- package/dist/orchestrators/codegen/emit.mjs +19 -1
- package/dist/orchestrators/codegen/emit.mjs.map +1 -1
- package/dist/orchestrators/codegen/errors.mjs +8 -3
- package/dist/orchestrators/codegen/errors.mjs.map +1 -1
- package/dist/orchestrators/codegen/format.mjs +47 -39
- package/dist/orchestrators/codegen/format.mjs.map +1 -1
- package/dist/orchestrators/codegen/gitignore.mjs +3 -1
- package/dist/orchestrators/codegen/gitignore.mjs.map +1 -1
- package/dist/orchestrators/codegen/output-location.mjs +41 -0
- package/dist/orchestrators/codegen/output-location.mjs.map +1 -0
- package/dist/orchestrators/codegen/paths.d.mts +1 -1
- package/dist/orchestrators/codegen/paths.mjs +40 -14
- package/dist/orchestrators/codegen/paths.mjs.map +1 -1
- package/dist/orchestrators/codegen/permissions.mjs +3 -1
- package/dist/orchestrators/codegen/permissions.mjs.map +1 -1
- package/dist/orchestrators/codegen/service.mjs +157 -105
- package/dist/orchestrators/codegen/service.mjs.map +1 -1
- package/dist/orchestrators/lifecycle-prune/errors.mjs +31 -0
- package/dist/orchestrators/lifecycle-prune/errors.mjs.map +1 -0
- package/dist/orchestrators/lifecycle-prune/index.mjs +350 -0
- package/dist/orchestrators/lifecycle-prune/index.mjs.map +1 -0
- package/dist/orchestrators/router/cleanup.mjs +26 -27
- package/dist/orchestrators/router/cleanup.mjs.map +1 -1
- package/dist/orchestrators/router/entrypoints.mjs +2 -2
- package/dist/orchestrators/router/entrypoints.mjs.map +1 -1
- package/dist/orchestrators/router/file-provider.mjs +14 -5
- package/dist/orchestrators/router/file-provider.mjs.map +1 -1
- package/dist/orchestrators/router/hostname.mjs +19 -55
- package/dist/orchestrators/router/hostname.mjs.map +1 -1
- package/dist/orchestrators/router/index.mjs +11 -0
- package/dist/orchestrators/router/profile.mjs +2 -2
- package/dist/orchestrators/router/sentinels.mjs +26 -0
- package/dist/orchestrators/router/sentinels.mjs.map +1 -0
- package/dist/orchestrators/router/service.mjs +40 -37
- package/dist/orchestrators/router/service.mjs.map +1 -1
- package/dist/orchestrators/router/traefik-container.mjs +27 -16
- package/dist/orchestrators/router/traefik-container.mjs.map +1 -1
- package/dist/{substrate/runtime → orchestrators}/run.mjs +26 -21
- package/dist/orchestrators/run.mjs.map +1 -0
- package/dist/orchestrators/runtime-composition.d.mts +1 -1
- package/dist/orchestrators/runtime-composition.mjs +123 -39
- package/dist/orchestrators/runtime-composition.mjs.map +1 -1
- package/dist/orchestrators/snapshot/capture-command.d.mts +1 -0
- package/dist/orchestrators/snapshot/capture-command.mjs +25 -0
- package/dist/orchestrators/snapshot/capture-command.mjs.map +1 -0
- package/dist/orchestrators/snapshot/capture.mjs +42 -28
- package/dist/orchestrators/snapshot/capture.mjs.map +1 -1
- package/dist/orchestrators/snapshot/descriptor.mjs +35 -10
- package/dist/orchestrators/snapshot/descriptor.mjs.map +1 -1
- package/dist/orchestrators/snapshot/identity-guard.mjs +1 -1
- package/dist/orchestrators/snapshot/index.mjs +13 -0
- package/dist/orchestrators/snapshot/integrity.d.mts +1 -1
- package/dist/orchestrators/snapshot/integrity.mjs +73 -17
- package/dist/orchestrators/snapshot/integrity.mjs.map +1 -1
- package/dist/orchestrators/snapshot/pending-marker.d.mts +1 -0
- package/dist/orchestrators/snapshot/pending-marker.mjs +99 -0
- package/dist/orchestrators/snapshot/pending-marker.mjs.map +1 -0
- package/dist/orchestrators/snapshot/phase-error.mjs +23 -0
- package/dist/orchestrators/snapshot/phase-error.mjs.map +1 -0
- package/dist/orchestrators/snapshot/prune.mjs +20 -55
- package/dist/orchestrators/snapshot/prune.mjs.map +1 -1
- package/dist/orchestrators/snapshot/recover-pending.d.mts +1 -0
- package/dist/orchestrators/snapshot/recover-pending.mjs +223 -0
- package/dist/orchestrators/snapshot/recover-pending.mjs.map +1 -0
- package/dist/orchestrators/snapshot/restore.mjs +118 -84
- package/dist/orchestrators/snapshot/restore.mjs.map +1 -1
- package/dist/orchestrators/snapshot/service.mjs +53 -56
- package/dist/orchestrators/snapshot/service.mjs.map +1 -1
- package/dist/orchestrators/snapshot/state-document.d.mts +1 -0
- package/dist/orchestrators/snapshot/state-document.mjs +47 -7
- package/dist/orchestrators/snapshot/state-document.mjs.map +1 -1
- package/dist/orchestrators/snapshot/wipe.mjs +67 -10
- package/dist/orchestrators/snapshot/wipe.mjs.map +1 -1
- package/dist/plugins/account/codegen.mjs +9 -8
- package/dist/plugins/account/codegen.mjs.map +1 -1
- package/dist/plugins/account/errors.d.mts +33 -6
- package/dist/plugins/account/errors.mjs.map +1 -1
- package/dist/plugins/account/funding.d.mts +29 -19
- package/dist/plugins/account/funding.mjs +84 -66
- package/dist/plugins/account/funding.mjs.map +1 -1
- package/dist/plugins/account/index.d.mts +2 -4
- package/dist/plugins/account/index.mjs +64 -31
- package/dist/plugins/account/index.mjs.map +1 -1
- package/dist/plugins/account/keypair.mjs +6 -9
- package/dist/plugins/account/keypair.mjs.map +1 -1
- package/dist/plugins/account/lease.mjs +1 -0
- package/dist/plugins/account/lease.mjs.map +1 -1
- package/dist/plugins/account/registry.d.mts +14 -1
- package/dist/plugins/account/registry.mjs +18 -18
- package/dist/plugins/account/registry.mjs.map +1 -1
- package/dist/plugins/account/service.d.mts +23 -4
- package/dist/plugins/account/service.mjs +83 -45
- package/dist/plugins/account/service.mjs.map +1 -1
- package/dist/plugins/account/snapshot.mjs +2 -5
- package/dist/plugins/account/snapshot.mjs.map +1 -1
- package/dist/plugins/account/spans.mjs +16 -0
- package/dist/plugins/account/spans.mjs.map +1 -0
- package/dist/plugins/account/variants/keystore.mjs +6 -8
- package/dist/plugins/account/variants/keystore.mjs.map +1 -1
- package/dist/plugins/action/build-context.d.mts +3 -2
- package/dist/plugins/action/errors.d.mts +16 -12
- package/dist/plugins/action/errors.mjs.map +1 -1
- package/dist/plugins/action/execute.mjs +65 -103
- package/dist/plugins/action/execute.mjs.map +1 -1
- package/dist/plugins/action/index.d.mts +0 -2
- package/dist/plugins/action/index.mjs +17 -13
- package/dist/plugins/action/index.mjs.map +1 -1
- package/dist/plugins/action/service.mjs +38 -35
- package/dist/plugins/action/service.mjs.map +1 -1
- package/dist/plugins/action/spans.mjs +11 -0
- package/dist/plugins/action/spans.mjs.map +1 -0
- package/dist/plugins/coin/codegen.mjs +9 -8
- package/dist/plugins/coin/codegen.mjs.map +1 -1
- package/dist/plugins/coin/discovery.mjs +8 -4
- package/dist/plugins/coin/discovery.mjs.map +1 -1
- package/dist/plugins/coin/index.d.mts +11 -29
- package/dist/plugins/coin/index.mjs +56 -27
- package/dist/plugins/coin/index.mjs.map +1 -1
- package/dist/plugins/coin/metadata.mjs +8 -15
- package/dist/plugins/coin/metadata.mjs.map +1 -1
- package/dist/plugins/coin/mint.d.mts +30 -11
- package/dist/plugins/coin/mint.mjs +78 -74
- package/dist/plugins/coin/mint.mjs.map +1 -1
- package/dist/plugins/coin/registry.mjs +4 -7
- package/dist/plugins/coin/registry.mjs.map +1 -1
- package/dist/plugins/coin/service.d.mts +39 -1
- package/dist/plugins/coin/service.mjs +11 -4
- package/dist/plugins/coin/service.mjs.map +1 -1
- package/dist/plugins/coin/spans.mjs +20 -0
- package/dist/plugins/coin/spans.mjs.map +1 -0
- package/dist/plugins/dashboard/domain.mjs +402 -0
- package/dist/plugins/dashboard/domain.mjs.map +1 -0
- package/dist/plugins/dashboard/index.d.mts +29 -0
- package/dist/plugins/dashboard/index.mjs +91 -0
- package/dist/plugins/dashboard/index.mjs.map +1 -0
- package/dist/plugins/dashboard/origin-policy.mjs +36 -0
- package/dist/plugins/dashboard/origin-policy.mjs.map +1 -0
- package/dist/plugins/dashboard/routable.mjs +27 -0
- package/dist/plugins/dashboard/routable.mjs.map +1 -0
- package/dist/plugins/dashboard/schema/builder.mjs +21 -0
- package/dist/plugins/dashboard/schema/builder.mjs.map +1 -0
- package/dist/plugins/dashboard/schema/enums.mjs +72 -0
- package/dist/plugins/dashboard/schema/enums.mjs.map +1 -0
- package/dist/plugins/dashboard/schema/root.mjs +351 -0
- package/dist/plugins/dashboard/schema/root.mjs.map +1 -0
- package/dist/plugins/dashboard/schema/types.mjs +508 -0
- package/dist/plugins/dashboard/schema/types.mjs.map +1 -0
- package/dist/plugins/dashboard/schema.mjs +11 -0
- package/dist/plugins/dashboard/schema.mjs.map +1 -0
- package/dist/plugins/dashboard/server.mjs +166 -0
- package/dist/plugins/dashboard/server.mjs.map +1 -0
- package/dist/plugins/deepbook/deploy.mjs +53 -115
- package/dist/plugins/deepbook/deploy.mjs.map +1 -1
- package/dist/plugins/deepbook/errors.mjs +1 -0
- package/dist/plugins/deepbook/errors.mjs.map +1 -1
- package/dist/plugins/deepbook/faucet-strategy.mjs +47 -34
- package/dist/plugins/deepbook/faucet-strategy.mjs.map +1 -1
- package/dist/plugins/deepbook/hash.mjs +9 -0
- package/dist/plugins/deepbook/hash.mjs.map +1 -0
- package/dist/plugins/deepbook/index.d.mts +1 -10
- package/dist/plugins/deepbook/index.mjs +25 -22
- package/dist/plugins/deepbook/index.mjs.map +1 -1
- package/dist/plugins/deepbook/pyth/index.mjs +26 -55
- package/dist/plugins/deepbook/pyth/index.mjs.map +1 -1
- package/dist/plugins/deepbook/snapshot.mjs +17 -29
- package/dist/plugins/deepbook/snapshot.mjs.map +1 -1
- package/dist/plugins/deepbook/spans.mjs +18 -0
- package/dist/plugins/deepbook/spans.mjs.map +1 -0
- package/dist/plugins/deepbook/type-strings.mjs +65 -0
- package/dist/plugins/deepbook/type-strings.mjs.map +1 -0
- package/dist/plugins/deepbook/types.d.mts +2 -3
- package/dist/plugins/deepbook/types.mjs.map +1 -1
- package/dist/plugins/faucet/dispatcher.d.mts +21 -2
- package/dist/plugins/faucet/dispatcher.mjs +1 -2
- package/dist/plugins/faucet/dispatcher.mjs.map +1 -1
- package/dist/plugins/faucet/errors.d.mts +11 -24
- package/dist/plugins/faucet/errors.mjs.map +1 -1
- package/dist/plugins/faucet/http.mjs +33 -14
- package/dist/plugins/faucet/http.mjs.map +1 -1
- package/dist/plugins/faucet/index.d.mts +15 -5
- package/dist/plugins/faucet/index.mjs +3 -0
- package/dist/plugins/faucet/index.mjs.map +1 -1
- package/dist/plugins/faucet/spans.mjs +12 -0
- package/dist/plugins/faucet/spans.mjs.map +1 -0
- package/dist/plugins/host-service/errors.d.mts +1 -1
- package/dist/plugins/host-service/index.mjs +34 -8
- package/dist/plugins/host-service/index.mjs.map +1 -1
- package/dist/plugins/host-service/routable.mjs +2 -1
- package/dist/plugins/host-service/routable.mjs.map +1 -1
- package/dist/plugins/host-service/service.d.mts +21 -0
- package/dist/plugins/host-service/service.mjs +136 -81
- package/dist/plugins/host-service/service.mjs.map +1 -1
- package/dist/plugins/internal/acquire-on-chain-artifact.mjs +23 -0
- package/dist/plugins/internal/acquire-on-chain-artifact.mjs.map +1 -0
- package/dist/plugins/internal/codegen-helpers.mjs +18 -0
- package/dist/plugins/internal/codegen-helpers.mjs.map +1 -0
- package/dist/plugins/internal/funding-failure-error.mjs +32 -0
- package/dist/plugins/internal/funding-failure-error.mjs.map +1 -0
- package/dist/plugins/package/build.mjs +1 -1
- package/dist/plugins/package/build.mjs.map +1 -1
- package/dist/plugins/package/codegen.mjs +26 -0
- package/dist/plugins/package/codegen.mjs.map +1 -1
- package/dist/plugins/package/errors.d.mts +16 -6
- package/dist/plugins/package/errors.mjs.map +1 -1
- package/dist/plugins/package/index.d.mts +0 -4
- package/dist/plugins/package/index.mjs +27 -21
- package/dist/plugins/package/index.mjs.map +1 -1
- package/dist/plugins/package/mode-known.mjs +26 -12
- package/dist/plugins/package/mode-known.mjs.map +1 -1
- package/dist/plugins/package/mode-local.mjs +47 -41
- package/dist/plugins/package/mode-local.mjs.map +1 -1
- package/dist/plugins/package/publish-executor.mjs +55 -114
- package/dist/plugins/package/publish-executor.mjs.map +1 -1
- package/dist/plugins/package/registry.mjs +2 -1
- package/dist/plugins/package/registry.mjs.map +1 -1
- package/dist/plugins/package/service.mjs.map +1 -1
- package/dist/plugins/package/spans.mjs +14 -0
- package/dist/plugins/package/spans.mjs.map +1 -0
- package/dist/plugins/postgres/db-ensure.mjs +19 -12
- package/dist/plugins/postgres/db-ensure.mjs.map +1 -1
- package/dist/plugins/postgres/errors.d.mts +5 -0
- package/dist/plugins/postgres/errors.mjs.map +1 -1
- package/dist/plugins/postgres/index.mjs +10 -17
- package/dist/plugins/postgres/index.mjs.map +1 -1
- package/dist/plugins/postgres/service.mjs +58 -24
- package/dist/plugins/postgres/service.mjs.map +1 -1
- package/dist/plugins/postgres/snapshot.mjs.map +1 -1
- package/dist/plugins/postgres/spans.mjs +11 -0
- package/dist/plugins/postgres/spans.mjs.map +1 -0
- package/dist/plugins/router-entrypoints.mjs +4 -4
- package/dist/plugins/router-entrypoints.mjs.map +1 -1
- package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs +15 -13
- package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs.map +1 -1
- package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs +15 -19
- package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs.map +1 -1
- package/dist/plugins/seal/codegen.d.mts +2 -3
- package/dist/plugins/seal/codegen.mjs.map +1 -1
- package/dist/plugins/seal/config-render.mjs +35 -8
- package/dist/plugins/seal/config-render.mjs.map +1 -1
- package/dist/plugins/seal/deploy.mjs +31 -39
- package/dist/plugins/seal/deploy.mjs.map +1 -1
- package/dist/plugins/seal/errors.d.mts +1 -3
- package/dist/plugins/seal/errors.mjs +7 -1
- package/dist/plugins/seal/errors.mjs.map +1 -1
- package/dist/plugins/seal/index.d.mts +1 -6
- package/dist/plugins/seal/index.mjs +40 -14
- package/dist/plugins/seal/index.mjs.map +1 -1
- package/dist/plugins/seal/key-manager.d.mts +1 -9
- package/dist/plugins/seal/key-manager.mjs +2 -18
- package/dist/plugins/seal/key-manager.mjs.map +1 -1
- package/dist/plugins/seal/key-server.mjs +37 -36
- package/dist/plugins/seal/key-server.mjs.map +1 -1
- package/dist/plugins/seal/keygen.mjs +16 -17
- package/dist/plugins/seal/keygen.mjs.map +1 -1
- package/dist/plugins/seal/mode/fork-known.mjs +36 -14
- package/dist/plugins/seal/mode/fork-known.mjs.map +1 -1
- package/dist/plugins/seal/mode/live.d.mts +6 -4
- package/dist/plugins/seal/mode/live.mjs +14 -21
- package/dist/plugins/seal/mode/live.mjs.map +1 -1
- package/dist/plugins/seal/mode/local-keygen.mjs +57 -21
- package/dist/plugins/seal/mode/local-keygen.mjs.map +1 -1
- package/dist/plugins/seal/registry-publish.mjs +1 -0
- 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/spans.mjs +18 -0
- package/dist/plugins/seal/spans.mjs.map +1 -0
- package/dist/plugins/sui/auto-tick.mjs +17 -23
- package/dist/plugins/sui/auto-tick.mjs.map +1 -1
- package/dist/plugins/sui/chain-build-container.mjs +18 -0
- package/dist/plugins/sui/chain-build-container.mjs.map +1 -0
- package/dist/plugins/sui/chain-probe.d.mts +5 -5
- package/dist/plugins/sui/chain-probe.mjs +28 -58
- package/dist/plugins/sui/chain-probe.mjs.map +1 -1
- package/dist/plugins/sui/codegen.mjs +26 -0
- package/dist/plugins/sui/codegen.mjs.map +1 -1
- package/dist/plugins/sui/errors.d.mts +2 -29
- package/dist/plugins/sui/errors.mjs +1 -3
- package/dist/plugins/sui/errors.mjs.map +1 -1
- package/dist/plugins/sui/fork-faucet-strategy.d.mts +1 -0
- package/dist/plugins/sui/fork-faucet-strategy.mjs +55 -0
- package/dist/plugins/sui/fork-faucet-strategy.mjs.map +1 -0
- package/dist/plugins/sui/fork-orchestration.d.mts +1 -0
- package/dist/plugins/sui/fork-orchestration.mjs +170 -2
- package/dist/plugins/sui/fork-orchestration.mjs.map +1 -1
- package/dist/plugins/sui/fork-transaction.d.mts +2 -0
- package/dist/plugins/sui/fork-transaction.mjs +92 -28
- package/dist/plugins/sui/fork-transaction.mjs.map +1 -1
- package/dist/plugins/sui/index.d.mts +33 -70
- package/dist/plugins/sui/index.mjs +58 -35
- package/dist/plugins/sui/index.mjs.map +1 -1
- package/dist/plugins/{faucet/strategies/sui-local.mjs → sui/local-faucet-strategy.mjs} +10 -7
- package/dist/plugins/sui/local-faucet-strategy.mjs.map +1 -0
- package/dist/plugins/sui/mode/external.mjs +7 -13
- package/dist/plugins/sui/mode/external.mjs.map +1 -1
- package/dist/plugins/sui/mode/fork.mjs +136 -59
- package/dist/plugins/sui/mode/fork.mjs.map +1 -1
- package/dist/plugins/sui/mode/live.mjs +10 -16
- package/dist/plugins/sui/mode/live.mjs.map +1 -1
- package/dist/plugins/sui/mode/local.mjs +37 -36
- package/dist/plugins/sui/mode/local.mjs.map +1 -1
- package/dist/plugins/sui/mode/shared-boot.mjs +38 -115
- package/dist/plugins/sui/mode/shared-boot.mjs.map +1 -1
- package/dist/plugins/sui/mode/shared.d.mts +1 -5
- package/dist/plugins/sui/mode/shared.mjs.map +1 -1
- package/dist/plugins/sui/mode/spec.d.mts +18 -0
- package/dist/plugins/sui/move-summary-runner.mjs +207 -0
- package/dist/plugins/sui/move-summary-runner.mjs.map +1 -0
- package/dist/plugins/sui/network-resolver.d.mts +4 -3
- package/dist/plugins/sui/service.mjs.map +1 -1
- package/dist/plugins/sui/snapshot.mjs +3 -0
- package/dist/plugins/sui/snapshot.mjs.map +1 -1
- package/dist/plugins/sui/spans.mjs +17 -0
- package/dist/plugins/sui/spans.mjs.map +1 -0
- package/dist/plugins/wallet/codegen.d.mts +0 -1
- package/dist/plugins/wallet/codegen.mjs +8 -9
- package/dist/plugins/wallet/codegen.mjs.map +1 -1
- package/dist/plugins/wallet/index.d.mts +6 -8
- package/dist/plugins/wallet/index.mjs +46 -35
- package/dist/plugins/wallet/index.mjs.map +1 -1
- package/dist/plugins/wallet/origin-policy.mjs +12 -28
- package/dist/plugins/wallet/origin-policy.mjs.map +1 -1
- package/dist/plugins/wallet/pairing.mjs +6 -4
- package/dist/plugins/wallet/pairing.mjs.map +1 -1
- package/dist/plugins/wallet/protocol.d.mts +1 -0
- package/dist/plugins/wallet/protocol.mjs +5 -44
- package/dist/plugins/wallet/protocol.mjs.map +1 -1
- package/dist/plugins/wallet/routable.mjs +2 -8
- package/dist/plugins/wallet/routable.mjs.map +1 -1
- package/dist/plugins/wallet/server.mjs +36 -44
- package/dist/plugins/wallet/server.mjs.map +1 -1
- package/dist/plugins/wallet/service.d.mts +1 -5
- package/dist/plugins/wallet/service.mjs +22 -18
- package/dist/plugins/wallet/service.mjs.map +1 -1
- package/dist/plugins/wallet/spans.mjs +22 -0
- package/dist/plugins/wallet/spans.mjs.map +1 -0
- package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs +33 -8
- package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs.map +1 -1
- package/dist/plugins/walrus/codegen.d.mts +3 -3
- package/dist/plugins/walrus/codegen.mjs.map +1 -1
- package/dist/plugins/walrus/deploy-paths.mjs +21 -5
- package/dist/plugins/walrus/deploy-paths.mjs.map +1 -1
- package/dist/plugins/walrus/deploy.mjs +67 -69
- package/dist/plugins/walrus/deploy.mjs.map +1 -1
- package/dist/plugins/walrus/errors.mjs +1 -0
- package/dist/plugins/walrus/errors.mjs.map +1 -1
- package/dist/plugins/walrus/faucet-strategy.mjs +12 -7
- package/dist/plugins/walrus/faucet-strategy.mjs.map +1 -1
- package/dist/plugins/walrus/index.d.mts +2 -13
- package/dist/plugins/walrus/index.mjs +18 -23
- package/dist/plugins/walrus/index.mjs.map +1 -1
- package/dist/plugins/walrus/mode/known-deploy.mjs +7 -5
- package/dist/plugins/walrus/mode/known-deploy.mjs.map +1 -1
- package/dist/plugins/walrus/mode/local-cluster.mjs +26 -7
- package/dist/plugins/walrus/mode/local-cluster.mjs.map +1 -1
- package/dist/plugins/walrus/registry-publish.mjs.map +1 -1
- package/dist/plugins/walrus/routable.mjs +7 -23
- package/dist/plugins/walrus/routable.mjs.map +1 -1
- package/dist/plugins/walrus/service.mjs.map +1 -1
- package/dist/plugins/walrus/spans.mjs +18 -0
- package/dist/plugins/walrus/spans.mjs.map +1 -0
- package/dist/plugins/walrus/storage-nodes.d.mts +6 -2
- package/dist/plugins/walrus/storage-nodes.mjs +15 -8
- package/dist/plugins/walrus/storage-nodes.mjs.map +1 -1
- package/dist/plugins/walrus/wal-swap.mjs +58 -15
- package/dist/plugins/walrus/wal-swap.mjs.map +1 -1
- package/dist/primitives/artifact-publisher.d.mts +1 -1
- package/dist/primitives/artifact-publisher.mjs +15 -0
- package/dist/primitives/artifact-publisher.mjs.map +1 -0
- package/dist/runtime/docker/client.mjs +17 -4
- package/dist/runtime/docker/client.mjs.map +1 -1
- package/dist/runtime/docker/container.mjs +219 -132
- package/dist/runtime/docker/container.mjs.map +1 -1
- package/dist/runtime/docker/errors.mjs +0 -6
- package/dist/runtime/docker/errors.mjs.map +1 -1
- package/dist/runtime/docker/exec.mjs +21 -18
- package/dist/runtime/docker/exec.mjs.map +1 -1
- package/dist/runtime/docker/image.mjs +36 -13
- package/dist/runtime/docker/image.mjs.map +1 -1
- package/dist/runtime/docker/index.mjs +14 -0
- package/dist/runtime/docker/inspect-and-decode.mjs +51 -0
- package/dist/runtime/docker/inspect-and-decode.mjs.map +1 -0
- package/dist/runtime/docker/inventory.mjs +14 -11
- package/dist/runtime/docker/inventory.mjs.map +1 -1
- package/dist/runtime/docker/labels.mjs +38 -5
- package/dist/runtime/docker/labels.mjs.map +1 -1
- package/dist/runtime/docker/logs.d.mts +1 -1
- package/dist/runtime/docker/logs.mjs.map +1 -1
- package/dist/runtime/docker/network.mjs +83 -47
- package/dist/runtime/docker/network.mjs.map +1 -1
- package/dist/runtime/docker/render-run-args.mjs +82 -0
- package/dist/runtime/docker/render-run-args.mjs.map +1 -0
- package/dist/runtime/docker/service.d.mts +1 -1
- package/dist/runtime/docker/service.mjs +12 -7
- package/dist/runtime/docker/service.mjs.map +1 -1
- package/dist/runtime/docker/sweep.mjs +118 -43
- package/dist/runtime/docker/sweep.mjs.map +1 -1
- package/dist/runtime/docker/volume.mjs +2 -0
- package/dist/runtime/docker/volume.mjs.map +1 -1
- package/dist/runtime/docker/wrap.mjs +30 -1
- package/dist/runtime/docker/wrap.mjs.map +1 -1
- package/dist/substrate/cross-process.mjs +14 -8
- package/dist/substrate/cross-process.mjs.map +1 -1
- package/dist/substrate/event-time.mjs +33 -0
- package/dist/substrate/event-time.mjs.map +1 -0
- package/dist/substrate/events.d.mts +28 -8
- package/dist/substrate/identity.d.mts +2 -1
- package/dist/substrate/manifest.d.mts +68 -5
- package/dist/substrate/manifest.mjs +34 -5
- package/dist/substrate/manifest.mjs.map +1 -1
- package/dist/substrate/network.d.mts +3 -5
- package/dist/substrate/options.d.mts +0 -2
- package/dist/substrate/plugin.d.mts +13 -0
- package/dist/substrate/plugin.mjs +4 -2
- package/dist/substrate/plugin.mjs.map +1 -1
- package/dist/substrate/projection.d.mts +35 -3
- package/dist/substrate/runtime/artifact-publisher/index.mjs +19 -9
- package/dist/substrate/runtime/artifact-publisher/index.mjs.map +1 -1
- package/dist/substrate/runtime/atomic-write.mjs +97 -32
- package/dist/substrate/runtime/atomic-write.mjs.map +1 -1
- package/dist/substrate/runtime/cache/index.mjs +3 -0
- package/dist/substrate/runtime/cache/schema.mjs +2 -2
- package/dist/substrate/runtime/cache/schema.mjs.map +1 -1
- package/dist/substrate/runtime/cache/service.mjs +9 -7
- package/dist/substrate/runtime/cache/service.mjs.map +1 -1
- package/dist/substrate/runtime/capability-sinks/index.d.mts +1 -1
- package/dist/substrate/runtime/capability-sinks/index.mjs +3 -0
- package/dist/substrate/runtime/capability-sinks/layer.mjs +1 -1
- package/dist/substrate/runtime/capability-sinks/service.d.mts +61 -2
- package/dist/substrate/runtime/capability-sinks/service.mjs +21 -26
- package/dist/substrate/runtime/capability-sinks/service.mjs.map +1 -1
- package/dist/substrate/runtime/config-validation.d.mts +1 -13
- package/dist/substrate/runtime/config-validation.mjs +2 -91
- package/dist/substrate/runtime/config-validation.mjs.map +1 -1
- package/dist/substrate/runtime/container-runtime.d.mts +1 -0
- package/dist/substrate/runtime/container-runtime.mjs +2 -0
- package/dist/substrate/runtime/control-plane/domain.mjs +101 -0
- package/dist/substrate/runtime/control-plane/domain.mjs.map +1 -0
- package/dist/substrate/runtime/control-plane/service.mjs +7 -0
- package/dist/substrate/runtime/control-plane/service.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/command-channel/channel.mjs +46 -19
- package/dist/substrate/runtime/cross-process/command-channel/channel.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs +30 -12
- package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/command-channel/index.mjs +5 -0
- package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs +11 -2
- 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 +1 -1
- package/dist/substrate/runtime/cross-process/index.mjs +11 -0
- package/dist/substrate/runtime/cross-process/live-clock.mjs +34 -0
- package/dist/substrate/runtime/cross-process/live-clock.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/liveness.mjs +52 -10
- package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/lock.d.mts +1 -0
- package/dist/substrate/runtime/cross-process/lock.mjs +23 -0
- package/dist/substrate/runtime/cross-process/lock.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs +64 -0
- package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/roster.mjs +97 -83
- package/dist/substrate/runtime/cross-process/roster.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/self-pid.mjs +8 -0
- package/dist/substrate/runtime/cross-process/self-pid.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs +24 -28
- package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/stack-lock.mjs +41 -20
- package/dist/substrate/runtime/cross-process/stack-lock.mjs.map +1 -1
- package/dist/substrate/runtime/current-plugin.mjs +2 -2
- package/dist/substrate/runtime/current-plugin.mjs.map +1 -1
- package/dist/substrate/runtime/errors.mjs +12 -6
- package/dist/substrate/runtime/errors.mjs.map +1 -1
- package/dist/substrate/runtime/format-unknown-error.mjs +30 -0
- package/dist/substrate/runtime/format-unknown-error.mjs.map +1 -0
- package/dist/substrate/runtime/host-bind-mount-owner.mjs +10 -0
- package/dist/substrate/runtime/host-bind-mount-owner.mjs.map +1 -0
- package/dist/substrate/runtime/host-gateway.mjs +20 -0
- package/dist/substrate/runtime/host-gateway.mjs.map +1 -0
- package/dist/substrate/runtime/host-tree-tar/index.mjs +61 -50
- package/dist/substrate/runtime/host-tree-tar/index.mjs.map +1 -1
- package/dist/substrate/runtime/http-probe.d.mts +1 -1
- package/dist/substrate/runtime/index.mjs +14 -0
- package/dist/substrate/runtime/lease-broker/index.d.mts +1 -1
- package/dist/substrate/runtime/lease-broker/index.mjs +2 -0
- package/dist/substrate/runtime/lease-broker/service.d.mts +1 -64
- package/dist/substrate/runtime/lease-broker/service.mjs +1 -1
- package/dist/substrate/runtime/lease-broker/service.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/dep-graph.mjs +22 -3
- package/dist/substrate/runtime/lifecycle/dep-graph.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/index.mjs +9 -0
- package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/plugin-registry.mjs +20 -3
- package/dist/substrate/runtime/lifecycle/plugin-registry.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/selective-restart.mjs +1 -1
- package/dist/substrate/runtime/lifecycle/signals.mjs +5 -5
- package/dist/substrate/runtime/lifecycle/signals.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/state-machine.mjs +1 -1
- package/dist/substrate/runtime/managed-container.d.mts +19 -1
- package/dist/substrate/runtime/managed-container.mjs +20 -1
- package/dist/substrate/runtime/managed-container.mjs.map +1 -1
- package/dist/substrate/runtime/manifest/index.mjs +3 -0
- package/dist/substrate/runtime/manifest/manifest.mjs +15 -11
- package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -1
- package/dist/substrate/runtime/mode-errors.mjs +20 -0
- package/dist/substrate/runtime/mode-errors.mjs.map +1 -0
- package/dist/substrate/runtime/observability/cascade-formatter.mjs +9 -13
- package/dist/substrate/runtime/observability/cascade-formatter.mjs.map +1 -1
- package/dist/substrate/runtime/observability/formatter-registry.mjs +19 -36
- package/dist/substrate/runtime/observability/formatter-registry.mjs.map +1 -1
- package/dist/substrate/runtime/observability/ignore-with-log.d.mts +1 -0
- package/dist/substrate/runtime/observability/ignore-with-log.mjs +17 -0
- package/dist/substrate/runtime/observability/ignore-with-log.mjs.map +1 -0
- package/dist/substrate/runtime/observability/index.d.mts +1 -1
- package/dist/substrate/runtime/observability/index.mjs +13 -0
- package/dist/substrate/runtime/observability/log-store.d.mts +1 -0
- package/dist/substrate/runtime/observability/log-store.mjs +175 -0
- package/dist/substrate/runtime/observability/log-store.mjs.map +1 -0
- package/dist/substrate/runtime/observability/logger.d.mts +2 -1
- package/dist/substrate/runtime/observability/logger.mjs +5 -9
- package/dist/substrate/runtime/observability/logger.mjs.map +1 -1
- package/dist/substrate/runtime/observability/output-truncate.mjs +35 -0
- package/dist/substrate/runtime/observability/output-truncate.mjs.map +1 -0
- package/dist/substrate/runtime/observability/pretty-error.mjs +9 -12
- package/dist/substrate/runtime/observability/pretty-error.mjs.map +1 -1
- package/dist/substrate/runtime/observability/redaction.d.mts +2 -13
- package/dist/substrate/runtime/observability/redaction.mjs +8 -14
- package/dist/substrate/runtime/observability/redaction.mjs.map +1 -1
- package/dist/substrate/runtime/observability/span-store.d.mts +1 -0
- package/dist/substrate/runtime/observability/span-store.mjs +110 -0
- package/dist/substrate/runtime/observability/span-store.mjs.map +1 -0
- package/dist/substrate/runtime/observability/spans.mjs +14 -14
- package/dist/substrate/runtime/observability/spans.mjs.map +1 -1
- package/dist/substrate/runtime/observability/subprocess-capture.mjs +1 -1
- package/dist/substrate/runtime/passthrough-or-wrap.mjs +33 -0
- package/dist/substrate/runtime/passthrough-or-wrap.mjs.map +1 -0
- package/dist/substrate/runtime/paths.d.mts +1 -1
- package/dist/substrate/runtime/paths.mjs +4 -3
- package/dist/substrate/runtime/paths.mjs.map +1 -1
- package/dist/substrate/runtime/phase-preserving-produce.mjs +40 -0
- package/dist/substrate/runtime/phase-preserving-produce.mjs.map +1 -0
- package/dist/substrate/runtime/port-broker/index.mjs +3 -0
- package/dist/substrate/runtime/port-broker/service.mjs +170 -126
- package/dist/substrate/runtime/port-broker/service.mjs.map +1 -1
- package/dist/substrate/runtime/post-acquire-tasks.mjs +1 -1
- package/dist/substrate/runtime/post-acquire-tasks.mjs.map +1 -1
- package/dist/substrate/runtime/probes.d.mts +7 -5
- package/dist/substrate/runtime/probes.mjs +3 -1
- package/dist/substrate/runtime/probes.mjs.map +1 -1
- package/dist/substrate/runtime/projection/index.mjs +4 -0
- package/dist/substrate/runtime/projection/operational-endpoints.mjs +1 -2
- package/dist/substrate/runtime/projection/operational-endpoints.mjs.map +1 -1
- package/dist/substrate/runtime/projection/persisted.mjs +33 -15
- package/dist/substrate/runtime/projection/persisted.mjs.map +1 -1
- package/dist/substrate/runtime/projection/state-ref.mjs +26 -2
- package/dist/substrate/runtime/projection/state-ref.mjs.map +1 -1
- package/dist/substrate/runtime/projection/update.mjs +88 -24
- package/dist/substrate/runtime/projection/update.mjs.map +1 -1
- package/dist/substrate/runtime/random-suffix.mjs +11 -0
- package/dist/substrate/runtime/random-suffix.mjs.map +1 -0
- package/dist/substrate/runtime/retry-policy.d.mts +59 -1
- package/dist/substrate/runtime/retry-policy.mjs +66 -1
- package/dist/substrate/runtime/retry-policy.mjs.map +1 -1
- package/dist/substrate/runtime/routed-url.d.mts +1 -0
- package/dist/substrate/runtime/routed-url.mjs +79 -0
- package/dist/substrate/runtime/routed-url.mjs.map +1 -0
- package/dist/substrate/runtime/runtime-decode.d.mts +1 -1
- package/dist/substrate/runtime/runtime-decode.mjs.map +1 -1
- package/dist/substrate/runtime/scoped-http-server.mjs +2 -3
- package/dist/substrate/runtime/scoped-http-server.mjs.map +1 -1
- package/dist/substrate/runtime/scoped-multimap/index.mjs +2 -0
- package/dist/substrate/runtime/scoped-multimap/service.mjs +52 -0
- package/dist/substrate/runtime/scoped-multimap/service.mjs.map +1 -0
- package/dist/substrate/runtime/scoped-ref-map/index.mjs +2 -0
- package/dist/substrate/runtime/scoped-ref-map/service.mjs +2 -2
- package/dist/substrate/runtime/scoped-ref-map/service.mjs.map +1 -1
- package/dist/substrate/runtime/stage-and-swap/index.mjs +72 -4
- package/dist/substrate/runtime/stage-and-swap/index.mjs.map +1 -1
- package/dist/substrate/runtime/state-store/index.d.mts +1 -0
- package/dist/substrate/runtime/state-store/index.mjs +3 -0
- package/dist/substrate/runtime/state-store/schema.d.mts +1 -0
- package/dist/substrate/runtime/state-store/schema.mjs +19 -14
- package/dist/substrate/runtime/state-store/schema.mjs.map +1 -1
- package/dist/substrate/runtime/state-store/service.d.mts +1 -0
- package/dist/substrate/runtime/state-store/service.mjs +145 -0
- package/dist/substrate/runtime/state-store/service.mjs.map +1 -0
- package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.d.mts +1 -0
- package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs +33 -0
- package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs.map +1 -0
- package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs +1 -1
- package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs.map +1 -1
- package/dist/substrate/runtime/strategy-registry/index.mjs +4 -0
- package/dist/substrate/runtime/strategy-registry/service.mjs +19 -33
- package/dist/substrate/runtime/strategy-registry/service.mjs.map +1 -1
- package/dist/substrate/runtime/subnet-broker.mjs +60 -0
- package/dist/substrate/runtime/subnet-broker.mjs.map +1 -0
- package/dist/substrate/runtime/sui-execute/index.d.mts +21 -1
- package/dist/substrate/runtime/sui-execute/index.mjs +84 -20
- package/dist/substrate/runtime/sui-execute/index.mjs.map +1 -1
- package/dist/substrate/runtime/sui-execute/sign-and-dispatch.d.mts +1 -0
- package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs +32 -0
- package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs.map +1 -0
- package/dist/substrate/runtime/sui-ledger/object-ref.mjs +32 -0
- package/dist/substrate/runtime/sui-ledger/object-ref.mjs.map +1 -0
- package/dist/substrate/runtime/sui-move-build/index.mjs +93 -19
- package/dist/substrate/runtime/sui-move-build/index.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/acquire-node.mjs +181 -0
- package/dist/substrate/runtime/supervisor/acquire-node.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/background-tasks.mjs +144 -0
- package/dist/substrate/runtime/supervisor/background-tasks.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/command-loop.mjs +68 -0
- package/dist/substrate/runtime/supervisor/command-loop.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs +151 -0
- package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/errors.d.mts +1 -0
- package/dist/substrate/runtime/supervisor/errors.mjs +9 -0
- package/dist/substrate/runtime/supervisor/errors.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/index.d.mts +1 -0
- package/dist/substrate/runtime/supervisor/index.mjs +3 -0
- package/dist/substrate/runtime/supervisor/shutdown.mjs +51 -0
- package/dist/substrate/runtime/supervisor/shutdown.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/start-supervisor.d.mts +1 -0
- package/dist/substrate/runtime/supervisor/start-supervisor.mjs +250 -0
- package/dist/substrate/runtime/supervisor/start-supervisor.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/state.d.mts +1 -0
- package/dist/substrate/runtime/supervisor/state.mjs +22 -0
- package/dist/substrate/runtime/supervisor/state.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/teardown.mjs +57 -0
- package/dist/substrate/runtime/supervisor/teardown.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/types.d.mts +1 -0
- package/dist/substrate/runtime/supervisor/wiring.mjs +133 -0
- package/dist/substrate/runtime/supervisor/wiring.mjs.map +1 -0
- package/dist/substrate/runtime/typed-env.mjs +8 -0
- package/dist/substrate/runtime/typed-env.mjs.map +1 -0
- package/dist/substrate/state-store.d.mts +1 -0
- package/dist/substrate/versioned-doc-schema.mjs +17 -0
- package/dist/substrate/versioned-doc-schema.mjs.map +1 -0
- package/dist/substrate/versioned-doc-sync.mjs +94 -0
- package/dist/substrate/versioned-doc-sync.mjs.map +1 -0
- package/dist/surfaces/cli/command-tree.mjs.map +1 -1
- package/dist/surfaces/cli/commands/index.mjs +9 -0
- package/dist/surfaces/cli/commands/prune-picker-entry.mjs +13 -0
- package/dist/surfaces/cli/commands/prune-picker-entry.mjs.map +1 -0
- package/dist/surfaces/cli/commands/prune-picker.mjs +3 -2
- package/dist/surfaces/cli/commands/prune-picker.mjs.map +1 -1
- package/dist/surfaces/cli/commands/prune.mjs +34 -17
- package/dist/surfaces/cli/commands/prune.mjs.map +1 -1
- package/dist/surfaces/cli/commands/status.mjs +2 -2
- package/dist/surfaces/cli/commands/status.mjs.map +1 -1
- package/dist/surfaces/cli/commands/supervisor-presence.mjs +5 -3
- package/dist/surfaces/cli/commands/supervisor-presence.mjs.map +1 -1
- package/dist/surfaces/cli/commands/wipe.mjs +38 -4
- package/dist/surfaces/cli/commands/wipe.mjs.map +1 -1
- package/dist/surfaces/cli/errors.mjs +1 -1
- package/dist/surfaces/cli/errors.mjs.map +1 -1
- package/dist/surfaces/cli/flags.mjs +0 -1
- package/dist/surfaces/cli/flags.mjs.map +1 -1
- package/dist/surfaces/cli/index.mjs +98 -18
- package/dist/surfaces/cli/index.mjs.map +1 -1
- package/dist/surfaces/cli/output.mjs +1 -0
- package/dist/surfaces/cli/output.mjs.map +1 -1
- package/dist/surfaces/cli/sysexits.mjs +2 -1
- package/dist/surfaces/cli/sysexits.mjs.map +1 -1
- package/dist/surfaces/tui/app.mjs +25 -3
- package/dist/surfaces/tui/app.mjs.map +1 -1
- package/dist/surfaces/tui/display-derivation.mjs +23 -74
- package/dist/surfaces/tui/display-derivation.mjs.map +1 -1
- package/dist/surfaces/tui/event-log.mjs +35 -23
- package/dist/surfaces/tui/event-log.mjs.map +1 -1
- package/dist/surfaces/tui/input.mjs +5 -1
- package/dist/surfaces/tui/input.mjs.map +1 -1
- package/dist/surfaces/tui/plain-renderer.mjs +70 -34
- package/dist/surfaces/tui/plain-renderer.mjs.map +1 -1
- package/package.json +24 -4
- package/dist/contracts/liveness-classifier.d.mts +0 -19
- package/dist/contracts/network-resolver.d.mts +0 -15
- package/dist/contracts/network-resolver.mjs +0 -7
- package/dist/contracts/network-resolver.mjs.map +0 -1
- package/dist/orchestrators/codegen/extras.mjs +0 -16
- package/dist/orchestrators/codegen/extras.mjs.map +0 -1
- package/dist/plugins/deepbook/routable.mjs +0 -21
- package/dist/plugins/deepbook/routable.mjs.map +0 -1
- package/dist/plugins/faucet/service.d.mts +0 -20
- package/dist/plugins/faucet/strategies/sui-local.d.mts +0 -23
- package/dist/plugins/faucet/strategies/sui-local.mjs.map +0 -1
- package/dist/plugins/sui/seed-objects.d.mts +0 -18
- package/dist/plugins/sui/seed-objects.mjs +0 -25
- package/dist/plugins/sui/seed-objects.mjs.map +0 -1
- package/dist/runtime/built-in-plugin-layers.mjs.map +0 -1
- package/dist/substrate/runtime/context-helpers.mjs +0 -32
- package/dist/substrate/runtime/context-helpers.mjs.map +0 -1
- package/dist/substrate/runtime/run.mjs.map +0 -1
- package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs +0 -30
- package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs.map +0 -1
- package/dist/substrate/runtime/supervisor.d.mts +0 -1
- package/dist/substrate/runtime/supervisor.mjs +0 -831
- package/dist/substrate/runtime/supervisor.mjs.map +0 -1
- package/dist/surfaces/cli/commands/doctor-probes.mjs.map +0 -1
- /package/dist/{substrate/runtime/strategy-registry/faucet-capability-for.d.mts → plugins/faucet/http.d.mts} +0 -0
|
@@ -29,6 +29,32 @@ const failStage = (stage, targetPath, stagingPath) => (cause) => Effect.fail(new
|
|
|
29
29
|
stagingPath,
|
|
30
30
|
cause
|
|
31
31
|
}));
|
|
32
|
+
/**
|
|
33
|
+
* Extract the original Node errno `code` from a `FileSystem.rename` failure.
|
|
34
|
+
*
|
|
35
|
+
* Effect v4's `@effect/platform-node` wraps the raw `NodeJS.ErrnoException`
|
|
36
|
+
* (`handleErrnoException` in `platform-node-shared/internal/utils.ts`) into a
|
|
37
|
+
* `PlatformError` whose `reason` is a `SystemError`. The raw errno is preserved
|
|
38
|
+
* as `reason.cause` (the unmodified Node error) — and the `PlatformError`
|
|
39
|
+
* constructor additionally hoists that to the wrapper's own top-level `cause`.
|
|
40
|
+
* So a genuine cross-filesystem rename surfaces the code at `reason.cause.code`
|
|
41
|
+
* (and, equivalently, at `cause.cause.code`), NOT at the top-level `.code`.
|
|
42
|
+
*
|
|
43
|
+
* We probe every plausible location defensively: the nested SystemError cause,
|
|
44
|
+
* the hoisted wrapper cause, and a bare-errno shape (in case a caller injects a
|
|
45
|
+
* raw `NodeJS.ErrnoException` or the wrapping ever changes).
|
|
46
|
+
*/
|
|
47
|
+
const errnoCode = (cause) => {
|
|
48
|
+
const codeOf = (value) => {
|
|
49
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
50
|
+
const code = value.code;
|
|
51
|
+
return typeof code === "string" ? code : void 0;
|
|
52
|
+
};
|
|
53
|
+
if (typeof cause !== "object" || cause === null) return void 0;
|
|
54
|
+
const reasonCause = cause.reason?.cause;
|
|
55
|
+
const directCause = cause.cause;
|
|
56
|
+
return codeOf(reasonCause) ?? codeOf(directCause) ?? codeOf(cause);
|
|
57
|
+
};
|
|
32
58
|
const normalizePreservedRelativePath = (relativePath) => {
|
|
33
59
|
const normalized = normalize(relativePath);
|
|
34
60
|
if (relativePath.length === 0 || isAbsolute(relativePath) || normalized === "." || normalized === ".." || normalized.startsWith(`..${sep}`)) return null;
|
|
@@ -42,6 +68,9 @@ const preserveTargetPaths = (args) => Effect.gen(function* () {
|
|
|
42
68
|
const source = join(args.backupPath, relativePath);
|
|
43
69
|
if (!(yield* fs.exists(source).pipe(Effect.catch(() => Effect.succeed(false))))) continue;
|
|
44
70
|
const target = join(args.stagingPath, relativePath);
|
|
71
|
+
if (preserved.overwrite === false) {
|
|
72
|
+
if (yield* fs.exists(target).pipe(Effect.catch(() => Effect.succeed(false)))) continue;
|
|
73
|
+
}
|
|
45
74
|
yield* fs.makeDirectory(dirname(target), { recursive: true }).pipe(Effect.catch(failStage("preserve-target-paths", args.targetPath, args.stagingPath)));
|
|
46
75
|
yield* fs.copy(source, target, { overwrite: true }).pipe(Effect.catch(failStage("preserve-target-paths", args.targetPath, args.stagingPath)));
|
|
47
76
|
}
|
|
@@ -68,10 +97,37 @@ const preserveTargetPaths = (args) => Effect.gen(function* () {
|
|
|
68
97
|
* targetPath. The helper picks it as a sibling under the same parent
|
|
69
98
|
* (caller passes `parentDir`, we mint the names) — this is enforced
|
|
70
99
|
* by the helper's signature shape.
|
|
100
|
+
*
|
|
101
|
+
* ## Two call shapes
|
|
102
|
+
*
|
|
103
|
+
* - Explicit-paths: callers that need bespoke sibling names (the
|
|
104
|
+
* Seal plugin uses `.backup.` not `.bak.`; pre-existing tests use
|
|
105
|
+
* literal `target.staging`/`target.bak`) pass both `stagingPath`
|
|
106
|
+
* and `backupPath` directly.
|
|
107
|
+
* - `idSuffix`: callers (codegen, snapshot) pass `idSuffix` and the
|
|
108
|
+
* primitive mints `<targetPath>.staging.<idSuffix>` /
|
|
109
|
+
* `<targetPath>.bak.<idSuffix>` so the staging/backup-naming
|
|
110
|
+
* convention is owned in ONE place. Callers MUST NOT pass both
|
|
111
|
+
* `idSuffix` and explicit paths.
|
|
112
|
+
*
|
|
113
|
+
* ## `preserveOnPreseed`
|
|
114
|
+
*
|
|
115
|
+
* When `true`, before the user's `build` runs the primitive copies the
|
|
116
|
+
* current `targetPath` (if any) into `stagingPath` so `build` can
|
|
117
|
+
* mutate that baseline incrementally. This subsumes the codegen
|
|
118
|
+
* orchestrator's pre-seed dance (per-file no-touch idempotency relies
|
|
119
|
+
* on seeing the previous target's mtimes). Cross-cutting with
|
|
120
|
+
* `preserveFromTarget` is allowed: pre-seed clones the whole tree
|
|
121
|
+
* BEFORE the build; `preserveFromTarget` cherry-picks paths AFTER the
|
|
122
|
+
* build and BEFORE the swap (and reads from the backup, so the build
|
|
123
|
+
* may have rewritten them in-tree). The two features answer different
|
|
124
|
+
* questions and the primitive runs them in that order.
|
|
71
125
|
*/
|
|
72
126
|
const stageAndSwap = (args) => Effect.gen(function* () {
|
|
73
127
|
const fs = yield* FileSystem.FileSystem;
|
|
74
|
-
const { targetPath,
|
|
128
|
+
const { targetPath, build, preserveFromTarget = [], preserveOnPreseed = false, publishLockPath } = args;
|
|
129
|
+
const stagingPath = args.stagingPath ?? `${targetPath}.staging.${args.idSuffix}`;
|
|
130
|
+
const backupPath = args.backupPath ?? `${targetPath}.bak.${args.idSuffix}`;
|
|
75
131
|
yield* Effect.annotateCurrentSpan({
|
|
76
132
|
"devstack.stage-and-swap.target": targetPath,
|
|
77
133
|
"devstack.stage-and-swap.staging": stagingPath
|
|
@@ -81,6 +137,15 @@ const stageAndSwap = (args) => Effect.gen(function* () {
|
|
|
81
137
|
force: true
|
|
82
138
|
}).pipe(Effect.ignore);
|
|
83
139
|
yield* fs.makeDirectory(stagingPath, { recursive: true }).pipe(Effect.catch(failStage("mkdir-staging", targetPath, stagingPath)));
|
|
140
|
+
if (preserveOnPreseed) {
|
|
141
|
+
if (yield* fs.exists(targetPath).pipe(Effect.catch(() => Effect.succeed(false)))) yield* fs.copy(targetPath, stagingPath, {
|
|
142
|
+
overwrite: true,
|
|
143
|
+
preserveTimestamps: true
|
|
144
|
+
}).pipe(Effect.catch(failStage("mkdir-staging", targetPath, stagingPath)), Effect.onError(() => fs.remove(stagingPath, {
|
|
145
|
+
recursive: true,
|
|
146
|
+
force: true
|
|
147
|
+
}).pipe(Effect.ignore)));
|
|
148
|
+
}
|
|
84
149
|
const result = yield* build.pipe(Effect.onError(() => fs.remove(stagingPath, {
|
|
85
150
|
recursive: true,
|
|
86
151
|
force: true
|
|
@@ -101,13 +166,16 @@ const stageAndSwap = (args) => Effect.gen(function* () {
|
|
|
101
166
|
})));
|
|
102
167
|
}
|
|
103
168
|
yield* fs.rename(stagingPath, targetPath).pipe(Effect.catch((cause) => {
|
|
104
|
-
if (cause
|
|
169
|
+
if (errnoCode(cause) === "EXDEV") return Effect.gen(function* () {
|
|
105
170
|
yield* Effect.logWarning("stage-and-swap cross-filesystem fallback").pipe(Effect.annotateLogs({
|
|
106
171
|
[SpanAttr.stageAndSwapTargetPath]: targetPath,
|
|
107
172
|
[SpanAttr.stageAndSwapStagingPath]: stagingPath,
|
|
108
173
|
[SpanAttr.errorCode]: "EXDEV"
|
|
109
174
|
}));
|
|
110
|
-
yield* fs.copy(stagingPath, targetPath, { overwrite: false }).pipe(Effect.catch(
|
|
175
|
+
yield* fs.copy(stagingPath, targetPath, { overwrite: false }).pipe(Effect.catch((copyCause) => Effect.gen(function* () {
|
|
176
|
+
if (targetExists) yield* fs.rename(backupPath, targetPath).pipe(Effect.catch(failStage("restore-backup", targetPath, stagingPath)));
|
|
177
|
+
return yield* failStage("cross-filesystem-fallback", targetPath, stagingPath)(copyCause);
|
|
178
|
+
})));
|
|
111
179
|
yield* fs.remove(stagingPath, {
|
|
112
180
|
recursive: true,
|
|
113
181
|
force: true
|
|
@@ -126,6 +194,6 @@ const stageAndSwap = (args) => Effect.gen(function* () {
|
|
|
126
194
|
return result;
|
|
127
195
|
}).pipe(Effect.withSpan("substrate.stage-and-swap"));
|
|
128
196
|
//#endregion
|
|
129
|
-
export { stageAndSwap };
|
|
197
|
+
export { StageAndSwapError, stageAndSwap };
|
|
130
198
|
|
|
131
199
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/substrate/runtime/stage-and-swap/index.ts"],"sourcesContent":["// Atomic stage-and-swap primitive.\n//\n// Architecture §\"Stage-and-swap rollback discipline is precious and\n// well-tested\": one tempdir, one rename for the whole publish.\n// Lifted to substrate so both snapshot AND codegen (per-cycle outer\n// swap) can consume — see ARCHITECTURE.md substrate roster + style\n// guide §19 (Open slot O14 closes with this lift).\n//\n// Contract:\n// 1. The caller provides a `targetPath` (the directory to publish).\n// 2. The orchestrator runs the build effect with a `stagingPath`\n// (a sibling under the same parent so the rename stays on the\n// same filesystem — POSIX rename atomicity is per-filesystem).\n// 3. If the build effect succeeds AND the rename succeeds, the\n// target now points at the new tree; external watchers never\n// observed a half-written intermediate.\n// 4. Optional `preserveFromTarget` paths are copied from the backed-up\n// target into staging immediately before publish.\n// 5. On any failure the staging directory is removed; the previous\n// target (if any) is restored verbatim from a same-parent\n// backup.\n// 6. Cross-filesystem fallback (`EXDEV` from rename) logs and falls\n// back to copy-then-rm — the architecture's documented exception.\n//\n// Discipline carried verbatim from the legacy `engine/snapshot/stage-\n// and-swap.ts` (distilled §17).\n\nimport { dirname, isAbsolute, join, normalize, sep } from 'node:path';\n\nimport { Effect, FileSystem, Schema } from 'effect';\n\nimport { acquireStackLock } from '../cross-process/stack-lock.ts';\nimport { SpanAttr } from '../observability/spans.ts';\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\n/** Tagged failure during a stage-and-swap step. `stage` discriminates\n * the precise failing step; the caller's underlying tag is preserved\n * via `cause` (the primitive does NOT wrap the user's error). */\nexport class StageAndSwapError extends Schema.TaggedErrorClass<StageAndSwapError>()(\n\t'StageAndSwapError',\n\t{\n\t\tstage: Schema.Literals([\n\t\t\t'mkdir-staging',\n\t\t\t'mkdir-backup',\n\t\t\t'build',\n\t\t\t'acquire-publish-lock',\n\t\t\t'backup-current',\n\t\t\t'preserve-target-paths',\n\t\t\t'rename-into-target',\n\t\t\t'restore-backup',\n\t\t\t'cleanup-staging',\n\t\t\t'cross-filesystem-fallback',\n\t\t]),\n\t\ttargetPath: Schema.String,\n\t\tstagingPath: Schema.String,\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n// -----------------------------------------------------------------------------\n// Primitive\n// -----------------------------------------------------------------------------\n\nexport interface StageAndSwapPreservedPath {\n\treadonly relativePath: string;\n\treadonly kind: 'file' | 'directory';\n}\n\nconst failStage =\n\t<S extends StageAndSwapError['stage']>(\n\t\tstage: S,\n\t\ttargetPath: string,\n\t\tstagingPath: string,\n\t): ((cause: unknown) => Effect.Effect<never, StageAndSwapError>) =>\n\t(cause) =>\n\t\tEffect.fail(new StageAndSwapError({ stage, targetPath, stagingPath, cause }));\n\nconst normalizePreservedRelativePath = (relativePath: string): string | null => {\n\tconst normalized = normalize(relativePath);\n\tif (\n\t\trelativePath.length === 0 ||\n\t\tisAbsolute(relativePath) ||\n\t\tnormalized === '.' ||\n\t\tnormalized === '..' ||\n\t\tnormalized.startsWith(`..${sep}`)\n\t) {\n\t\treturn null;\n\t}\n\treturn normalized;\n};\n\nconst preserveTargetPaths = (args: {\n\treadonly backupPath: string;\n\treadonly targetPath: string;\n\treadonly stagingPath: string;\n\treadonly preservedPaths: ReadonlyArray<StageAndSwapPreservedPath>;\n}): Effect.Effect<void, StageAndSwapError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tfor (const preserved of args.preservedPaths) {\n\t\t\tconst relativePath = normalizePreservedRelativePath(preserved.relativePath);\n\t\t\tif (relativePath === null) {\n\t\t\t\treturn yield* failStage(\n\t\t\t\t\t'preserve-target-paths',\n\t\t\t\t\targs.targetPath,\n\t\t\t\t\targs.stagingPath,\n\t\t\t\t)(new Error(`unsafe preserved path: ${preserved.relativePath}`));\n\t\t\t}\n\t\t\tconst source = join(args.backupPath, relativePath);\n\t\t\tconst exists = yield* fs.exists(source).pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\tif (!exists) continue;\n\t\t\tconst target = join(args.stagingPath, relativePath);\n\t\t\tyield* fs\n\t\t\t\t.makeDirectory(dirname(target), { recursive: true })\n\t\t\t\t.pipe(Effect.catch(failStage('preserve-target-paths', args.targetPath, args.stagingPath)));\n\t\t\tyield* fs\n\t\t\t\t.copy(source, target, { overwrite: true })\n\t\t\t\t.pipe(Effect.catch(failStage('preserve-target-paths', args.targetPath, args.stagingPath)));\n\t\t}\n\t});\n\n/**\n * Build-then-swap. `build` is a user effect that populates `stagingPath`;\n * on success the helper:\n *\n * - backs up the current `targetPath` (if any) to `<targetPath>.bak.<pid>`,\n * - copies any selected `preserveFromTarget` paths from that backup\n * into staging,\n * - renames `stagingPath` → `targetPath`,\n * - removes the backup.\n *\n * On any failure the helper removes `stagingPath` and, if the backup\n * was taken, renames it back to `targetPath` so the world looks\n * exactly as it did at entry.\n *\n * The build effect's error type passes through unchanged — the\n * primitive returns `E | StageAndSwapError`. This is the\n * \"preserve the caller's error tag\" rule from distilled §17.\n *\n * IMPORTANT: stagingPath MUST be on the same filesystem as\n * targetPath. The helper picks it as a sibling under the same parent\n * (caller passes `parentDir`, we mint the names) — this is enforced\n * by the helper's signature shape.\n */\nexport const stageAndSwap = <A, E>(args: {\n\treadonly targetPath: string;\n\treadonly stagingPath: string;\n\treadonly backupPath: string;\n\treadonly build: Effect.Effect<A, E, FileSystem.FileSystem>;\n\treadonly preserveFromTarget?: ReadonlyArray<StageAndSwapPreservedPath>;\n\treadonly publishLockPath?: string;\n}): Effect.Effect<A, E | StageAndSwapError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst {\n\t\t\ttargetPath,\n\t\t\tstagingPath,\n\t\t\tbackupPath,\n\t\t\tbuild,\n\t\t\tpreserveFromTarget = [],\n\t\t\tpublishLockPath,\n\t\t} = args;\n\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t'devstack.stage-and-swap.target': targetPath,\n\t\t\t'devstack.stage-and-swap.staging': stagingPath,\n\t\t});\n\n\t\t// 0. Pre-clean the staging slot (a previous crash may have left\n\t\t// a sibling). recursive+force is idempotent.\n\t\tyield* fs.remove(stagingPath, { recursive: true, force: true }).pipe(Effect.ignore);\n\t\tyield* fs\n\t\t\t.makeDirectory(stagingPath, { recursive: true })\n\t\t\t.pipe(Effect.catch(failStage('mkdir-staging', targetPath, stagingPath)));\n\n\t\t// 1. Run the user's build inside the staging directory. On\n\t\t// failure clean up the staging dir (best-effort) BEFORE\n\t\t// re-raising — preserves the caller's error tag (no wrap).\n\t\tconst result = yield* build.pipe(\n\t\t\tEffect.onError(() =>\n\t\t\t\tfs.remove(stagingPath, { recursive: true, force: true }).pipe(Effect.ignore),\n\t\t\t),\n\t\t);\n\n\t\t// 2. Backup the current target and promote staging while holding\n\t\t// an optional caller-supplied lock. Restore uses this to block\n\t\t// command/event writers while the stack root path is absent.\n\t\tconst targetExists = yield* Effect.scoped(\n\t\t\tEffect.gen(function* () {\n\t\t\t\tif (publishLockPath !== undefined) {\n\t\t\t\t\tyield* acquireStackLock(publishLockPath).pipe(\n\t\t\t\t\t\tEffect.catch(failStage('acquire-publish-lock', targetPath, stagingPath)),\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst targetExists = yield* fs\n\t\t\t\t\t.exists(targetPath)\n\t\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\t\tif (targetExists) {\n\t\t\t\t\t// Move current → backup (atomic rename on same filesystem).\n\t\t\t\t\tyield* fs\n\t\t\t\t\t\t.rename(targetPath, backupPath)\n\t\t\t\t\t\t.pipe(Effect.catch(failStage('backup-current', targetPath, stagingPath)));\n\t\t\t\t\tyield* preserveTargetPaths({\n\t\t\t\t\t\tbackupPath,\n\t\t\t\t\t\ttargetPath,\n\t\t\t\t\t\tstagingPath,\n\t\t\t\t\t\tpreservedPaths: preserveFromTarget,\n\t\t\t\t\t}).pipe(\n\t\t\t\t\t\tEffect.catch((error) =>\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tyield* fs\n\t\t\t\t\t\t\t\t\t.rename(backupPath, targetPath)\n\t\t\t\t\t\t\t\t\t.pipe(Effect.catch(failStage('restore-backup', targetPath, stagingPath)));\n\t\t\t\t\t\t\t\treturn yield* Effect.fail(error);\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\n\t\t\t\t// 3. Promote staging → target. This is the atomic publish step.\n\t\t\t\tyield* fs.rename(stagingPath, targetPath).pipe(\n\t\t\t\t\tEffect.catch((cause) => {\n\t\t\t\t\t\tconst code = (cause as NodeJS.ErrnoException).code;\n\t\t\t\t\t\tif (code === 'EXDEV') {\n\t\t\t\t\t\t\t// Cross-filesystem fallback — architecture's documented\n\t\t\t\t\t\t\t// exception. Log loudly; copy then rm. The atomicity\n\t\t\t\t\t\t\t// guarantee is LOST in this branch.\n\t\t\t\t\t\t\treturn Effect.gen(function* () {\n\t\t\t\t\t\t\t\tyield* Effect.logWarning('stage-and-swap cross-filesystem fallback').pipe(\n\t\t\t\t\t\t\t\t\tEffect.annotateLogs({\n\t\t\t\t\t\t\t\t\t\t[SpanAttr.stageAndSwapTargetPath]: targetPath,\n\t\t\t\t\t\t\t\t\t\t[SpanAttr.stageAndSwapStagingPath]: stagingPath,\n\t\t\t\t\t\t\t\t\t\t[SpanAttr.errorCode]: 'EXDEV',\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tyield* fs\n\t\t\t\t\t\t\t\t\t.copy(stagingPath, targetPath, { overwrite: false })\n\t\t\t\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\t\t\t\tEffect.catch(failStage('cross-filesystem-fallback', targetPath, stagingPath)),\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tyield* fs.remove(stagingPath, { recursive: true, force: true }).pipe(Effect.ignore);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Same-filesystem rename failed. Restore backup if we took\n\t\t\t\t\t\t// one; surface the original tag.\n\t\t\t\t\t\treturn Effect.gen(function* () {\n\t\t\t\t\t\t\tif (targetExists) {\n\t\t\t\t\t\t\t\tyield* fs\n\t\t\t\t\t\t\t\t\t.rename(backupPath, targetPath)\n\t\t\t\t\t\t\t\t\t.pipe(Effect.catch(failStage('restore-backup', targetPath, stagingPath)));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn yield* failStage('rename-into-target', targetPath, stagingPath)(cause);\n\t\t\t\t\t\t});\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\treturn targetExists;\n\t\t\t}),\n\t\t);\n\n\t\t// 4. Drop the backup. Failure here is observable but non-fatal:\n\t\t// the new target is live; an orphan backup is cosmetic.\n\t\tif (targetExists) {\n\t\t\tyield* fs.remove(backupPath, { recursive: true, force: true }).pipe(Effect.ignore);\n\t\t}\n\n\t\treturn result;\n\t}).pipe(Effect.withSpan('substrate.stage-and-swap'));\n"],"mappings":";;;;;;;;AAyCA,IAAa,oBAAb,cAAuC,OAAO,kBAAqC,CAClF,qBACA;CACC,OAAO,OAAO,SAAS;EACtB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,CAAC;CACF,YAAY,OAAO;CACnB,aAAa,OAAO;CACpB,OAAO,OAAO,SAAS,OAAO,OAAO;CACrC,CACD,CAAC;AAWF,MAAM,aAEJ,OACA,YACA,iBAEA,UACA,OAAO,KAAK,IAAI,kBAAkB;CAAE;CAAO;CAAY;CAAa;CAAO,CAAC,CAAC;AAE/E,MAAM,kCAAkC,iBAAwC;CAC/E,MAAM,aAAa,UAAU,aAAa;AAC1C,KACC,aAAa,WAAW,KACxB,WAAW,aAAa,IACxB,eAAe,OACf,eAAe,QACf,WAAW,WAAW,KAAK,MAAM,CAEjC,QAAO;AAER,QAAO;;AAGR,MAAM,uBAAuB,SAM5B,OAAO,IAAI,aAAa;CACvB,MAAM,KAAK,OAAO,WAAW;AAC7B,MAAK,MAAM,aAAa,KAAK,gBAAgB;EAC5C,MAAM,eAAe,+BAA+B,UAAU,aAAa;AAC3E,MAAI,iBAAiB,KACpB,QAAO,OAAO,UACb,yBACA,KAAK,YACL,KAAK,YACL,iBAAC,IAAI,MAAM,0BAA0B,UAAU,eAAe,CAAC;EAEjE,MAAM,SAAS,KAAK,KAAK,YAAY,aAAa;AAElD,MAAI,EAAC,OADiB,GAAG,OAAO,OAAO,CAAC,KAAK,OAAO,YAAY,OAAO,QAAQ,MAAM,CAAC,CAAC,EAC1E;EACb,MAAM,SAAS,KAAK,KAAK,aAAa,aAAa;AACnD,SAAO,GACL,cAAc,QAAQ,OAAO,EAAE,EAAE,WAAW,MAAM,CAAC,CACnD,KAAK,OAAO,MAAM,UAAU,yBAAyB,KAAK,YAAY,KAAK,YAAY,CAAC,CAAC;AAC3F,SAAO,GACL,KAAK,QAAQ,QAAQ,EAAE,WAAW,MAAM,CAAC,CACzC,KAAK,OAAO,MAAM,UAAU,yBAAyB,KAAK,YAAY,KAAK,YAAY,CAAC,CAAC;;EAE3F;;;;;;;;;;;;;;;;;;;;;;;;AAyBH,MAAa,gBAAsB,SAQlC,OAAO,IAAI,aAAa;CACvB,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,EACL,YACA,aACA,YACA,OACA,qBAAqB,EAAE,EACvB,oBACG;AACJ,QAAO,OAAO,oBAAoB;EACjC,kCAAkC;EAClC,mCAAmC;EACnC,CAAC;AAIF,QAAO,GAAG,OAAO,aAAa;EAAE,WAAW;EAAM,OAAO;EAAM,CAAC,CAAC,KAAK,OAAO,OAAO;AACnF,QAAO,GACL,cAAc,aAAa,EAAE,WAAW,MAAM,CAAC,CAC/C,KAAK,OAAO,MAAM,UAAU,iBAAiB,YAAY,YAAY,CAAC,CAAC;CAKzE,MAAM,SAAS,OAAO,MAAM,KAC3B,OAAO,cACN,GAAG,OAAO,aAAa;EAAE,WAAW;EAAM,OAAO;EAAM,CAAC,CAAC,KAAK,OAAO,OAAO,CAC5E,CACD;AAiFD,KAAI,OA5EwB,OAAO,OAClC,OAAO,IAAI,aAAa;AACvB,MAAI,oBAAoB,KAAA,EACvB,QAAO,iBAAiB,gBAAgB,CAAC,KACxC,OAAO,MAAM,UAAU,wBAAwB,YAAY,YAAY,CAAC,CACxE;EAGF,MAAM,eAAe,OAAO,GAC1B,OAAO,WAAW,CAClB,KAAK,OAAO,YAAY,OAAO,QAAQ,MAAM,CAAC,CAAC;AACjD,MAAI,cAAc;AAEjB,UAAO,GACL,OAAO,YAAY,WAAW,CAC9B,KAAK,OAAO,MAAM,UAAU,kBAAkB,YAAY,YAAY,CAAC,CAAC;AAC1E,UAAO,oBAAoB;IAC1B;IACA;IACA;IACA,gBAAgB;IAChB,CAAC,CAAC,KACF,OAAO,OAAO,UACb,OAAO,IAAI,aAAa;AACvB,WAAO,GACL,OAAO,YAAY,WAAW,CAC9B,KAAK,OAAO,MAAM,UAAU,kBAAkB,YAAY,YAAY,CAAC,CAAC;AAC1E,WAAO,OAAO,OAAO,KAAK,MAAM;KAC/B,CACF,CACD;;AAIF,SAAO,GAAG,OAAO,aAAa,WAAW,CAAC,KACzC,OAAO,OAAO,UAAU;AAEvB,OADc,MAAgC,SACjC,QAIZ,QAAO,OAAO,IAAI,aAAa;AAC9B,WAAO,OAAO,WAAW,2CAA2C,CAAC,KACpE,OAAO,aAAa;MAClB,SAAS,yBAAyB;MAClC,SAAS,0BAA0B;MACnC,SAAS,YAAY;KACtB,CAAC,CACF;AACD,WAAO,GACL,KAAK,aAAa,YAAY,EAAE,WAAW,OAAO,CAAC,CACnD,KACA,OAAO,MAAM,UAAU,6BAA6B,YAAY,YAAY,CAAC,CAC7E;AACF,WAAO,GAAG,OAAO,aAAa;KAAE,WAAW;KAAM,OAAO;KAAM,CAAC,CAAC,KAAK,OAAO,OAAO;KAClF;AAIH,UAAO,OAAO,IAAI,aAAa;AAC9B,QAAI,aACH,QAAO,GACL,OAAO,YAAY,WAAW,CAC9B,KAAK,OAAO,MAAM,UAAU,kBAAkB,YAAY,YAAY,CAAC,CAAC;AAE3E,WAAO,OAAO,UAAU,sBAAsB,YAAY,YAAY,CAAC,MAAM;KAC5E;IACD,CACF;AAED,SAAO;GACN,CACF,CAKA,QAAO,GAAG,OAAO,YAAY;EAAE,WAAW;EAAM,OAAO;EAAM,CAAC,CAAC,KAAK,OAAO,OAAO;AAGnF,QAAO;EACN,CAAC,KAAK,OAAO,SAAS,2BAA2B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/substrate/runtime/stage-and-swap/index.ts"],"sourcesContent":["// Atomic stage-and-swap primitive.\n//\n// Architecture §\"Stage-and-swap rollback discipline is precious and\n// well-tested\": one tempdir, one rename for the whole publish.\n// Lifted to substrate so both snapshot AND codegen (per-cycle outer\n// swap) can consume — see ARCHITECTURE.md substrate roster + style\n// guide §19 (Open slot O14 closes with this lift).\n//\n// Contract:\n// 1. The caller provides a `targetPath` (the directory to publish).\n// 2. The orchestrator runs the build effect with a `stagingPath`\n// (a sibling under the same parent so the rename stays on the\n// same filesystem — POSIX rename atomicity is per-filesystem).\n// 3. If the build effect succeeds AND the rename succeeds, the\n// target now points at the new tree; external watchers never\n// observed a half-written intermediate.\n// 4. Optional `preserveFromTarget` paths are copied from the backed-up\n// target into staging immediately before publish.\n// 5. On any failure the staging directory is removed; the previous\n// target (if any) is restored verbatim from a same-parent\n// backup.\n// 6. Cross-filesystem fallback (`EXDEV` from rename) logs and falls\n// back to copy-then-rm — the architecture's documented exception.\n//\n// Discipline carried verbatim from the legacy `engine/snapshot/stage-\n// and-swap.ts` (distilled §17).\n\nimport { dirname, isAbsolute, join, normalize, sep } from 'node:path';\n\nimport { Effect, FileSystem, Schema } from 'effect';\n\nimport { acquireStackLock } from '../cross-process/stack-lock.ts';\nimport { SpanAttr } from '../observability/spans.ts';\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\n/** Tagged failure during a stage-and-swap step. `stage` discriminates\n * the precise failing step; the caller's underlying tag is preserved\n * via `cause` (the primitive does NOT wrap the user's error). */\nexport class StageAndSwapError extends Schema.TaggedErrorClass<StageAndSwapError>()(\n\t'StageAndSwapError',\n\t{\n\t\tstage: Schema.Literals([\n\t\t\t'mkdir-staging',\n\t\t\t'mkdir-backup',\n\t\t\t'build',\n\t\t\t'acquire-publish-lock',\n\t\t\t'backup-current',\n\t\t\t'preserve-target-paths',\n\t\t\t'rename-into-target',\n\t\t\t'restore-backup',\n\t\t\t'cleanup-staging',\n\t\t\t'cross-filesystem-fallback',\n\t\t]),\n\t\ttargetPath: Schema.String,\n\t\tstagingPath: Schema.String,\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n// -----------------------------------------------------------------------------\n// Primitive\n// -----------------------------------------------------------------------------\n\nexport interface StageAndSwapPreservedPath {\n\treadonly relativePath: string;\n\treadonly kind: 'file' | 'directory';\n\t/** When `false`, the backed-up live copy is preserved into staging ONLY if\n\t * staging does not already carry the path — so a value the build already\n\t * placed there (e.g. a deploy cache untarred from the snapshot's host-tree)\n\t * WINS over the live copy. For a `directory` entry the skip is\n\t * all-or-nothing, not a per-file merge: if staging already has the dir, the\n\t * ENTIRE live copy is dropped, including any sub-path that existed only live\n\t * (post-snapshot drift) — the intended \"restore-to-snapshot wins\" semantic.\n\t * Defaults to `true` (always overwrite the staging copy with the live one). */\n\treadonly overwrite?: boolean;\n}\n\nconst failStage =\n\t<S extends StageAndSwapError['stage']>(\n\t\tstage: S,\n\t\ttargetPath: string,\n\t\tstagingPath: string,\n\t): ((cause: unknown) => Effect.Effect<never, StageAndSwapError>) =>\n\t(cause) =>\n\t\tEffect.fail(new StageAndSwapError({ stage, targetPath, stagingPath, cause }));\n\n/**\n * Extract the original Node errno `code` from a `FileSystem.rename` failure.\n *\n * Effect v4's `@effect/platform-node` wraps the raw `NodeJS.ErrnoException`\n * (`handleErrnoException` in `platform-node-shared/internal/utils.ts`) into a\n * `PlatformError` whose `reason` is a `SystemError`. The raw errno is preserved\n * as `reason.cause` (the unmodified Node error) — and the `PlatformError`\n * constructor additionally hoists that to the wrapper's own top-level `cause`.\n * So a genuine cross-filesystem rename surfaces the code at `reason.cause.code`\n * (and, equivalently, at `cause.cause.code`), NOT at the top-level `.code`.\n *\n * We probe every plausible location defensively: the nested SystemError cause,\n * the hoisted wrapper cause, and a bare-errno shape (in case a caller injects a\n * raw `NodeJS.ErrnoException` or the wrapping ever changes).\n */\nconst errnoCode = (cause: unknown): string | undefined => {\n\tconst codeOf = (value: unknown): string | undefined => {\n\t\tif (typeof value !== 'object' || value === null) return undefined;\n\t\tconst code = (value as { code?: unknown }).code;\n\t\treturn typeof code === 'string' ? code : undefined;\n\t};\n\tif (typeof cause !== 'object' || cause === null) return undefined;\n\tconst reasonCause = (cause as { reason?: { cause?: unknown } }).reason?.cause;\n\tconst directCause = (cause as { cause?: unknown }).cause;\n\treturn codeOf(reasonCause) ?? codeOf(directCause) ?? codeOf(cause);\n};\n\nconst normalizePreservedRelativePath = (relativePath: string): string | null => {\n\tconst normalized = normalize(relativePath);\n\tif (\n\t\trelativePath.length === 0 ||\n\t\tisAbsolute(relativePath) ||\n\t\tnormalized === '.' ||\n\t\tnormalized === '..' ||\n\t\tnormalized.startsWith(`..${sep}`)\n\t) {\n\t\treturn null;\n\t}\n\treturn normalized;\n};\n\nconst preserveTargetPaths = (args: {\n\treadonly backupPath: string;\n\treadonly targetPath: string;\n\treadonly stagingPath: string;\n\treadonly preservedPaths: ReadonlyArray<StageAndSwapPreservedPath>;\n}): Effect.Effect<void, StageAndSwapError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tfor (const preserved of args.preservedPaths) {\n\t\t\tconst relativePath = normalizePreservedRelativePath(preserved.relativePath);\n\t\t\tif (relativePath === null) {\n\t\t\t\treturn yield* failStage(\n\t\t\t\t\t'preserve-target-paths',\n\t\t\t\t\targs.targetPath,\n\t\t\t\t\targs.stagingPath,\n\t\t\t\t)(new Error(`unsafe preserved path: ${preserved.relativePath}`));\n\t\t\t}\n\t\t\tconst source = join(args.backupPath, relativePath);\n\t\t\tconst exists = yield* fs.exists(source).pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\tif (!exists) continue;\n\t\t\tconst target = join(args.stagingPath, relativePath);\n\t\t\t// `overwrite: false` — don't clobber a path the build already placed in\n\t\t\t// staging (e.g. a deploy cache the snapshot captured into the host-tree\n\t\t\t// tar): that captured copy is consistent with the restored chain, so it\n\t\t\t// WINS over the live copy. The live copy is only a fallback when staging\n\t\t\t// doesn't already carry it (e.g. a pre-capture snapshot).\n\t\t\tif (preserved.overwrite === false) {\n\t\t\t\tconst targetExists = yield* fs\n\t\t\t\t\t.exists(target)\n\t\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\t\tif (targetExists) continue;\n\t\t\t}\n\t\t\tyield* fs\n\t\t\t\t.makeDirectory(dirname(target), { recursive: true })\n\t\t\t\t.pipe(Effect.catch(failStage('preserve-target-paths', args.targetPath, args.stagingPath)));\n\t\t\tyield* fs\n\t\t\t\t.copy(source, target, { overwrite: true })\n\t\t\t\t.pipe(Effect.catch(failStage('preserve-target-paths', args.targetPath, args.stagingPath)));\n\t\t}\n\t});\n\n/**\n * Build-then-swap. `build` is a user effect that populates `stagingPath`;\n * on success the helper:\n *\n * - backs up the current `targetPath` (if any) to `<targetPath>.bak.<pid>`,\n * - copies any selected `preserveFromTarget` paths from that backup\n * into staging,\n * - renames `stagingPath` → `targetPath`,\n * - removes the backup.\n *\n * On any failure the helper removes `stagingPath` and, if the backup\n * was taken, renames it back to `targetPath` so the world looks\n * exactly as it did at entry.\n *\n * The build effect's error type passes through unchanged — the\n * primitive returns `E | StageAndSwapError`. This is the\n * \"preserve the caller's error tag\" rule from distilled §17.\n *\n * IMPORTANT: stagingPath MUST be on the same filesystem as\n * targetPath. The helper picks it as a sibling under the same parent\n * (caller passes `parentDir`, we mint the names) — this is enforced\n * by the helper's signature shape.\n *\n * ## Two call shapes\n *\n * - Explicit-paths: callers that need bespoke sibling names (the\n * Seal plugin uses `.backup.` not `.bak.`; pre-existing tests use\n * literal `target.staging`/`target.bak`) pass both `stagingPath`\n * and `backupPath` directly.\n * - `idSuffix`: callers (codegen, snapshot) pass `idSuffix` and the\n * primitive mints `<targetPath>.staging.<idSuffix>` /\n * `<targetPath>.bak.<idSuffix>` so the staging/backup-naming\n * convention is owned in ONE place. Callers MUST NOT pass both\n * `idSuffix` and explicit paths.\n *\n * ## `preserveOnPreseed`\n *\n * When `true`, before the user's `build` runs the primitive copies the\n * current `targetPath` (if any) into `stagingPath` so `build` can\n * mutate that baseline incrementally. This subsumes the codegen\n * orchestrator's pre-seed dance (per-file no-touch idempotency relies\n * on seeing the previous target's mtimes). Cross-cutting with\n * `preserveFromTarget` is allowed: pre-seed clones the whole tree\n * BEFORE the build; `preserveFromTarget` cherry-picks paths AFTER the\n * build and BEFORE the swap (and reads from the backup, so the build\n * may have rewritten them in-tree). The two features answer different\n * questions and the primitive runs them in that order.\n */\nexport const stageAndSwap = <A, E>(\n\targs: {\n\t\treadonly targetPath: string;\n\t\treadonly build: Effect.Effect<A, E, FileSystem.FileSystem>;\n\t\treadonly preserveFromTarget?: ReadonlyArray<StageAndSwapPreservedPath>;\n\t\treadonly preserveOnPreseed?: boolean;\n\t\treadonly publishLockPath?: string;\n\t} & (\n\t\t| { readonly stagingPath: string; readonly backupPath: string; readonly idSuffix?: never }\n\t\t| { readonly idSuffix: string; readonly stagingPath?: never; readonly backupPath?: never }\n\t),\n): Effect.Effect<A, E | StageAndSwapError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst {\n\t\t\ttargetPath,\n\t\t\tbuild,\n\t\t\tpreserveFromTarget = [],\n\t\t\tpreserveOnPreseed = false,\n\t\t\tpublishLockPath,\n\t\t} = args;\n\t\tconst stagingPath =\n\t\t\targs.stagingPath ?? `${targetPath}.staging.${(args as { idSuffix: string }).idSuffix}`;\n\t\tconst backupPath =\n\t\t\targs.backupPath ?? `${targetPath}.bak.${(args as { idSuffix: string }).idSuffix}`;\n\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t'devstack.stage-and-swap.target': targetPath,\n\t\t\t'devstack.stage-and-swap.staging': stagingPath,\n\t\t});\n\n\t\t// 0. Pre-clean the staging slot (a previous crash may have left\n\t\t// a sibling). recursive+force is idempotent.\n\t\tyield* fs.remove(stagingPath, { recursive: true, force: true }).pipe(Effect.ignore);\n\t\tyield* fs\n\t\t\t.makeDirectory(stagingPath, { recursive: true })\n\t\t\t.pipe(Effect.catch(failStage('mkdir-staging', targetPath, stagingPath)));\n\n\t\t// 0a. Optional pre-seed: when `preserveOnPreseed`, clone the\n\t\t// current target into staging so the build can edit it\n\t\t// incrementally (codegen needs this for per-file no-touch\n\t\t// idempotency). `preserveTimestamps: true` keeps mtimes\n\t\t// intact so HMR watchers (Vite/Turbopack — they trigger on\n\t\t// mtime, not content) stay quiet for unchanged outputs.\n\t\tif (preserveOnPreseed) {\n\t\t\tconst targetExistsForPreseed = yield* fs\n\t\t\t\t.exists(targetPath)\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\tif (targetExistsForPreseed) {\n\t\t\t\tyield* fs.copy(targetPath, stagingPath, { overwrite: true, preserveTimestamps: true }).pipe(\n\t\t\t\t\tEffect.catch(failStage('mkdir-staging', targetPath, stagingPath)),\n\t\t\t\t\tEffect.onError(() =>\n\t\t\t\t\t\tfs.remove(stagingPath, { recursive: true, force: true }).pipe(Effect.ignore),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// 1. Run the user's build inside the staging directory. On\n\t\t// failure clean up the staging dir (best-effort) BEFORE\n\t\t// re-raising — preserves the caller's error tag (no wrap).\n\t\tconst result = yield* build.pipe(\n\t\t\tEffect.onError(() =>\n\t\t\t\tfs.remove(stagingPath, { recursive: true, force: true }).pipe(Effect.ignore),\n\t\t\t),\n\t\t);\n\n\t\t// 2. Backup the current target and promote staging while holding\n\t\t// an optional caller-supplied lock. Restore uses this to block\n\t\t// command/event writers while the stack root path is absent.\n\t\tconst targetExists = yield* Effect.scoped(\n\t\t\tEffect.gen(function* () {\n\t\t\t\tif (publishLockPath !== undefined) {\n\t\t\t\t\tyield* acquireStackLock(publishLockPath).pipe(\n\t\t\t\t\t\tEffect.catch(failStage('acquire-publish-lock', targetPath, stagingPath)),\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst targetExists = yield* fs\n\t\t\t\t\t.exists(targetPath)\n\t\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\t\tif (targetExists) {\n\t\t\t\t\t// Move current → backup (atomic rename on same filesystem).\n\t\t\t\t\tyield* fs\n\t\t\t\t\t\t.rename(targetPath, backupPath)\n\t\t\t\t\t\t.pipe(Effect.catch(failStage('backup-current', targetPath, stagingPath)));\n\t\t\t\t\tyield* preserveTargetPaths({\n\t\t\t\t\t\tbackupPath,\n\t\t\t\t\t\ttargetPath,\n\t\t\t\t\t\tstagingPath,\n\t\t\t\t\t\tpreservedPaths: preserveFromTarget,\n\t\t\t\t\t}).pipe(\n\t\t\t\t\t\tEffect.catch((error) =>\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tyield* fs\n\t\t\t\t\t\t\t\t\t.rename(backupPath, targetPath)\n\t\t\t\t\t\t\t\t\t.pipe(Effect.catch(failStage('restore-backup', targetPath, stagingPath)));\n\t\t\t\t\t\t\t\treturn yield* Effect.fail(error);\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\n\t\t\t\t// 3. Promote staging → target. This is the atomic publish step.\n\t\t\t\tyield* fs.rename(stagingPath, targetPath).pipe(\n\t\t\t\t\tEffect.catch((cause) => {\n\t\t\t\t\t\t// A real Effect v4 `rename` failure is a PlatformError whose\n\t\t\t\t\t\t// raw Node errno lives at `reason.cause.code` (and the hoisted\n\t\t\t\t\t\t// `cause.cause.code`), NOT the top-level `.code` — see errnoCode.\n\t\t\t\t\t\tif (errnoCode(cause) === 'EXDEV') {\n\t\t\t\t\t\t\t// Cross-filesystem fallback — architecture's documented\n\t\t\t\t\t\t\t// exception. Log loudly; copy then rm. The atomicity\n\t\t\t\t\t\t\t// guarantee is LOST in this branch.\n\t\t\t\t\t\t\treturn Effect.gen(function* () {\n\t\t\t\t\t\t\t\tyield* Effect.logWarning('stage-and-swap cross-filesystem fallback').pipe(\n\t\t\t\t\t\t\t\t\tEffect.annotateLogs({\n\t\t\t\t\t\t\t\t\t\t[SpanAttr.stageAndSwapTargetPath]: targetPath,\n\t\t\t\t\t\t\t\t\t\t[SpanAttr.stageAndSwapStagingPath]: stagingPath,\n\t\t\t\t\t\t\t\t\t\t[SpanAttr.errorCode]: 'EXDEV',\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tyield* fs.copy(stagingPath, targetPath, { overwrite: false }).pipe(\n\t\t\t\t\t\t\t\t\tEffect.catch((copyCause) =>\n\t\t\t\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\t\t\t\t// Copy failed mid-fallback. Mirror the same-FS branch:\n\t\t\t\t\t\t\t\t\t\t\t// if we backed a target up, restore it verbatim before\n\t\t\t\t\t\t\t\t\t\t\t// surfacing the original tag (contract item 5).\n\t\t\t\t\t\t\t\t\t\t\tif (targetExists) {\n\t\t\t\t\t\t\t\t\t\t\t\tyield* fs\n\t\t\t\t\t\t\t\t\t\t\t\t\t.rename(backupPath, targetPath)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.pipe(Effect.catch(failStage('restore-backup', targetPath, stagingPath)));\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\treturn yield* failStage(\n\t\t\t\t\t\t\t\t\t\t\t\t'cross-filesystem-fallback',\n\t\t\t\t\t\t\t\t\t\t\t\ttargetPath,\n\t\t\t\t\t\t\t\t\t\t\t\tstagingPath,\n\t\t\t\t\t\t\t\t\t\t\t)(copyCause);\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tyield* fs.remove(stagingPath, { recursive: true, force: true }).pipe(Effect.ignore);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Same-filesystem rename failed. Restore backup if we took\n\t\t\t\t\t\t// one; surface the original tag.\n\t\t\t\t\t\treturn Effect.gen(function* () {\n\t\t\t\t\t\t\tif (targetExists) {\n\t\t\t\t\t\t\t\tyield* fs\n\t\t\t\t\t\t\t\t\t.rename(backupPath, targetPath)\n\t\t\t\t\t\t\t\t\t.pipe(Effect.catch(failStage('restore-backup', targetPath, stagingPath)));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn yield* failStage('rename-into-target', targetPath, stagingPath)(cause);\n\t\t\t\t\t\t});\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\treturn targetExists;\n\t\t\t}),\n\t\t);\n\n\t\t// 4. Drop the backup. Failure here is observable but non-fatal:\n\t\t// the new target is live; an orphan backup is cosmetic.\n\t\tif (targetExists) {\n\t\t\tyield* fs.remove(backupPath, { recursive: true, force: true }).pipe(Effect.ignore);\n\t\t}\n\n\t\treturn result;\n\t}).pipe(Effect.withSpan('substrate.stage-and-swap'));\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { versionedDocSchema } from "../../versioned-doc-schema.mjs";
|
|
1
2
|
import { Schema } from "effect";
|
|
2
3
|
//#region src/substrate/runtime/state-store/schema.ts
|
|
3
4
|
/** Stored value envelope. The phantom-typed `StateKey<V>` reads /
|
|
@@ -17,20 +18,24 @@ const StateEntry = Schema.Struct({
|
|
|
17
18
|
updatedAt: Schema.Number
|
|
18
19
|
});
|
|
19
20
|
/** Document — one file per stack. Keys are the `StateKey<V>` string
|
|
20
|
-
* form (`<pluginKey>/<suffix>`), values are entries.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
* form (`<pluginKey>/<suffix>`), values are entries.
|
|
22
|
+
*
|
|
23
|
+
* Schema version: bump on incompatible changes. v1 is the initial
|
|
24
|
+
* release; bumps are the only way to introduce backward-incompatible
|
|
25
|
+
* shape changes. Schema-decode failure on read with an unknown
|
|
26
|
+
* version surfaces as `corruption`. */
|
|
27
|
+
const StateDocument = versionedDocSchema(1, {
|
|
28
|
+
/** Per-plugin namespaces. Key is the plugin-key prefix; nested
|
|
29
|
+
* record is the plugin's own keys (suffix → entry). Two-level
|
|
30
|
+
* structure (vs. flat `pluginKey/suffix → entry`) so `listUnder`
|
|
31
|
+
* is an O(1) lookup and per-plugin wipes are O(1) field deletes. */
|
|
32
|
+
plugins: Schema.Record(Schema.String, Schema.Record(Schema.String, StateEntry)) });
|
|
33
|
+
/** Initial empty document. */
|
|
34
|
+
const emptyDocument = {
|
|
35
|
+
version: 1,
|
|
36
|
+
plugins: {}
|
|
37
|
+
};
|
|
33
38
|
//#endregion
|
|
34
|
-
export { StateDocument };
|
|
39
|
+
export { StateDocument, StateEntry, emptyDocument };
|
|
35
40
|
|
|
36
41
|
//# sourceMappingURL=schema.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.mjs","names":[],"sources":["../../../../src/substrate/runtime/state-store/schema.ts"],"sourcesContent":["// On-disk schema for the per-stack state.json.\n//\n// One file per stack. Plugin-keyed top-level subtrees so two\n// plugins never see each other's keys. Tombstones distinguish\n// \"deleted\" from \"missing\" — see header of `state-store.ts`.\n\nimport { Schema } from 'effect';\n\n/** Stored value envelope. The phantom-typed `StateKey<V>` reads /\n * writes against the `value` slot opaquely (the substrate doesn't\n * introspect plugin values — it stores the JSON value as `unknown`\n * and re-encodes the document on every set). */\nexport const StateEntry = Schema.Struct({\n\t/** Discriminator. `present` carries a value; `tombstone` records\n\t * that the key was deleted (so reads can distinguish \"user\n\t * removed this\" from \"user never wrote it\"). */\n\tstate: Schema.Literals(['present', 'tombstone']),\n\t/** Encoded value. For `tombstone` entries this is `null`; the\n\t * substrate refuses to deliver it on `get`. */\n\tvalue: Schema.Unknown,\n\t/** Timestamp of the last mutation. Renderers may surface it;\n\t * the substrate uses it only for diagnostics. */\n\tupdatedAt: Schema.Number,\n});\nexport type StateEntry = typeof StateEntry.Type;\n\n/** Document — one file per stack. Keys are the `StateKey<V>` string\n * form (`<pluginKey>/<suffix>`), values are entries
|
|
1
|
+
{"version":3,"file":"schema.mjs","names":[],"sources":["../../../../src/substrate/runtime/state-store/schema.ts"],"sourcesContent":["// On-disk schema for the per-stack state.json.\n//\n// One file per stack. Plugin-keyed top-level subtrees so two\n// plugins never see each other's keys. Tombstones distinguish\n// \"deleted\" from \"missing\" — see header of `state-store.ts`.\n\nimport { Schema } from 'effect';\n\nimport { versionedDocSchema } from '../../versioned-doc-schema.ts';\n\n/** Stored value envelope. The phantom-typed `StateKey<V>` reads /\n * writes against the `value` slot opaquely (the substrate doesn't\n * introspect plugin values — it stores the JSON value as `unknown`\n * and re-encodes the document on every set). */\nexport const StateEntry = Schema.Struct({\n\t/** Discriminator. `present` carries a value; `tombstone` records\n\t * that the key was deleted (so reads can distinguish \"user\n\t * removed this\" from \"user never wrote it\"). */\n\tstate: Schema.Literals(['present', 'tombstone']),\n\t/** Encoded value. For `tombstone` entries this is `null`; the\n\t * substrate refuses to deliver it on `get`. */\n\tvalue: Schema.Unknown,\n\t/** Timestamp of the last mutation. Renderers may surface it;\n\t * the substrate uses it only for diagnostics. */\n\tupdatedAt: Schema.Number,\n});\nexport type StateEntry = typeof StateEntry.Type;\n\n/** Document — one file per stack. Keys are the `StateKey<V>` string\n * form (`<pluginKey>/<suffix>`), values are entries.\n *\n * Schema version: bump on incompatible changes. v1 is the initial\n * release; bumps are the only way to introduce backward-incompatible\n * shape changes. Schema-decode failure on read with an unknown\n * version surfaces as `corruption`. */\nexport const StateDocument = versionedDocSchema(1, {\n\t/** Per-plugin namespaces. Key is the plugin-key prefix; nested\n\t * record is the plugin's own keys (suffix → entry). Two-level\n\t * structure (vs. flat `pluginKey/suffix → entry`) so `listUnder`\n\t * is an O(1) lookup and per-plugin wipes are O(1) field deletes. */\n\tplugins: Schema.Record(Schema.String, Schema.Record(Schema.String, StateEntry)),\n});\nexport type StateDocument = typeof StateDocument.Type;\n\n/** Initial empty document. */\nexport const emptyDocument: StateDocument = { version: 1, plugins: {} };\n"],"mappings":";;;;;;;AAcA,MAAa,aAAa,OAAO,OAAO;;;;CAIvC,OAAO,OAAO,SAAS,CAAC,WAAW,YAAY,CAAC;;;CAGhD,OAAO,OAAO;;;CAGd,WAAW,OAAO;CAClB,CAAC;;;;;;;;AAUF,MAAa,gBAAgB,mBAAmB,GAAG;;;;;AAKlD,SAAS,OAAO,OAAO,OAAO,QAAQ,OAAO,OAAO,OAAO,QAAQ,WAAW,CAAC,EAC/E,CAAC;;AAIF,MAAa,gBAA+B;CAAE,SAAS;CAAG,SAAS,EAAE;CAAE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { Context, FileSystem, Layer } from "effect";
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { StackPathsService } from "../paths.mjs";
|
|
2
|
+
import { StateStoreError } from "../errors.mjs";
|
|
3
|
+
import { atomicWriteJson } from "../atomic-write.mjs";
|
|
4
|
+
import { decodeJsonText } from "../runtime-decode.mjs";
|
|
5
|
+
import { StateDocument, emptyDocument } from "./schema.mjs";
|
|
6
|
+
import { CrossProcessLock } from "../cross-process/lock.mjs";
|
|
7
|
+
import { Context, Effect, FileSystem, Layer, Ref } from "effect";
|
|
8
|
+
//#region src/substrate/runtime/state-store/service.ts
|
|
9
|
+
/**
|
|
10
|
+
* The substrate-internal state-store service. The
|
|
11
|
+
* `StateStore` interface re-exported from `substrate/state-store.ts`
|
|
12
|
+
* is the user-facing shape; this is the wired-up implementation.
|
|
13
|
+
*/
|
|
14
|
+
var StateStoreService = class extends Context.Service()("@devstack/substrate/StateStore") {};
|
|
15
|
+
/**
|
|
16
|
+
* Parse `StateKey<V>` (`<pluginKey>/<suffix>`) into its two parts.
|
|
17
|
+
* The key invariant from `substrate/state-store.ts`'s
|
|
18
|
+
* `defineStateKey` is the single `/` separator; we cope with
|
|
19
|
+
* suffix-contains-slash by splitting on the FIRST `/`.
|
|
20
|
+
*/
|
|
21
|
+
const splitKey = (key) => {
|
|
22
|
+
const i = key.indexOf("/");
|
|
23
|
+
if (i < 0) return {
|
|
24
|
+
plugin: "__malformed__",
|
|
25
|
+
suffix: key
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
plugin: key.slice(0, i),
|
|
29
|
+
suffix: key.slice(i + 1)
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
/** Read+decode the document. Missing file → empty document. */
|
|
33
|
+
const readDocument = Effect.gen(function* () {
|
|
34
|
+
const fs = yield* FileSystem.FileSystem;
|
|
35
|
+
const paths = yield* StackPathsService;
|
|
36
|
+
if (!(yield* fs.exists(paths.stateFile).pipe(Effect.catch((cause) => Effect.fail(new StateStoreError({
|
|
37
|
+
reason: "io-failed",
|
|
38
|
+
detail: `failed to stat state file: ${paths.stateFile}`,
|
|
39
|
+
cause
|
|
40
|
+
})))))) return emptyDocument;
|
|
41
|
+
return yield* decodeJsonText(StateDocument, yield* fs.readFileString(paths.stateFile).pipe(Effect.catch((cause) => Effect.fail(new StateStoreError({
|
|
42
|
+
reason: "io-failed",
|
|
43
|
+
detail: `failed to read state file: ${paths.stateFile}`,
|
|
44
|
+
cause
|
|
45
|
+
})))), {
|
|
46
|
+
source: paths.stateFile,
|
|
47
|
+
mkError: (issue) => new StateStoreError({
|
|
48
|
+
reason: "corruption",
|
|
49
|
+
detail: issue.message === "failed to parse JSON" ? `state file is not valid JSON: ${paths.stateFile}` : `state file failed schema decode: ${paths.stateFile}`,
|
|
50
|
+
cause: issue.cause
|
|
51
|
+
})
|
|
52
|
+
});
|
|
53
|
+
}).pipe(Effect.withSpan("substrate.stateStore.read"));
|
|
54
|
+
/** Write+encode the document atomically. */
|
|
55
|
+
const writeDocument = (doc) => Effect.gen(function* () {
|
|
56
|
+
const paths = yield* StackPathsService;
|
|
57
|
+
yield* atomicWriteJson(paths.stateFile, StateDocument, doc).pipe(Effect.catch((cause) => Effect.fail(new StateStoreError({
|
|
58
|
+
reason: "io-failed",
|
|
59
|
+
detail: `atomic write failed: ${paths.stateFile}`,
|
|
60
|
+
cause
|
|
61
|
+
}))));
|
|
62
|
+
}).pipe(Effect.withSpan("substrate.stateStore.write"));
|
|
63
|
+
Layer.effect(StateStoreService, Effect.gen(function* () {
|
|
64
|
+
const lock = yield* CrossProcessLock;
|
|
65
|
+
const fs = yield* FileSystem.FileSystem;
|
|
66
|
+
const paths = yield* StackPathsService;
|
|
67
|
+
const provideEnv = (effect) => effect.pipe(Effect.provideService(FileSystem.FileSystem, fs), Effect.provideService(StackPathsService, paths));
|
|
68
|
+
const initial = yield* provideEnv(readDocument);
|
|
69
|
+
const cache = yield* Ref.make(initial);
|
|
70
|
+
const get = (key) => Effect.gen(function* () {
|
|
71
|
+
const doc = yield* Ref.get(cache);
|
|
72
|
+
const { plugin, suffix } = splitKey(key);
|
|
73
|
+
const entry = doc.plugins[plugin]?.[suffix];
|
|
74
|
+
if (!entry) return null;
|
|
75
|
+
if (entry.state === "tombstone") return null;
|
|
76
|
+
return entry.value;
|
|
77
|
+
});
|
|
78
|
+
const mapLockErrors = (eff) => eff.pipe(Effect.catchTags({
|
|
79
|
+
StackLockTimeoutError: (e) => Effect.fail(new StateStoreError({
|
|
80
|
+
reason: "lock-contention",
|
|
81
|
+
detail: `state-store write blocked: peer holds ${e.path} (waited ${e.waitedMillis}ms)`,
|
|
82
|
+
cause: e
|
|
83
|
+
})),
|
|
84
|
+
StackLockIoError: (e) => Effect.fail(new StateStoreError({
|
|
85
|
+
reason: "io-failed",
|
|
86
|
+
detail: `state-store lock IO error on ${e.path}`,
|
|
87
|
+
cause: e.cause
|
|
88
|
+
}))
|
|
89
|
+
}));
|
|
90
|
+
const set = (key, value) => mapLockErrors(lock.withLock(provideEnv(Effect.uninterruptible(Effect.gen(function* () {
|
|
91
|
+
const fresh = yield* readDocument;
|
|
92
|
+
const { plugin, suffix } = splitKey(key);
|
|
93
|
+
const next = {
|
|
94
|
+
version: 1,
|
|
95
|
+
plugins: {
|
|
96
|
+
...fresh.plugins,
|
|
97
|
+
[plugin]: {
|
|
98
|
+
...fresh.plugins[plugin],
|
|
99
|
+
[suffix]: {
|
|
100
|
+
state: "present",
|
|
101
|
+
value,
|
|
102
|
+
updatedAt: Date.now()
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
yield* writeDocument(next);
|
|
108
|
+
yield* Ref.set(cache, next);
|
|
109
|
+
})))));
|
|
110
|
+
const del = (key) => mapLockErrors(lock.withLock(provideEnv(Effect.uninterruptible(Effect.gen(function* () {
|
|
111
|
+
const fresh = yield* readDocument;
|
|
112
|
+
const { plugin, suffix } = splitKey(key);
|
|
113
|
+
const next = {
|
|
114
|
+
version: 1,
|
|
115
|
+
plugins: {
|
|
116
|
+
...fresh.plugins,
|
|
117
|
+
[plugin]: {
|
|
118
|
+
...fresh.plugins[plugin],
|
|
119
|
+
[suffix]: {
|
|
120
|
+
state: "tombstone",
|
|
121
|
+
value: null,
|
|
122
|
+
updatedAt: Date.now()
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
yield* writeDocument(next);
|
|
128
|
+
yield* Ref.set(cache, next);
|
|
129
|
+
})))));
|
|
130
|
+
const listUnder = (prefix) => Effect.gen(function* () {
|
|
131
|
+
const ns = (yield* Ref.get(cache)).plugins[prefix];
|
|
132
|
+
if (!ns) return [];
|
|
133
|
+
return Object.entries(ns).filter(([, entry]) => entry.state === "present").map(([suffix]) => `${prefix}/${suffix}`);
|
|
134
|
+
});
|
|
135
|
+
return StateStoreService.of({
|
|
136
|
+
get,
|
|
137
|
+
set,
|
|
138
|
+
delete: del,
|
|
139
|
+
listUnder
|
|
140
|
+
});
|
|
141
|
+
}));
|
|
142
|
+
//#endregion
|
|
143
|
+
export { StateStoreService };
|
|
144
|
+
|
|
145
|
+
//# sourceMappingURL=service.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.mjs","names":[],"sources":["../../../../src/substrate/runtime/state-store/service.ts"],"sourcesContent":["// State-store implementation.\n//\n// Per-stack JSON file at `<runtime-root>/stacks/<stack>/state.json`.\n// Plugin-keyed namespacing; tombstone-vs-missing distinction;\n// atomic-write on every mutation; cross-process safety via the\n// injected lock.\n//\n// Tombstones:\n// - `get` on a tombstoned key returns `null` (same as missing) —\n// the type-level contract is \"absent\". The on-disk distinction\n// is preserved so snapshot/restore round-trips correctly\n// classify a deletion (tombstone) vs a never-written key\n// (missing). Inspectors can `peek` to see the discriminator;\n// normal plugin code never needs it.\n// - `delete` writes a tombstone (rather than deleting the field).\n// Two reasons:\n// (a) Snapshot equality is now precise — \"removed since\n// snapshot\" survives the round-trip.\n// (b) An interrupted writer never silently resurrects a\n// previously-deleted key (the present-record stays\n// tombstone-shaped until set() runs again).\n// - `wipe()` (full plugin namespace removal) IS a hard remove;\n// tombstones are field-grain, namespace-grain wipe is route-\n// grain.\n\nimport { Context, Effect, FileSystem, Layer, Ref } from 'effect';\n\nimport type { PluginKey } from '../../brand.ts';\nimport type { StateKey, StateStore } from '../../state-store.ts';\nimport { atomicWriteJson } from '../atomic-write.ts';\nimport { CrossProcessLock } from '../cross-process/lock.ts';\nimport type { StackLockIoError, StackLockTimeoutError } from '../cross-process/stack-lock.ts';\nimport { StateStoreError } from '../errors.ts';\nimport { StackPathsService } from '../paths.ts';\nimport { decodeJsonText } from '../runtime-decode.ts';\nimport { emptyDocument, StateDocument, type StateEntry } from './schema.ts';\n\n/**\n * The substrate-internal state-store service. The\n * `StateStore` interface re-exported from `substrate/state-store.ts`\n * is the user-facing shape; this is the wired-up implementation.\n */\nexport class StateStoreService extends Context.Service<StateStoreService, StateStore>()(\n\t'@devstack/substrate/StateStore',\n) {}\n\n/**\n * Parse `StateKey<V>` (`<pluginKey>/<suffix>`) into its two parts.\n * The key invariant from `substrate/state-store.ts`'s\n * `defineStateKey` is the single `/` separator; we cope with\n * suffix-contains-slash by splitting on the FIRST `/`.\n */\nconst splitKey = (key: string): { readonly plugin: string; readonly suffix: string } => {\n\tconst i = key.indexOf('/');\n\t// A key without `/` is programmer error (`defineStateKey`\n\t// always produces one). Fall back gracefully so we don't\n\t// hard-crash on a misuse — store the whole string under a\n\t// `__malformed__` namespace, surfaced on listUnder for the\n\t// authoring plugin to notice in dev.\n\tif (i < 0) return { plugin: '__malformed__', suffix: key };\n\treturn { plugin: key.slice(0, i), suffix: key.slice(i + 1) };\n};\n\n/** Read+decode the document. Missing file → empty document. */\nconst readDocument: Effect.Effect<\n\tStateDocument,\n\tStateStoreError,\n\tFileSystem.FileSystem | StackPathsService\n> = Effect.gen(function* () {\n\tconst fs = yield* FileSystem.FileSystem;\n\tconst paths = yield* StackPathsService;\n\tconst exists = yield* fs.exists(paths.stateFile).pipe(\n\t\tEffect.catch((cause) =>\n\t\t\tEffect.fail(\n\t\t\t\tnew StateStoreError({\n\t\t\t\t\treason: 'io-failed',\n\t\t\t\t\tdetail: `failed to stat state file: ${paths.stateFile}`,\n\t\t\t\t\tcause,\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t);\n\tif (!exists) return emptyDocument;\n\tconst text = yield* fs.readFileString(paths.stateFile).pipe(\n\t\tEffect.catch((cause) =>\n\t\t\tEffect.fail(\n\t\t\t\tnew StateStoreError({\n\t\t\t\t\treason: 'io-failed',\n\t\t\t\t\tdetail: `failed to read state file: ${paths.stateFile}`,\n\t\t\t\t\tcause,\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t);\n\treturn yield* decodeJsonText(StateDocument, text, {\n\t\tsource: paths.stateFile,\n\t\tmkError: (issue) =>\n\t\t\tnew StateStoreError({\n\t\t\t\treason: 'corruption',\n\t\t\t\tdetail:\n\t\t\t\t\tissue.message === 'failed to parse JSON'\n\t\t\t\t\t\t? `state file is not valid JSON: ${paths.stateFile}`\n\t\t\t\t\t\t: `state file failed schema decode: ${paths.stateFile}`,\n\t\t\t\tcause: issue.cause,\n\t\t\t}),\n\t});\n}).pipe(Effect.withSpan('substrate.stateStore.read'));\n\n/** Write+encode the document atomically. */\nconst writeDocument = (\n\tdoc: StateDocument,\n): Effect.Effect<void, StateStoreError, FileSystem.FileSystem | StackPathsService> =>\n\tEffect.gen(function* () {\n\t\tconst paths = yield* StackPathsService;\n\t\tyield* atomicWriteJson(paths.stateFile, StateDocument, doc).pipe(\n\t\t\tEffect.catch((cause) =>\n\t\t\t\tEffect.fail(\n\t\t\t\t\tnew StateStoreError({\n\t\t\t\t\t\treason: 'io-failed',\n\t\t\t\t\t\tdetail: `atomic write failed: ${paths.stateFile}`,\n\t\t\t\t\t\tcause,\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t}).pipe(Effect.withSpan('substrate.stateStore.write'));\n\n/**\n * The state-store Layer. Caches the document in a process-local\n * `Ref` so reads after the first don't touch disk; every mutation\n * runs under the cross-process lock so foreign processes don't\n * trample our changes mid-flight.\n *\n * Cache semantics: the in-process `Ref` is the truth for in-process\n * readers; it is invalidated by re-reading inside the locked\n * mutation. This deliberately does NOT poll the file — if another\n * process writes between our reads, we'll observe the change on the\n * next `withLock` r-m-w (re-read inside the critical section).\n * Plugins that need \"see foreign writes\" must invoke `refresh`\n * (exposed for the supervisor / watch dispatcher).\n */\nexport const layerStateStore: Layer.Layer<\n\tStateStoreService,\n\tStateStoreError,\n\tFileSystem.FileSystem | StackPathsService | CrossProcessLock\n> = Layer.effect(\n\tStateStoreService,\n\tEffect.gen(function* () {\n\t\tconst lock = yield* CrossProcessLock;\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst paths = yield* StackPathsService;\n\t\t// `readDocument` / `writeDocument` declare `FileSystem |\n\t\t// StackPathsService` requirements; we discharge both from the\n\t\t// Layer-level closure so the per-method Effects are `R = never`,\n\t\t// matching the `StateStore` contract.\n\t\tconst provideEnv = <A, E>(\n\t\t\teffect: Effect.Effect<A, E, FileSystem.FileSystem | StackPathsService>,\n\t\t): Effect.Effect<A, E> =>\n\t\t\teffect.pipe(\n\t\t\t\tEffect.provideService(FileSystem.FileSystem, fs),\n\t\t\t\tEffect.provideService(StackPathsService, paths),\n\t\t\t);\n\t\t// Prime the cache with the on-disk document. If decoding\n\t\t// fails at boot, we fail the Layer — the supervisor must\n\t\t// surface the corruption to the user (not silently re-init).\n\t\tconst initial = yield* provideEnv(readDocument);\n\t\tconst cache = yield* Ref.make(initial);\n\n\t\tconst get: StateStore['get'] = <V>(key: StateKey<V>) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst doc = yield* Ref.get(cache);\n\t\t\t\tconst { plugin, suffix } = splitKey(key);\n\t\t\t\tconst entry: StateEntry | undefined = doc.plugins[plugin]?.[suffix];\n\t\t\t\tif (!entry) return null;\n\t\t\t\tif (entry.state === 'tombstone') return null;\n\t\t\t\treturn entry.value as V;\n\t\t\t});\n\n\t\t// Map the lock's typed acquire failures onto our `lock-contention`\n\t\t// reason on `StateStoreError`. The `StateStore` contract already\n\t\t// advertises that mutation methods surface this reason — caller\n\t\t// recovery is the same shape as any other write failure (retry on\n\t\t// the next user action; surface in the supervisor cascade).\n\t\tconst mapLockErrors = <A, R>(\n\t\t\teff: Effect.Effect<A, StateStoreError | StackLockTimeoutError | StackLockIoError, R>,\n\t\t): Effect.Effect<A, StateStoreError, R> =>\n\t\t\teff.pipe(\n\t\t\t\tEffect.catchTags({\n\t\t\t\t\tStackLockTimeoutError: (e) =>\n\t\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\t\tnew StateStoreError({\n\t\t\t\t\t\t\t\treason: 'lock-contention',\n\t\t\t\t\t\t\t\tdetail: `state-store write blocked: peer holds ${e.path} (waited ${e.waitedMillis}ms)`,\n\t\t\t\t\t\t\t\tcause: e,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\tStackLockIoError: (e) =>\n\t\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\t\tnew StateStoreError({\n\t\t\t\t\t\t\t\treason: 'io-failed',\n\t\t\t\t\t\t\t\tdetail: `state-store lock IO error on ${e.path}`,\n\t\t\t\t\t\t\t\tcause: e.cause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t}),\n\t\t\t);\n\n\t\tconst set: StateStore['set'] = <V>(key: StateKey<V>, value: V) =>\n\t\t\tmapLockErrors(\n\t\t\t\tlock.withLock(\n\t\t\t\t\tprovideEnv(\n\t\t\t\t\t\t// Uninterruptible: an interrupt landing between the\n\t\t\t\t\t\t// disk write and the cache update would leave the\n\t\t\t\t\t\t// in-process cache stale vs disk. The section is\n\t\t\t\t\t\t// short and already serialized by `withLock`, so\n\t\t\t\t\t\t// masking interruption here costs nothing and keeps\n\t\t\t\t\t\t// cache/disk coherent.\n\t\t\t\t\t\tEffect.uninterruptible(\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\t// Re-read INSIDE the lock so we don't clobber\n\t\t\t\t\t\t\t\t// foreign writes that landed since our prime.\n\t\t\t\t\t\t\t\tconst fresh = yield* readDocument;\n\t\t\t\t\t\t\t\tconst { plugin, suffix } = splitKey(key);\n\t\t\t\t\t\t\t\tconst next: StateDocument = {\n\t\t\t\t\t\t\t\t\tversion: 1,\n\t\t\t\t\t\t\t\t\tplugins: {\n\t\t\t\t\t\t\t\t\t\t...fresh.plugins,\n\t\t\t\t\t\t\t\t\t\t[plugin]: {\n\t\t\t\t\t\t\t\t\t\t\t...fresh.plugins[plugin],\n\t\t\t\t\t\t\t\t\t\t\t[suffix]: {\n\t\t\t\t\t\t\t\t\t\t\t\tstate: 'present',\n\t\t\t\t\t\t\t\t\t\t\t\tvalue: value as unknown,\n\t\t\t\t\t\t\t\t\t\t\t\tupdatedAt: Date.now(),\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tyield* writeDocument(next);\n\t\t\t\t\t\t\t\tyield* Ref.set(cache, next);\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\t);\n\n\t\tconst del: StateStore['delete'] = <V>(key: StateKey<V>) =>\n\t\t\tmapLockErrors(\n\t\t\t\tlock.withLock(\n\t\t\t\t\tprovideEnv(\n\t\t\t\t\t\t// Uninterruptible for the same cache/disk coherence\n\t\t\t\t\t\t// reason as `set` — see the note there.\n\t\t\t\t\t\tEffect.uninterruptible(\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tconst fresh = yield* readDocument;\n\t\t\t\t\t\t\t\tconst { plugin, suffix } = splitKey(key);\n\t\t\t\t\t\t\t\t// Tombstone-write — preserves the\n\t\t\t\t\t\t\t\t// \"deleted-since\" record across snapshots.\n\t\t\t\t\t\t\t\tconst next: StateDocument = {\n\t\t\t\t\t\t\t\t\tversion: 1,\n\t\t\t\t\t\t\t\t\tplugins: {\n\t\t\t\t\t\t\t\t\t\t...fresh.plugins,\n\t\t\t\t\t\t\t\t\t\t[plugin]: {\n\t\t\t\t\t\t\t\t\t\t\t...fresh.plugins[plugin],\n\t\t\t\t\t\t\t\t\t\t\t[suffix]: {\n\t\t\t\t\t\t\t\t\t\t\t\tstate: 'tombstone',\n\t\t\t\t\t\t\t\t\t\t\t\tvalue: null,\n\t\t\t\t\t\t\t\t\t\t\t\tupdatedAt: Date.now(),\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tyield* writeDocument(next);\n\t\t\t\t\t\t\t\tyield* Ref.set(cache, next);\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\t);\n\n\t\tconst listUnder: StateStore['listUnder'] = (prefix: PluginKey) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst doc = yield* Ref.get(cache);\n\t\t\t\tconst ns = doc.plugins[prefix];\n\t\t\t\tif (!ns) return [];\n\t\t\t\t// Skip tombstones from `listUnder` — they're\n\t\t\t\t// internally observable via `peek` but the public\n\t\t\t\t// \"what keys are present?\" view hides them.\n\t\t\t\treturn Object.entries(ns)\n\t\t\t\t\t.filter(([, entry]) => entry.state === 'present')\n\t\t\t\t\t.map(([suffix]) => `${prefix}/${suffix}`);\n\t\t\t});\n\n\t\treturn StateStoreService.of({\n\t\t\tget,\n\t\t\tset,\n\t\t\tdelete: del,\n\t\t\tlistUnder,\n\t\t});\n\t}),\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { StrategyRegistryService } from "./service.mjs";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
//#region src/substrate/runtime/strategy-registry/chain-keyed-strategy-for.ts
|
|
4
|
+
/**
|
|
5
|
+
* Look up the strategy registered under `<prefix>:<chainId>` and surface
|
|
6
|
+
* it with the caller's expected shape.
|
|
7
|
+
*
|
|
8
|
+
* The strategy shape `P` is a free generic — the plugin that owns the
|
|
9
|
+
* capability prefix also owns its strategy contract, and callers pass
|
|
10
|
+
* both at the call site:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { FAUCET_CAPABILITY_KEY_PREFIX, type FaucetStrategy } from '../faucet/index.ts';
|
|
14
|
+
*
|
|
15
|
+
* const strategy = yield* chainKeyedStrategyFor<FaucetStrategy>(
|
|
16
|
+
* FAUCET_CAPABILITY_KEY_PREFIX,
|
|
17
|
+
* parts.chainId,
|
|
18
|
+
* );
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Failure shape is the substrate `StrategyNotFoundError`; callers that
|
|
22
|
+
* want a plugin-flavored error project it via `Effect.catchTag` at the
|
|
23
|
+
* call site.
|
|
24
|
+
*/
|
|
25
|
+
const chainKeyedStrategyFor = (prefix, chainId) => Effect.gen(function* () {
|
|
26
|
+
const registry = yield* StrategyRegistryService;
|
|
27
|
+
const key = `${prefix}:${chainId}`;
|
|
28
|
+
return yield* registry.get(key);
|
|
29
|
+
});
|
|
30
|
+
//#endregion
|
|
31
|
+
export { chainKeyedStrategyFor };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=chain-keyed-strategy-for.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chain-keyed-strategy-for.mjs","names":[],"sources":["../../../../src/substrate/runtime/strategy-registry/chain-keyed-strategy-for.ts"],"sourcesContent":["// `chainKeyedStrategyFor` — strategy-registry sugar for chain-keyed\n// strategy lookups. Mirrors `chainProbeFor`.\n//\n// The substrate is name-blind: the caller passes both the prefix and\n// the chain id, and the helper assembles `<prefix>:<chainId>` and\n// performs the registry lookup. The faucet plugin's\n// `FAUCET_CAPABILITY_KEY_PREFIX` is the single source of truth for the\n// faucet key shape; the substrate does NOT duplicate it.\n\nimport { Effect } from 'effect';\n\nimport type { ChainId } from '../../brand.ts';\nimport type { StrategyNotFoundError } from '../errors.ts';\nimport { StrategyRegistryService } from './service.ts';\n\n/**\n * Look up the strategy registered under `<prefix>:<chainId>` and surface\n * it with the caller's expected shape.\n *\n * The strategy shape `P` is a free generic — the plugin that owns the\n * capability prefix also owns its strategy contract, and callers pass\n * both at the call site:\n *\n * ```ts\n * import { FAUCET_CAPABILITY_KEY_PREFIX, type FaucetStrategy } from '../faucet/index.ts';\n *\n * const strategy = yield* chainKeyedStrategyFor<FaucetStrategy>(\n * FAUCET_CAPABILITY_KEY_PREFIX,\n * parts.chainId,\n * );\n * ```\n *\n * Failure shape is the substrate `StrategyNotFoundError`; callers that\n * want a plugin-flavored error project it via `Effect.catchTag` at the\n * call site.\n */\nexport const chainKeyedStrategyFor = <P>(\n\tprefix: string,\n\tchainId: ChainId,\n): Effect.Effect<P, StrategyNotFoundError, StrategyRegistryService> =>\n\tEffect.gen(function* () {\n\t\tconst registry = yield* StrategyRegistryService;\n\t\tconst key = `${prefix}:${chainId}`;\n\t\treturn yield* registry.get<typeof key, P>(key);\n\t});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAoCA,MAAa,yBACZ,QACA,YAEA,OAAO,IAAI,aAAa;CACvB,MAAM,WAAW,OAAO;CACxB,MAAM,MAAM,GAAG,OAAO,GAAG;AACzB,QAAO,OAAO,SAAS,IAAmB,IAAI;EAC7C"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { chainProbeCapabilityKey } from "../../../contracts/chain-probe.mjs";
|
|
2
1
|
import { StrategyRegistryService } from "./service.mjs";
|
|
2
|
+
import { chainProbeCapabilityKey } from "../../../contracts/chain-probe.mjs";
|
|
3
3
|
import { Effect } from "effect";
|
|
4
4
|
//#region src/substrate/runtime/strategy-registry/chain-probe-for.ts
|
|
5
5
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chain-probe-for.mjs","names":[],"sources":["../../../../src/substrate/runtime/strategy-registry/chain-probe-for.ts"],"sourcesContent":["// `chainProbeFor` — strategy-registry sugar for the chain-probe lookup.\n//\n// Every plugin that produces an on-chain artifact (package, coin, seal,\n// walrus, action, ...) repeats the same 4-line dance: yield the\n// StrategyRegistryService, then `registry.get<...>(chainProbeCapabilityKey(chain))`\n// with the same generic args and the same trailing cast. The dance is\n// load-bearing exactly once (the cast threads `SuiProbeKey` through the\n// capability-key's `string` discriminator) — every other site is\n// boilerplate.\n//\n// This helper consolidates the pattern: one Effect, one R-channel\n// (`StrategyRegistryService`), one typed return (`ChainProbe<Key>`).\n// Call sites become a single `yield* chainProbeFor(sui.chain)`.\n\nimport { Effect } from 'effect';\n\nimport { chainProbeCapabilityKey, type ChainProbe } from '../../../contracts/chain-probe.ts';\nimport type {
|
|
1
|
+
{"version":3,"file":"chain-probe-for.mjs","names":[],"sources":["../../../../src/substrate/runtime/strategy-registry/chain-probe-for.ts"],"sourcesContent":["// `chainProbeFor` — strategy-registry sugar for the chain-probe lookup.\n//\n// Every plugin that produces an on-chain artifact (package, coin, seal,\n// walrus, action, ...) repeats the same 4-line dance: yield the\n// StrategyRegistryService, then `registry.get<...>(chainProbeCapabilityKey(chain))`\n// with the same generic args and the same trailing cast. The dance is\n// load-bearing exactly once (the cast threads `SuiProbeKey` through the\n// capability-key's `string` discriminator) — every other site is\n// boilerplate.\n//\n// This helper consolidates the pattern: one Effect, one R-channel\n// (`StrategyRegistryService`), one typed return (`ChainProbe<Key>`).\n// Call sites become a single `yield* chainProbeFor(sui.chain)`.\n\nimport { Effect } from 'effect';\n\nimport { chainProbeCapabilityKey, type ChainProbe } from '../../../contracts/chain-probe.ts';\nimport type { ChainId } from '../../brand.ts';\nimport type { StrategyNotFoundError } from '../errors.ts';\nimport { StrategyRegistryService } from './service.ts';\n\n/**\n * Look up the chain-probe contributed by the chain owner (Sui's\n * `acquire` registers one per resolved chain id) and surface it with\n * the caller's expected key shape.\n *\n * `Key` defaults to `unknown` so call sites that don't care about the\n * probe key shape can omit the generic; sites that consume Sui probes\n * pass `SuiProbeKey` explicitly. The cast from the registry's\n * `unknown` payload to `ChainProbe<Key>` is centralised here — call\n * sites stop repeating it.\n */\nexport const chainProbeFor = <Key = unknown>(\n\tchain: ChainId,\n): Effect.Effect<ChainProbe<Key>, StrategyNotFoundError, StrategyRegistryService> =>\n\tEffect.gen(function* () {\n\t\tconst registry = yield* StrategyRegistryService;\n\t\tconst probe = yield* registry.get<`chain-probe:${string}`, ChainProbe<Key>>(\n\t\t\tchainProbeCapabilityKey(chain),\n\t\t);\n\t\treturn probe;\n\t});\n"],"mappings":";;;;;;;;;;;;;;;AAgCA,MAAa,iBACZ,UAEA,OAAO,IAAI,aAAa;AAKvB,QAAO,QAHc,OADG,yBACM,IAC7B,wBAAwB,MAAM,CAC9B;EAEA"}
|
|
@@ -1,60 +1,46 @@
|
|
|
1
|
+
import { SpanAttr } from "../observability/spans.mjs";
|
|
2
|
+
import { makeScopedMultimap } from "../scoped-multimap/service.mjs";
|
|
3
|
+
import "../scoped-multimap/index.mjs";
|
|
1
4
|
import { StrategyNotFoundError } from "../errors.mjs";
|
|
2
|
-
import { Context, Effect, Layer
|
|
5
|
+
import { Context, Effect, Layer } from "effect";
|
|
3
6
|
//#region src/substrate/runtime/strategy-registry/service.ts
|
|
4
|
-
var StrategyRegistryService = class extends Context.Service()("@devstack
|
|
7
|
+
var StrategyRegistryService = class extends Context.Service()("@devstack/substrate/StrategyRegistry") {};
|
|
5
8
|
/**
|
|
6
9
|
* Layer. Constructed per-scope; the orchestrator hands the registry
|
|
7
10
|
* to each plugin's acquire so contributions land on the right
|
|
8
11
|
* stack's registry, not a global one.
|
|
9
12
|
*/
|
|
10
13
|
const layerStrategyRegistry = Layer.effect(StrategyRegistryService, Effect.gen(function* () {
|
|
11
|
-
const
|
|
12
|
-
const seqRef = yield* Ref.make(0);
|
|
14
|
+
const store = yield* makeScopedMultimap();
|
|
13
15
|
const register = (key, strategy, options) => Effect.gen(function* () {
|
|
14
|
-
const seq = yield* Ref.updateAndGet(seqRef, (n) => n + 1);
|
|
15
16
|
const entry = {
|
|
16
17
|
strategy,
|
|
17
18
|
autoMounted: options?.autoMounted ?? false,
|
|
18
|
-
priority: options?.priority ?? 0
|
|
19
|
-
seq
|
|
19
|
+
priority: options?.priority ?? 0
|
|
20
20
|
};
|
|
21
|
-
yield*
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return next;
|
|
26
|
-
});
|
|
27
|
-
yield* Effect.addFinalizer((_exit) => Ref.update(state, (current) => {
|
|
28
|
-
const existing = current.get(key);
|
|
29
|
-
if (!existing) return current;
|
|
30
|
-
const filtered = existing.filter((e) => e.seq !== seq);
|
|
31
|
-
const next = new Map(current);
|
|
32
|
-
if (filtered.length === 0) next.delete(key);
|
|
33
|
-
else next.set(key, filtered);
|
|
34
|
-
return next;
|
|
35
|
-
}));
|
|
21
|
+
yield* store.register([{
|
|
22
|
+
key,
|
|
23
|
+
value: entry
|
|
24
|
+
}]);
|
|
36
25
|
yield* Effect.annotateCurrentSpan({
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
[SpanAttr.strategyKey]: key,
|
|
27
|
+
[SpanAttr.strategyAutoMounted]: entry.autoMounted
|
|
39
28
|
});
|
|
40
29
|
}).pipe(Effect.withSpan("substrate.strategyRegistry.register"));
|
|
41
30
|
const get = (key) => Effect.gen(function* () {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
if (!entries || entries.length === 0) return yield* new StrategyNotFoundError({
|
|
31
|
+
const entries = yield* store.entriesFor(key);
|
|
32
|
+
if (entries.length === 0) return yield* new StrategyNotFoundError({
|
|
45
33
|
capabilityKey: key,
|
|
46
|
-
registeredKeys:
|
|
34
|
+
registeredKeys: yield* store.keys
|
|
47
35
|
});
|
|
48
36
|
let best = entries[0];
|
|
49
37
|
for (let i = 1; i < entries.length; i++) {
|
|
50
38
|
const e = entries[i];
|
|
51
|
-
if (e.priority > best.priority || e.priority === best.priority && e.seq > best.seq) best = e;
|
|
39
|
+
if (e.value.priority > best.value.priority || e.value.priority === best.value.priority && e.seq > best.seq) best = e;
|
|
52
40
|
}
|
|
53
|
-
return best.strategy;
|
|
41
|
+
return best.value.strategy;
|
|
54
42
|
}).pipe(Effect.withSpan("substrate.strategyRegistry.get", { attributes: { key } }));
|
|
55
|
-
const list = () =>
|
|
56
|
-
return [...(yield* Ref.get(state)).keys()];
|
|
57
|
-
});
|
|
43
|
+
const list = () => store.keys;
|
|
58
44
|
return StrategyRegistryService.of({
|
|
59
45
|
get,
|
|
60
46
|
register,
|