@mysten-incubation/devstack 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- 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 +40 -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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/substrate/runtime/artifact-publisher/index.ts"],"sourcesContent":["// ArtifactPublisher — substrate-level service.\n//\n// Architecture §10: substrate primitive that orchestrates the\n// `cache → verify → produce → register` cycle every on-chain\n// produce/verify plugin (package, coin, walrus deploy, seal deploy,\n// deepbook deploy, etc.) flows through.\n//\n// Plugins call `publisher.publish(spec)` with their per-cycle\n// `ArtifactSpec<Produced, Verified>` (namespace, chain,\n// contentHash, verifySchema, verify, produce, register). The\n// substrate handles:\n//\n// 1. `cache.lookup({namespace, chain, contentHash})`. Hit AND\n// `spec.verify` returns a non-null value → `register`(decoded\n// cached payload) and return.\n// 2. Miss OR verify-returned-null → run `spec.produce` under the\n// caller's Scope; on success, write the produced payload to\n// the cache (best-effort, errors logged but not propagated);\n// then `register(produced)`.\n//\n// The substrate carries the cache + chain-probe seams via Layers\n// (CacheService, StrategyRegistryService). Plugins yield this\n// service from their acquire body; the supervisor injects it via\n// pluginContext.\n//\n// Cache payload codec: we serialize `Produced` as JSON. The artifact publisher\n// spec doesn't bake a codec in (`Produced` is generic); JSON\n// round-trip matches existing plugin call sites\n// (`CachedPackageEntry`, `CachedMint`, etc. are JSON-shaped).\n\nimport { Context, Effect, Layer, Scope } from 'effect';\n\nimport type {\n\tArtifactPublishError,\n\tArtifactPublisher,\n\tArtifactSpec,\n} from '../../../primitives/artifact-publisher.ts';\nimport { CacheService } from '../cache/index.ts';\nimport { parseJsonTextSync } from '../runtime-decode.ts';\n\n// Re-export the ChainOperation typed seam — plugin authors compose\n// produce bodies via `compileChainOperation({...})` rather than\n// hand-rolling the `Effect<Produced, ArtifactPublishError, Scope>`.\n//\n// `ResolvedSigner` is intentionally NOT re-exported here — it is\n// owned by `sui-execute/` and reached via that module's barrel; both\n// modules share the single canonical shape.\nexport {\n\tcompileChainOperation,\n\ttype ChainOperation,\n\ttype OneShotRunner,\n\ttype OneShotSpec,\n\ttype SuiEffects,\n\ttype SuiTxBuilder,\n} from './chain-operation.ts';\n\n// ---------------------------------------------------------------------------\n// Service tag\n// ---------------------------------------------------------------------------\n\n/**\n * Substrate-level service projecting the\n * `ArtifactPublisher` primitive. Plugins yield this tag in\n * their acquire body; the supervisor's `pluginContext` carries it.\n *\n * The service is constructed once per stack (Layer-driven) and\n * closes over the per-stack Cache + StrategyRegistry. Parallel\n * stacks isolate by construction — each stack's Layer build is\n * independent.\n */\nexport class ArtifactPublisherService extends Context.Service<\n\tArtifactPublisherService,\n\tArtifactPublisher\n>()('@devstack-rewrite/substrate/ArtifactPublisher') {}\n\n// ---------------------------------------------------------------------------\n// Encode / decode the cached payload\n// ---------------------------------------------------------------------------\n\nconst encode = (value: unknown): Uint8Array => new TextEncoder().encode(JSON.stringify(value));\n\nconst decode = <Produced>(bytes: Uint8Array): Produced | null => {\n\ttry {\n\t\treturn parseJsonTextSync(new TextDecoder().decode(bytes), {\n\t\t\tsource: 'artifact-publisher cache payload',\n\t\t\tmkError: (issue) => issue,\n\t\t}) as Produced;\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n// ---------------------------------------------------------------------------\n// Publish: cache → verify → produce → register\n// ---------------------------------------------------------------------------\n\nconst makePublisher = (cache: typeof CacheService.Service): ArtifactPublisher => ({\n\tpublish: <Produced, Verified>(\n\t\tspec: ArtifactSpec<Produced, Verified>,\n\t): Effect.Effect<Produced | Verified, ArtifactPublishError, Scope.Scope> =>\n\t\tEffect.gen(function* () {\n\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t'artifactPublisher.namespace': spec.namespace,\n\t\t\t\t'artifactPublisher.chain': spec.chain,\n\t\t\t\t'artifactPublisher.contentHash': spec.contentHash,\n\t\t\t});\n\n\t\t\t// 1. Cache lookup. Best-effort: a CacheError on lookup\n\t\t\t// surfaces as `cache-corrupt`; the substrate's contract\n\t\t\t// is \"re-produce is always safe\", so we coerce read\n\t\t\t// failures to a miss rather than abort the cycle.\n\t\t\tconst hit = yield* cache\n\t\t\t\t.lookup({\n\t\t\t\t\tnamespace: spec.namespace,\n\t\t\t\t\tchain: spec.chain,\n\t\t\t\t\tcontentHash: spec.contentHash,\n\t\t\t\t})\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(null)));\n\n\t\t\tif (hit !== null) {\n\t\t\t\t// Decode the cached `Produced` BEFORE verify so the\n\t\t\t\t// plugin's verify Effect can key its on-chain probe\n\t\t\t\t// off a field of the cached payload (e.g. action's\n\t\t\t\t// `digest`, package's `packageId`). A decode failure\n\t\t\t\t// here is \"cache corruption\" — drop through to\n\t\t\t\t// re-produce so the cycle is still safe.\n\t\t\t\tconst cached = decode<Produced>(hit.bytes);\n\t\t\t\tif (cached !== null) {\n\t\t\t\t\t// 2. Verify (lenient — returns null on transient\n\t\t\t\t\t// failure or not-found). Architecture §10:\n\t\t\t\t\t// verify-on-hit covers chain-state drift.\n\t\t\t\t\tconst verified = yield* spec.verify(cached);\n\t\t\t\t\tif (verified !== null) {\n\t\t\t\t\t\tconst payload: Produced | Verified = cached;\n\t\t\t\t\t\tyield* spec.register(payload);\n\t\t\t\t\t\tyield* Effect.annotateCurrentSpan({ 'artifactPublisher.path': 'hit' });\n\t\t\t\t\t\treturn payload;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 3. Miss or verify-failed → produce.\n\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t'artifactPublisher.path': hit === null ? 'miss' : 'verify-failed',\n\t\t\t});\n\t\t\tconst produced = yield* spec.produce;\n\n\t\t\t// 4. Cache write — best-effort. Architecture: a write\n\t\t\t// failure must NOT roll back the on-chain effect.\n\t\t\tyield* cache\n\t\t\t\t.write(\n\t\t\t\t\t{\n\t\t\t\t\t\tnamespace: spec.namespace,\n\t\t\t\t\t\tchain: spec.chain,\n\t\t\t\t\t\tcontentHash: spec.contentHash,\n\t\t\t\t\t},\n\t\t\t\t\tencode(produced),\n\t\t\t\t)\n\t\t\t\t.pipe(Effect.catch(() => Effect.void));\n\n\t\t\t// 5. Register — fires on EVERY cycle (architecture §10).\n\t\t\tyield* spec.register(produced);\n\t\t\treturn produced;\n\t\t}).pipe(Effect.withSpan('substrate.artifactPublisher.publish')),\n});\n\n// ---------------------------------------------------------------------------\n// Layer\n// ---------------------------------------------------------------------------\n\n/**\n * Layer that materializes the publisher from the substrate's\n * Cache service. Plugins yield `ArtifactPublisherService`;\n * the supervisor provides it via `pluginContext`.\n *\n * The publisher is service-name-blind: it reaches `CacheService`\n * via the Context, NOT through a hard-coded import edge with any\n * plugin. Chain-probe lookup is the caller's concern — the spec's\n * `verify` Effect closes over the probe.\n */\nexport const layerArtifactPublisher: Layer.Layer<ArtifactPublisherService, never, CacheService> =\n\tLayer.effect(\n\t\tArtifactPublisherService,\n\t\tEffect.gen(function* () {\n\t\t\tconst cache = yield* CacheService;\n\t\t\treturn ArtifactPublisherService.of(makePublisher(cache));\n\t\t}),\n\t);\n"],"mappings":";;;;;;;;;;;;;;AAsEA,IAAa,2BAAb,cAA8C,QAAQ,SAGnD,CAAC,gDAAgD,CAAC;AAMrD,MAAM,UAAU,UAA+B,IAAI,aAAa,CAAC,OAAO,KAAK,UAAU,MAAM,CAAC;AAE9F,MAAM,UAAoB,UAAuC;AAChE,KAAI;AACH,SAAO,kBAAkB,IAAI,aAAa,CAAC,OAAO,MAAM,EAAE;GACzD,QAAQ;GACR,UAAU,UAAU;GACpB,CAAC;SACK;AACP,SAAO;;;AAQT,MAAM,iBAAiB,WAA2D,EACjF,UACC,SAEA,OAAO,IAAI,aAAa;AACvB,QAAO,OAAO,oBAAoB;EACjC,+BAA+B,KAAK;EACpC,2BAA2B,KAAK;EAChC,iCAAiC,KAAK;EACtC,CAAC;CAMF,MAAM,MAAM,OAAO,MACjB,OAAO;EACP,WAAW,KAAK;EAChB,OAAO,KAAK;EACZ,aAAa,KAAK;EAClB,CAAC,CACD,KAAK,OAAO,YAAY,OAAO,QAAQ,KAAK,CAAC,CAAC;AAEhD,KAAI,QAAQ,MAAM;EAOjB,MAAM,SAAS,OAAiB,IAAI,MAAM;AAC1C,MAAI,WAAW;QAKV,OADoB,KAAK,OAAO,OAAO,MAC1B,MAAM;IACtB,MAAM,UAA+B;AACrC,WAAO,KAAK,SAAS,QAAQ;AAC7B,WAAO,OAAO,oBAAoB,EAAE,0BAA0B,OAAO,CAAC;AACtE,WAAO;;;;AAMV,QAAO,OAAO,oBAAoB,EACjC,0BAA0B,QAAQ,OAAO,SAAS,iBAClD,CAAC;CACF,MAAM,WAAW,OAAO,KAAK;AAI7B,QAAO,MACL,MACA;EACC,WAAW,KAAK;EAChB,OAAO,KAAK;EACZ,aAAa,KAAK;EAClB,EACD,OAAO,SAAS,CAChB,CACA,KAAK,OAAO,YAAY,OAAO,KAAK,CAAC;AAGvC,QAAO,KAAK,SAAS,SAAS;AAC9B,QAAO;EACN,CAAC,KAAK,OAAO,SAAS,sCAAsC,CAAC,EAChE;;;;;;;;;;;AAgBD,MAAa,yBACZ,MAAM,OACL,0BACA,OAAO,IAAI,aAAa;CACvB,MAAM,QAAQ,OAAO;AACrB,QAAO,yBAAyB,GAAG,cAAc,MAAM,CAAC;EACvD,CACF"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/substrate/runtime/artifact-publisher/index.ts"],"sourcesContent":["// ArtifactPublisher — substrate-level service.\n//\n// Architecture §10: substrate primitive that orchestrates the\n// `cache → verify → produce → register` cycle every on-chain\n// produce/verify plugin (package, coin, walrus deploy, seal deploy,\n// deepbook deploy, etc.) flows through.\n//\n// Plugins call `publisher.publish(spec)` with their per-cycle\n// `ArtifactSpec<Produced, Verified>` (namespace, chain,\n// contentHash, verify, produce, register). The\n// substrate handles:\n//\n// 1. `cache.lookup({namespace, chain, contentHash})`. Hit AND\n// `spec.verify` returns a non-null value → `register`(decoded\n// cached payload) and return.\n// 2. Miss OR verify-returned-null → run `spec.produce` under the\n// caller's Scope; on success, write the produced payload to\n// the cache (best-effort, errors logged but not propagated);\n// then `register(produced)`.\n//\n// The substrate carries the cache + chain-probe seams via Layers\n// (CacheService, StrategyRegistryService). Plugins yield this\n// service from their acquire body; the supervisor injects it via\n// pluginContext.\n//\n// Cache payload codec: we serialize `Produced` as JSON. The artifact publisher\n// spec doesn't bake a codec in (`Produced` is generic); JSON\n// round-trip matches existing plugin call sites\n// (`CachedPackageEntry`, `CachedMint`, etc. are JSON-shaped).\n\nimport { Context, Effect, Layer, Scope } from 'effect';\n\nimport type {\n\tArtifactPublishError,\n\tArtifactPublisher,\n\tArtifactSpec,\n} from '../../../primitives/artifact-publisher.ts';\nimport { CacheService } from '../cache/index.ts';\nimport { setCurrentPluginPhase } from '../current-plugin.ts';\nimport { SpanAttr } from '../observability/spans.ts';\nimport { parseJsonTextSync } from '../runtime-decode.ts';\n\n// ---------------------------------------------------------------------------\n// Service tag\n// ---------------------------------------------------------------------------\n\n/**\n * Substrate-level service projecting the\n * `ArtifactPublisher` primitive. Plugins yield this tag in\n * their acquire body; the supervisor's `pluginContext` carries it.\n *\n * The service is constructed once per stack (Layer-driven) and\n * closes over the per-stack Cache + StrategyRegistry. Parallel\n * stacks isolate by construction — each stack's Layer build is\n * independent.\n */\nexport class ArtifactPublisherService extends Context.Service<\n\tArtifactPublisherService,\n\tArtifactPublisher\n>()('@devstack/substrate/ArtifactPublisher') {}\n\n// ---------------------------------------------------------------------------\n// Encode / decode the cached payload\n// ---------------------------------------------------------------------------\n\nconst encode = (value: unknown): Uint8Array => new TextEncoder().encode(JSON.stringify(value));\n\nconst decode = <Produced>(bytes: Uint8Array): Produced | null => {\n\ttry {\n\t\treturn parseJsonTextSync(new TextDecoder().decode(bytes), {\n\t\t\tsource: 'artifact-publisher cache payload',\n\t\t\tmkError: (issue) => issue,\n\t\t}) as Produced;\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n// ---------------------------------------------------------------------------\n// Publish: cache → verify → produce → register\n// ---------------------------------------------------------------------------\n\nconst makePublisher = (cache: typeof CacheService.Service): ArtifactPublisher => ({\n\tpublish: <Produced, Verified>(\n\t\tspec: ArtifactSpec<Produced, Verified>,\n\t): Effect.Effect<Produced, ArtifactPublishError, Scope.Scope> =>\n\t\tEffect.gen(function* () {\n\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t[SpanAttr.artifactPublisherNamespace]: spec.namespace,\n\t\t\t\t[SpanAttr.artifactPublisherChain]: spec.chain,\n\t\t\t\t[SpanAttr.artifactPublisherContentHash]: spec.contentHash,\n\t\t\t});\n\n\t\t\t// 1. Cache lookup. Best-effort: a CacheError on lookup\n\t\t\t// surfaces as `cache-corrupt`; the substrate's contract\n\t\t\t// is \"re-produce is always safe\", so we coerce read\n\t\t\t// failures to a miss rather than abort the cycle.\n\t\t\tconst hit = yield* cache\n\t\t\t\t.lookup({\n\t\t\t\t\tnamespace: spec.namespace,\n\t\t\t\t\tchain: spec.chain,\n\t\t\t\t\tcontentHash: spec.contentHash,\n\t\t\t\t})\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(null)));\n\n\t\t\tif (hit !== null) {\n\t\t\t\t// Decode the cached `Produced` BEFORE verify so the\n\t\t\t\t// plugin's verify Effect can key its on-chain probe\n\t\t\t\t// off a field of the cached payload (e.g. action's\n\t\t\t\t// `digest`, package's `packageId`). A decode failure\n\t\t\t\t// here is \"cache corruption\" — drop through to\n\t\t\t\t// re-produce so the cycle is still safe.\n\t\t\t\tconst cached = decode<Produced>(hit.bytes);\n\t\t\t\tif (cached !== null) {\n\t\t\t\t\t// 2. Verify (lenient — returns null on transient\n\t\t\t\t\t// failure or not-found). Architecture §10:\n\t\t\t\t\t// verify-on-hit covers chain-state drift.\n\t\t\t\t\tconst verified = yield* spec.verify(cached);\n\t\t\t\t\tif (verified !== null) {\n\t\t\t\t\t\t// The substrate always returns the decoded\n\t\t\t\t\t\t// `Produced` payload — the `Verified` shape is a\n\t\t\t\t\t\t// probe-only signal that never escapes. Callers\n\t\t\t\t\t\t// therefore type-narrow trivially against `Produced`.\n\t\t\t\t\t\tyield* spec.register(cached);\n\t\t\t\t\t\tyield* Effect.annotateCurrentSpan({ [SpanAttr.artifactPublisherPath]: 'hit' });\n\t\t\t\t\t\t// Reuse is the quiet, expected warm-restart path — debug\n\t\t\t\t\t\t// only. The interesting (and noisy) case is a *re-run*,\n\t\t\t\t\t\t// logged below.\n\t\t\t\t\t\tyield* Effect.logDebug(\n\t\t\t\t\t\t\t`artifact-publisher: reusing cached '${spec.namespace}' on chain ${spec.chain} ` +\n\t\t\t\t\t\t\t\t`(cache hit; no re-deploy).`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn cached;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 3. Miss or verify-failed → produce. State WHEN and WHY loudly:\n\t\t\t// on a restart this is the line that explains why an on-chain\n\t\t\t// id is about to change. A fresh chain genesis misses every\n\t\t\t// cache key (keyed by chain id), so packages / key servers /\n\t\t\t// walrus all get brand-new ids, and content bound to the old\n\t\t\t// ids (e.g. Seal-encrypted blobs) can no longer be resolved.\n\t\t\tif (hit === null) {\n\t\t\t\tyield* Effect.annotateCurrentSpan({ [SpanAttr.artifactPublisherPath]: 'miss' });\n\t\t\t\tyield* Effect.logInfo(\n\t\t\t\t\t`artifact-publisher: producing '${spec.namespace}' on chain ${spec.chain} — ` +\n\t\t\t\t\t\t`no cached artifact for this chain + content hash ` +\n\t\t\t\t\t\t`(first deploy on this chain, fresh genesis after a restart, or changed inputs).`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t\t[SpanAttr.artifactPublisherPath]: 'verify-failed',\n\t\t\t\t});\n\t\t\t\tyield* Effect.logWarning(\n\t\t\t\t\t`artifact-publisher: re-deploying '${spec.namespace}' on chain ${spec.chain} — ` +\n\t\t\t\t\t\t`a cached artifact existed but failed on-chain verification ` +\n\t\t\t\t\t\t`(object missing, or RPC unavailable during restart); a new id will replace ` +\n\t\t\t\t\t\t`the prior deployment.`,\n\t\t\t\t);\n\t\t\t\t// Effect logs are dropped under the `up` TUI (Logger.layer([])).\n\t\t\t\t// The verify-failed re-deploy is the anomalous restart case — a\n\t\t\t\t// cached id existed but is no longer resolvable — so narrate it\n\t\t\t\t// on the supervised plugin's row, the channel the TUI renders.\n\t\t\t\t// (A plain cache miss stays quiet here: on a cold boot every\n\t\t\t\t// artifact misses, and the plugin narrates its own publish.)\n\t\t\t\tyield* setCurrentPluginPhase(\n\t\t\t\t\t`re-deploying ${spec.namespace} (cached artifact failed verification on restart)`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst produced = yield* spec.produce;\n\n\t\t\t// 4. Cache write — best-effort. Architecture: a write\n\t\t\t// failure must NOT roll back the on-chain effect.\n\t\t\tyield* cache\n\t\t\t\t.write(\n\t\t\t\t\t{\n\t\t\t\t\t\tnamespace: spec.namespace,\n\t\t\t\t\t\tchain: spec.chain,\n\t\t\t\t\t\tcontentHash: spec.contentHash,\n\t\t\t\t\t},\n\t\t\t\t\tencode(produced),\n\t\t\t\t)\n\t\t\t\t.pipe(Effect.catch(() => Effect.void));\n\n\t\t\t// 5. Register — fires on EVERY cycle (architecture §10).\n\t\t\tyield* spec.register(produced);\n\t\t\treturn produced;\n\t\t}).pipe(Effect.withSpan('substrate.artifactPublisher.publish')),\n});\n\n// ---------------------------------------------------------------------------\n// Layer\n// ---------------------------------------------------------------------------\n\n/**\n * Layer that materializes the publisher from the substrate's\n * Cache service. Plugins yield `ArtifactPublisherService`;\n * the supervisor provides it via `pluginContext`.\n *\n * The publisher is service-name-blind: it reaches `CacheService`\n * via the Context, NOT through a hard-coded import edge with any\n * plugin. Chain-probe lookup is the caller's concern — the spec's\n * `verify` Effect closes over the probe.\n */\nexport const layerArtifactPublisher: Layer.Layer<ArtifactPublisherService, never, CacheService> =\n\tLayer.effect(\n\t\tArtifactPublisherService,\n\t\tEffect.gen(function* () {\n\t\t\tconst cache = yield* CacheService;\n\t\t\treturn ArtifactPublisherService.of(makePublisher(cache));\n\t\t}),\n\t);\n"],"mappings":";;;;;;;;;;;;;;;;;AAwDA,IAAa,2BAAb,cAA8C,QAAQ,SAGnD,CAAC,wCAAwC,CAAC;AAM7C,MAAM,UAAU,UAA+B,IAAI,aAAa,CAAC,OAAO,KAAK,UAAU,MAAM,CAAC;AAE9F,MAAM,UAAoB,UAAuC;AAChE,KAAI;AACH,SAAO,kBAAkB,IAAI,aAAa,CAAC,OAAO,MAAM,EAAE;GACzD,QAAQ;GACR,UAAU,UAAU;GACpB,CAAC;SACK;AACP,SAAO;;;AAQT,MAAM,iBAAiB,WAA2D,EACjF,UACC,SAEA,OAAO,IAAI,aAAa;AACvB,QAAO,OAAO,oBAAoB;GAChC,SAAS,6BAA6B,KAAK;GAC3C,SAAS,yBAAyB,KAAK;GACvC,SAAS,+BAA+B,KAAK;EAC9C,CAAC;CAMF,MAAM,MAAM,OAAO,MACjB,OAAO;EACP,WAAW,KAAK;EAChB,OAAO,KAAK;EACZ,aAAa,KAAK;EAClB,CAAC,CACD,KAAK,OAAO,YAAY,OAAO,QAAQ,KAAK,CAAC,CAAC;AAEhD,KAAI,QAAQ,MAAM;EAOjB,MAAM,SAAS,OAAiB,IAAI,MAAM;AAC1C,MAAI,WAAW;QAKV,OADoB,KAAK,OAAO,OAAO,MAC1B,MAAM;AAKtB,WAAO,KAAK,SAAS,OAAO;AAC5B,WAAO,OAAO,oBAAoB,GAAG,SAAS,wBAAwB,OAAO,CAAC;AAI9E,WAAO,OAAO,SACb,uCAAuC,KAAK,UAAU,aAAa,KAAK,MAAM,6BAE9E;AACD,WAAO;;;;AAWV,KAAI,QAAQ,MAAM;AACjB,SAAO,OAAO,oBAAoB,GAAG,SAAS,wBAAwB,QAAQ,CAAC;AAC/E,SAAO,OAAO,QACb,kCAAkC,KAAK,UAAU,aAAa,KAAK,MAAM,qIAGzE;QACK;AACN,SAAO,OAAO,oBAAoB,GAChC,SAAS,wBAAwB,iBAClC,CAAC;AACF,SAAO,OAAO,WACb,qCAAqC,KAAK,UAAU,aAAa,KAAK,MAAM,gKAI5E;AAOD,SAAO,sBACN,gBAAgB,KAAK,UAAU,mDAC/B;;CAEF,MAAM,WAAW,OAAO,KAAK;AAI7B,QAAO,MACL,MACA;EACC,WAAW,KAAK;EAChB,OAAO,KAAK;EACZ,aAAa,KAAK;EAClB,EACD,OAAO,SAAS,CAChB,CACA,KAAK,OAAO,YAAY,OAAO,KAAK,CAAC;AAGvC,QAAO,KAAK,SAAS,SAAS;AAC9B,QAAO;EACN,CAAC,KAAK,OAAO,SAAS,sCAAsC,CAAC,EAChE;;;;;;;;;;;AAgBD,MAAa,yBACZ,MAAM,OACL,0BACA,OAAO,IAAI,aAAa;CACvB,MAAM,QAAQ,OAAO;AACrB,QAAO,yBAAyB,GAAG,cAAc,MAAM,CAAC;EACvD,CACF"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { selfPid } from "./cross-process/self-pid.mjs";
|
|
1
2
|
import { AtomicWriteFailed } from "./errors.mjs";
|
|
2
3
|
import { Effect, FileSystem, Schema } from "effect";
|
|
4
|
+
import { closeSync, fsyncSync, linkSync, mkdirSync, openSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
5
|
import { randomUUID } from "node:crypto";
|
|
4
|
-
import { closeSync, fsyncSync, mkdirSync, openSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
6
|
//#region src/substrate/runtime/atomic-write.ts
|
|
6
7
|
/** 8-hex tempfile suffix. `crypto.randomUUID()` is collision-safe under
|
|
7
8
|
* parallel callers within the same pid — replaces the
|
|
@@ -23,20 +24,22 @@ const atomicWriteFile = (path, bytes, options = {}) => Effect.gen(function* () {
|
|
|
23
24
|
const fs = yield* FileSystem.FileSystem;
|
|
24
25
|
const mode = options.mode ?? 384;
|
|
25
26
|
const parentMode = options.parentMode ?? 448;
|
|
26
|
-
const tmp = `${path}.tmp.${
|
|
27
|
+
const tmp = `${path}.tmp.${selfPid()}.${tempSuffix()}`;
|
|
27
28
|
yield* fs.makeDirectory(dirname(path), {
|
|
28
29
|
recursive: true,
|
|
29
30
|
mode: parentMode
|
|
30
31
|
}).pipe(Effect.catch(failStage(path, "mkdir-parent")));
|
|
31
|
-
yield* Effect.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
yield* Effect.gen(function* () {
|
|
33
|
+
yield* Effect.scoped(Effect.gen(function* () {
|
|
34
|
+
const file = yield* fs.open(tmp, {
|
|
35
|
+
flag: "wx",
|
|
36
|
+
mode
|
|
37
|
+
}).pipe(Effect.catch(failStage(path, "open-temp")));
|
|
38
|
+
yield* file.writeAll(bytes).pipe(Effect.catch(failStage(path, "write")));
|
|
39
|
+
yield* file.sync.pipe(Effect.catch(failStage(path, "fsync")));
|
|
40
|
+
}));
|
|
41
|
+
yield* fs.rename(tmp, path).pipe(Effect.catch(failStage(path, "rename")));
|
|
42
|
+
}).pipe(Effect.onError(() => fs.remove(tmp, { force: true }).pipe(Effect.ignore)));
|
|
40
43
|
}).pipe(Effect.withSpan("substrate.atomicWriteFile", { attributes: { path } }));
|
|
41
44
|
/**
|
|
42
45
|
* Atomically write a JSON value. Encodes via Schema (so the on-disk
|
|
@@ -49,20 +52,20 @@ const atomicWriteJson = (path, schema, value, options) => Effect.gen(function* (
|
|
|
49
52
|
yield* atomicWriteFile(path, new TextEncoder().encode(json), options);
|
|
50
53
|
});
|
|
51
54
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
55
|
+
* Internal: mkdir-parent → O_EXCL temp → write → fsync. Returns the
|
|
56
|
+
* tempfile path on success; throws on any failure (and unlinks the
|
|
57
|
+
* tempfile if it managed to land). The temp exists after return —
|
|
58
|
+
* the caller is responsible for moving it to its final home (rename
|
|
59
|
+
* for clobber semantics, link for exclusive-create semantics) AND
|
|
60
|
+
* for unlinking the temp afterwards.
|
|
56
61
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
62
|
+
* Centralizes the durability boundary so both `atomicWriteFileSync`
|
|
63
|
+
* (rename) and `atomicWriteFileExclusiveSync` (link+unlink) share
|
|
64
|
+
* one tempfile/fsync implementation.
|
|
60
65
|
*/
|
|
61
|
-
const
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
const tmp = `${path}.tmp.${process.pid}.${tempSuffix()}`;
|
|
65
|
-
mkdirSync(dirname(path), {
|
|
66
|
+
const writeAndFsyncTempSync = (finalPath, bytes, mode, parentMode) => {
|
|
67
|
+
const tmp = `${finalPath}.tmp.${selfPid()}.${tempSuffix()}`;
|
|
68
|
+
mkdirSync(dirname(finalPath), {
|
|
66
69
|
recursive: true,
|
|
67
70
|
mode: parentMode
|
|
68
71
|
});
|
|
@@ -70,21 +73,83 @@ const atomicWriteFileSync = (path, bytes, options = {}) => {
|
|
|
70
73
|
flag: "wx",
|
|
71
74
|
mode
|
|
72
75
|
});
|
|
73
|
-
let fd = null;
|
|
74
|
-
try {
|
|
75
|
-
fd = openSync(tmp, "r");
|
|
76
|
-
fsyncSync(fd);
|
|
77
|
-
} finally {
|
|
78
|
-
if (fd !== null) closeSync(fd);
|
|
79
|
-
}
|
|
80
76
|
try {
|
|
81
|
-
|
|
77
|
+
let fd = null;
|
|
78
|
+
try {
|
|
79
|
+
fd = openSync(tmp, "r");
|
|
80
|
+
fsyncSync(fd);
|
|
81
|
+
} finally {
|
|
82
|
+
if (fd !== null) closeSync(fd);
|
|
83
|
+
}
|
|
82
84
|
} catch (cause) {
|
|
83
85
|
try {
|
|
84
86
|
unlinkSync(tmp);
|
|
85
87
|
} catch {}
|
|
86
88
|
throw cause;
|
|
87
89
|
}
|
|
90
|
+
return tmp;
|
|
91
|
+
};
|
|
92
|
+
/** Best-effort unlink. Swallows errors — used in cleanup paths where
|
|
93
|
+
* a failure to remove the temp should NOT mask the original error. */
|
|
94
|
+
const unlinkBestEffort = (path) => {
|
|
95
|
+
try {
|
|
96
|
+
unlinkSync(path);
|
|
97
|
+
} catch {}
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Synchronous atomic write. Same disk-side contract as
|
|
101
|
+
* `atomicWriteFile` (mkdir-parent → O_EXCL temp → write → fsync →
|
|
102
|
+
* rename). Returns `void` on success; throws on any failure with
|
|
103
|
+
* `cause` set to the underlying `NodeJS.ErrnoException`.
|
|
104
|
+
*
|
|
105
|
+
* Rename CLOBBERS a pre-existing file at the final path. Callers
|
|
106
|
+
* that need exclusive-create semantics ("fail if the final path
|
|
107
|
+
* already exists, atomically") want `atomicWriteFileExclusiveSync`
|
|
108
|
+
* instead — that surface uses `linkSync` which is POSIX-atomic and
|
|
109
|
+
* fails with `EEXIST` rather than overwriting.
|
|
110
|
+
*
|
|
111
|
+
* Used inside `Effect.try` by the cross-process modules; the caller
|
|
112
|
+
* maps the thrown error to a typed plugin/runtime error
|
|
113
|
+
* (`RosterIoError` / `SnapshotReservationIoError` / etc.).
|
|
114
|
+
*/
|
|
115
|
+
const atomicWriteFileSync = (path, bytes, options = {}) => {
|
|
116
|
+
const tmp = writeAndFsyncTempSync(path, bytes, options.mode ?? 384, options.parentMode ?? 448);
|
|
117
|
+
let renamed = false;
|
|
118
|
+
try {
|
|
119
|
+
renameSync(tmp, path);
|
|
120
|
+
renamed = true;
|
|
121
|
+
} finally {
|
|
122
|
+
if (!renamed) unlinkBestEffort(tmp);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Synchronous atomic EXCLUSIVE-CREATE write. Same disk-side contract
|
|
127
|
+
* as `atomicWriteFileSync` (mkdir-parent → O_EXCL temp → write →
|
|
128
|
+
* fsync) but uses `linkSync(tmp, final)` instead of `renameSync` for
|
|
129
|
+
* the final installation step. `link` is POSIX-atomic and FAILS with
|
|
130
|
+
* `EEXIST` if the target already exists — closing the rename-clobber
|
|
131
|
+
* race that `existsSync` + `renameSync` cannot.
|
|
132
|
+
*
|
|
133
|
+
* On success: the tempfile is unlinked (the hard link satisfies the
|
|
134
|
+
* final path).
|
|
135
|
+
*
|
|
136
|
+
* On `EEXIST`: the tempfile is unlinked and the underlying
|
|
137
|
+
* `NodeJS.ErrnoException` (with `code === 'EEXIST'`) is rethrown so
|
|
138
|
+
* the caller can read the winning final-path and decide what to do.
|
|
139
|
+
*
|
|
140
|
+
* On any other failure: the tempfile is unlinked and the error is
|
|
141
|
+
* rethrown unchanged.
|
|
142
|
+
*
|
|
143
|
+
* POSIX `link(2)` is available on Linux + macOS + WSL. Devstack is
|
|
144
|
+
* POSIX-only at runtime.
|
|
145
|
+
*/
|
|
146
|
+
const atomicWriteFileExclusiveSync = (path, bytes, options = {}) => {
|
|
147
|
+
const tmp = writeAndFsyncTempSync(path, bytes, options.mode ?? 384, options.parentMode ?? 448);
|
|
148
|
+
try {
|
|
149
|
+
linkSync(tmp, path);
|
|
150
|
+
} finally {
|
|
151
|
+
unlinkBestEffort(tmp);
|
|
152
|
+
}
|
|
88
153
|
};
|
|
89
154
|
/**
|
|
90
155
|
* Synchronous atomic JSON write. Stringifies `value` and routes
|
|
@@ -101,6 +166,6 @@ const dirname = (p) => {
|
|
|
101
166
|
return i <= 0 ? "/" : p.slice(0, i);
|
|
102
167
|
};
|
|
103
168
|
//#endregion
|
|
104
|
-
export { atomicWriteFile, atomicWriteJson, atomicWriteJsonSync };
|
|
169
|
+
export { atomicWriteFile, atomicWriteFileExclusiveSync, atomicWriteFileSync, atomicWriteJson, atomicWriteJsonSync };
|
|
105
170
|
|
|
106
171
|
//# sourceMappingURL=atomic-write.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"atomic-write.mjs","names":[],"sources":["../../../src/substrate/runtime/atomic-write.ts"],"sourcesContent":["// Atomic write primitive — ONE canonical implementation.\n//\n// Architecture § \"What's collapsed\" — three tempfile+rename impls\n// (atomic-write, state-store, global registry) collapse to one. This\n// is that one.\n//\n// Contract:\n// 1. Ensure parent directory exists.\n// 2. Open `<final>.tmp.<pid>.<rand>` with `wx` (O_EXCL) — refuse\n// to clobber a half-written sibling from a crashed earlier\n// writer in the same pid.\n// 3. Write bytes.\n// 4. `fsync` the file.\n// 5. Rename tempfile → final. Rename on POSIX is atomic w.r.t.\n// `open` of the final path.\n// 6. On any failure mid-flight, unlink the tempfile. Best-effort:\n// a tempfile-cleanup failure does NOT mask the original error.\n//\n// Two surfaces:\n// - `atomicWriteFile` / `atomicWriteJson` — Effect/FileSystem-based,\n// used by every async write site (manifest, state-store, cache).\n// - `atomicWriteFileSync` / `atomicWriteJsonSync` — node:fs-sync,\n// used by the cross-process modules (roster, snapshot-reservation,\n// stack-lock) that hold `stack.lock` and must keep their critical\n// section non-yielding. Substrate-fix-plan #11 tracks unifying\n// these onto Effect FS once we lift the cross-process modules off\n// `node:fs`; until then both surfaces share THIS file (and only\n// this file) so the tempfile dance has ONE owner.\n//\n// What we do NOT do here:\n// - fsync the parent directory. Linux's man fsync(2) suggests it\n// for full durability after rename(); the Effect platform layer\n// does not expose dir-fsync. Documented limitation — recoverable\n// on crash because the state-store rewrites on every change and\n// cache misses re-produce.\n\nimport {\n\tcloseSync,\n\tfsyncSync,\n\tmkdirSync,\n\topenSync,\n\trenameSync,\n\tunlinkSync,\n\twriteFileSync,\n} from 'node:fs';\nimport { randomUUID } from 'node:crypto';\n\nimport { Effect, FileSystem, Schema } from 'effect';\n\nimport { AtomicWriteFailed } from './errors.ts';\n\n/** 8-hex tempfile suffix. `crypto.randomUUID()` is collision-safe under\n * parallel callers within the same pid — replaces the\n * `Math.random()`-based suffix flagged in STYLE_GUIDE §17. */\nconst tempSuffix = (): string => randomUUID().replace(/-/g, '').slice(0, 8);\n\nconst failStage =\n\t(\n\t\tpath: string,\n\t\tstage: AtomicWriteFailed['stage'],\n\t): ((cause: unknown) => Effect.Effect<never, AtomicWriteFailed>) =>\n\t(cause) =>\n\t\tEffect.fail(new AtomicWriteFailed({ path, stage, cause }));\n\n/**\n * Atomically write `bytes` to `path`. The helper ensures\n * `dirname(path)` exists (recursive mkdir, idempotent).\n *\n * `mode` is the final file mode bits (default 0o600 — secret-safe\n * default; cache callers may bump to 0o644 if they want world-read).\n */\nexport const atomicWriteFile = (\n\tpath: string,\n\tbytes: Uint8Array,\n\toptions: { readonly mode?: number; readonly parentMode?: number } = {},\n): Effect.Effect<void, AtomicWriteFailed, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst mode = options.mode ?? 0o600;\n\t\tconst parentMode = options.parentMode ?? 0o700;\n\t\tconst tmp = `${path}.tmp.${process.pid}.${tempSuffix()}`;\n\t\t// 1. Ensure parent. recursive: true is idempotent under\n\t\t// concurrent callers — `mkdir -p` semantics.\n\t\tyield* fs\n\t\t\t.makeDirectory(dirname(path), { recursive: true, mode: parentMode })\n\t\t\t.pipe(Effect.catch(failStage(path, 'mkdir-parent')));\n\t\t// 2-4. Open tempfile with `wx` (O_EXCL), write, fsync, all\n\t\t// inside a Scope so the file handle closes on any path.\n\t\tyield* Effect.scoped(\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst file = yield* fs\n\t\t\t\t\t.open(tmp, { flag: 'wx', mode })\n\t\t\t\t\t.pipe(Effect.catch(failStage(path, 'open-temp')));\n\t\t\t\tyield* file.writeAll(bytes).pipe(Effect.catch(failStage(path, 'write')));\n\t\t\t\t// fsync — durability boundary. Without this the\n\t\t\t\t// rename can land but bytes can stay in the page\n\t\t\t\t// cache through a power loss.\n\t\t\t\tyield* file.sync.pipe(Effect.catch(failStage(path, 'fsync')));\n\t\t\t}),\n\t\t);\n\t\t// 5. Atomic rename. On failure unlink the tempfile so we\n\t\t// don't leak; unlink failure is ignored (the original\n\t\t// rename failure is the one that matters).\n\t\tyield* fs\n\t\t\t.rename(tmp, path)\n\t\t\t.pipe(\n\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\tfs\n\t\t\t\t\t\t.remove(tmp, { force: true })\n\t\t\t\t\t\t.pipe(Effect.ignore, Effect.andThen(failStage(path, 'rename')(cause))),\n\t\t\t\t),\n\t\t\t);\n\t}).pipe(Effect.withSpan('substrate.atomicWriteFile', { attributes: { path } }));\n\n/**\n * Atomically write a JSON value. Encodes via Schema (so the on-disk\n * shape is the schema's `Encoded` form, not the rich `Type`). Caller\n * supplies a schema whose `Type` matches `value`.\n */\nexport const atomicWriteJson = <A, I>(\n\tpath: string,\n\tschema: Schema.Codec<A, I>,\n\tvalue: A,\n\toptions?: { readonly mode?: number; readonly parentMode?: number },\n): Effect.Effect<void, AtomicWriteFailed, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst encoded = yield* Schema.encodeUnknownEffect(schema)(value).pipe(\n\t\t\tEffect.catch(failStage(path, 'encode')),\n\t\t);\n\t\tconst json = JSON.stringify(encoded, null, 2);\n\t\tconst bytes = new TextEncoder().encode(json);\n\t\tyield* atomicWriteFile(path, bytes, options);\n\t});\n\n// -----------------------------------------------------------------------------\n// Sync surface — for the cross-process modules that hold `stack.lock`\n// and must keep their critical section non-yielding. Same contract as\n// the Effect surface above.\n// -----------------------------------------------------------------------------\n\n/**\n * Synchronous atomic write. Same disk-side contract as\n * `atomicWriteFile` (mkdir-parent → O_EXCL temp → write → fsync →\n * rename). Returns `void` on success; throws on any failure with\n * `cause` set to the underlying `NodeJS.ErrnoException`.\n *\n * Used inside `Effect.try` by the cross-process modules; the caller\n * maps the thrown error to a typed plugin/runtime error\n * (`RosterIoError` / `SnapshotReservationIoError` / etc.).\n */\nexport const atomicWriteFileSync = (\n\tpath: string,\n\tbytes: Uint8Array | string,\n\toptions: { readonly mode?: number; readonly parentMode?: number } = {},\n): void => {\n\tconst mode = options.mode ?? 0o600;\n\tconst parentMode = options.parentMode ?? 0o700;\n\tconst tmp = `${path}.tmp.${process.pid}.${tempSuffix()}`;\n\tmkdirSync(dirname(path), { recursive: true, mode: parentMode });\n\t// O_EXCL via `flag: 'wx'`. writeFileSync handles open + write +\n\t// close in one call but does NOT fsync; do it manually so we\n\t// preserve the durability boundary the Effect surface has.\n\twriteFileSync(tmp, bytes, { flag: 'wx', mode });\n\t// fsync — open the file just to fsync the bytes through the page\n\t// cache. `writeFileSync` already closed the original fd.\n\tlet fd: number | null = null;\n\ttry {\n\t\tfd = openSync(tmp, 'r');\n\t\tfsyncSync(fd);\n\t} finally {\n\t\tif (fd !== null) closeSync(fd);\n\t}\n\ttry {\n\t\trenameSync(tmp, path);\n\t} catch (cause) {\n\t\t// Best-effort tempfile cleanup; the original rename failure is\n\t\t// the one we re-throw to the caller.\n\t\ttry {\n\t\t\tunlinkSync(tmp);\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t\tthrow cause;\n\t}\n};\n\n/**\n * Synchronous atomic JSON write. Stringifies `value` and routes\n * through `atomicWriteFileSync`. No Schema encoding here — the\n * cross-process modules round-trip plain JSON (their schemas are\n * Schema.Struct of primitives; `JSON.stringify` is sufficient and\n * keeps the sync path dependency-free of Effect's Schema effects).\n */\nexport const atomicWriteJsonSync = (\n\tpath: string,\n\tvalue: unknown,\n\toptions?: { readonly mode?: number; readonly parentMode?: number },\n): void => {\n\tconst json = JSON.stringify(value);\n\tatomicWriteFileSync(path, json, options);\n};\n\n// Local dirname — we don't yield Path service here because\n// atomic-write may be called outside of a path-service-having\n// context (tests, recovery scripts). Posix-only; devstack is\n// posix-only in practice.\nconst dirname = (p: string): string => {\n\tconst i = p.lastIndexOf('/');\n\treturn i <= 0 ? '/' : p.slice(0, i);\n};\n"],"mappings":";;;;;;;;AAsDA,MAAM,mBAA2B,YAAY,CAAC,QAAQ,MAAM,GAAG,CAAC,MAAM,GAAG,EAAE;AAE3E,MAAM,aAEJ,MACA,WAEA,UACA,OAAO,KAAK,IAAI,kBAAkB;CAAE;CAAM;CAAO;CAAO,CAAC,CAAC;;;;;;;;AAS5D,MAAa,mBACZ,MACA,OACA,UAAoE,EAAE,KAEtE,OAAO,IAAI,aAAa;CACvB,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,IAAI,GAAG,YAAY;AAGtD,QAAO,GACL,cAAc,QAAQ,KAAK,EAAE;EAAE,WAAW;EAAM,MAAM;EAAY,CAAC,CACnE,KAAK,OAAO,MAAM,UAAU,MAAM,eAAe,CAAC,CAAC;AAGrD,QAAO,OAAO,OACb,OAAO,IAAI,aAAa;EACvB,MAAM,OAAO,OAAO,GAClB,KAAK,KAAK;GAAE,MAAM;GAAM;GAAM,CAAC,CAC/B,KAAK,OAAO,MAAM,UAAU,MAAM,YAAY,CAAC,CAAC;AAClD,SAAO,KAAK,SAAS,MAAM,CAAC,KAAK,OAAO,MAAM,UAAU,MAAM,QAAQ,CAAC,CAAC;AAIxE,SAAO,KAAK,KAAK,KAAK,OAAO,MAAM,UAAU,MAAM,QAAQ,CAAC,CAAC;GAC5D,CACF;AAID,QAAO,GACL,OAAO,KAAK,KAAK,CACjB,KACA,OAAO,OAAO,UACb,GACE,OAAO,KAAK,EAAE,OAAO,MAAM,CAAC,CAC5B,KAAK,OAAO,QAAQ,OAAO,QAAQ,UAAU,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC,CACvE,CACD;EACD,CAAC,KAAK,OAAO,SAAS,6BAA6B,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC;;;;;;AAOhF,MAAa,mBACZ,MACA,QACA,OACA,YAEA,OAAO,IAAI,aAAa;CACvB,MAAM,UAAU,OAAO,OAAO,oBAAoB,OAAO,CAAC,MAAM,CAAC,KAChE,OAAO,MAAM,UAAU,MAAM,SAAS,CAAC,CACvC;CACD,MAAM,OAAO,KAAK,UAAU,SAAS,MAAM,EAAE;AAE7C,QAAO,gBAAgB,MADT,IAAI,aAAa,CAAC,OAAO,KACL,EAAE,QAAQ;EAC3C;;;;;;;;;;;AAkBH,MAAa,uBACZ,MACA,OACA,UAAoE,EAAE,KAC5D;CACV,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,IAAI,GAAG,YAAY;AACtD,WAAU,QAAQ,KAAK,EAAE;EAAE,WAAW;EAAM,MAAM;EAAY,CAAC;AAI/D,eAAc,KAAK,OAAO;EAAE,MAAM;EAAM;EAAM,CAAC;CAG/C,IAAI,KAAoB;AACxB,KAAI;AACH,OAAK,SAAS,KAAK,IAAI;AACvB,YAAU,GAAG;WACJ;AACT,MAAI,OAAO,KAAM,WAAU,GAAG;;AAE/B,KAAI;AACH,aAAW,KAAK,KAAK;UACb,OAAO;AAGf,MAAI;AACH,cAAW,IAAI;UACR;AAGR,QAAM;;;;;;;;;;AAWR,MAAa,uBACZ,MACA,OACA,YACU;AAEV,qBAAoB,MADP,KAAK,UAAU,MACE,EAAE,QAAQ;;AAOzC,MAAM,WAAW,MAAsB;CACtC,MAAM,IAAI,EAAE,YAAY,IAAI;AAC5B,QAAO,KAAK,IAAI,MAAM,EAAE,MAAM,GAAG,EAAE"}
|
|
1
|
+
{"version":3,"file":"atomic-write.mjs","names":[],"sources":["../../../src/substrate/runtime/atomic-write.ts"],"sourcesContent":["// Atomic write primitive — ONE canonical implementation.\n//\n// Architecture § \"What's collapsed\" — three tempfile+rename impls\n// (atomic-write, state-store, global registry) collapse to one. This\n// is that one.\n//\n// Contract:\n// 1. Ensure parent directory exists.\n// 2. Open `<final>.tmp.<pid>.<rand>` with `wx` (O_EXCL) — refuse\n// to clobber a half-written sibling from a crashed earlier\n// writer in the same pid.\n// 3. Write bytes.\n// 4. `fsync` the file.\n// 5. Rename tempfile → final. Rename on POSIX is atomic w.r.t.\n// `open` of the final path.\n// 6. On any failure mid-flight, unlink the tempfile. Best-effort:\n// a tempfile-cleanup failure does NOT mask the original error.\n//\n// Two surfaces:\n// - `atomicWriteFile` / `atomicWriteJson` — Effect/FileSystem-based,\n// used by every async write site (manifest, state-store, cache).\n// - `atomicWriteFileSync` / `atomicWriteJsonSync` — node:fs-sync,\n// used by the cross-process modules (roster, snapshot-reservation,\n// stack-lock) that hold `stack.lock` and must keep their critical\n// section non-yielding. Substrate-fix-plan #11 tracks unifying\n// these onto Effect FS once we lift the cross-process modules off\n// `node:fs`; until then both surfaces share THIS file (and only\n// this file) so the tempfile dance has ONE owner.\n//\n// What we do NOT do here:\n// - fsync the parent directory. Linux's man fsync(2) suggests it\n// for full durability after rename(); the Effect platform layer\n// does not expose dir-fsync. Documented limitation — recoverable\n// on crash because the state-store rewrites on every change and\n// cache misses re-produce.\n\nimport {\n\tcloseSync,\n\tfsyncSync,\n\tlinkSync,\n\tmkdirSync,\n\topenSync,\n\trenameSync,\n\tunlinkSync,\n\twriteFileSync,\n} from 'node:fs';\nimport { randomUUID } from 'node:crypto';\n\nimport { Effect, FileSystem, Schema } from 'effect';\n\nimport { selfPid } from './cross-process/self-pid.ts';\nimport { AtomicWriteFailed } from './errors.ts';\n\n/** 8-hex tempfile suffix. `crypto.randomUUID()` is collision-safe under\n * parallel callers within the same pid — replaces the\n * `Math.random()`-based suffix flagged in STYLE_GUIDE §17. */\nconst tempSuffix = (): string => randomUUID().replace(/-/g, '').slice(0, 8);\n\nconst failStage =\n\t(\n\t\tpath: string,\n\t\tstage: AtomicWriteFailed['stage'],\n\t): ((cause: unknown) => Effect.Effect<never, AtomicWriteFailed>) =>\n\t(cause) =>\n\t\tEffect.fail(new AtomicWriteFailed({ path, stage, cause }));\n\n/**\n * Atomically write `bytes` to `path`. The helper ensures\n * `dirname(path)` exists (recursive mkdir, idempotent).\n *\n * `mode` is the final file mode bits (default 0o600 — secret-safe\n * default; cache callers may bump to 0o644 if they want world-read).\n */\nexport const atomicWriteFile = (\n\tpath: string,\n\tbytes: Uint8Array,\n\toptions: { readonly mode?: number; readonly parentMode?: number } = {},\n): Effect.Effect<void, AtomicWriteFailed, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst mode = options.mode ?? 0o600;\n\t\tconst parentMode = options.parentMode ?? 0o700;\n\t\tconst tmp = `${path}.tmp.${selfPid()}.${tempSuffix()}`;\n\t\t// 1. Ensure parent. recursive: true is idempotent under\n\t\t// concurrent callers — `mkdir -p` semantics.\n\t\tyield* fs\n\t\t\t.makeDirectory(dirname(path), { recursive: true, mode: parentMode })\n\t\t\t.pipe(Effect.catch(failStage(path, 'mkdir-parent')));\n\t\t// 2-5. Write + rename, with unconditional tempfile cleanup on\n\t\t// ANY failure path (open-temp / write / fsync / rename).\n\t\t// `force: true` makes the unlink a no-op if the file\n\t\t// doesn't exist (open-temp failed early, or rename moved\n\t\t// it to its final home).\n\t\tyield* Effect.gen(function* () {\n\t\t\tyield* Effect.scoped(\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst file = yield* fs\n\t\t\t\t\t\t.open(tmp, { flag: 'wx', mode })\n\t\t\t\t\t\t.pipe(Effect.catch(failStage(path, 'open-temp')));\n\t\t\t\t\tyield* file.writeAll(bytes).pipe(Effect.catch(failStage(path, 'write')));\n\t\t\t\t\t// fsync — durability boundary. Without this the\n\t\t\t\t\t// rename can land but bytes can stay in the page\n\t\t\t\t\t// cache through a power loss.\n\t\t\t\t\tyield* file.sync.pipe(Effect.catch(failStage(path, 'fsync')));\n\t\t\t\t}),\n\t\t\t);\n\t\t\tyield* fs.rename(tmp, path).pipe(Effect.catch(failStage(path, 'rename')));\n\t\t}).pipe(Effect.onError(() => fs.remove(tmp, { force: true }).pipe(Effect.ignore)));\n\t}).pipe(Effect.withSpan('substrate.atomicWriteFile', { attributes: { path } }));\n\n/**\n * Atomically write a JSON value. Encodes via Schema (so the on-disk\n * shape is the schema's `Encoded` form, not the rich `Type`). Caller\n * supplies a schema whose `Type` matches `value`.\n */\nexport const atomicWriteJson = <A, I>(\n\tpath: string,\n\tschema: Schema.Codec<A, I>,\n\tvalue: A,\n\toptions?: { readonly mode?: number; readonly parentMode?: number },\n): Effect.Effect<void, AtomicWriteFailed, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst encoded = yield* Schema.encodeUnknownEffect(schema)(value).pipe(\n\t\t\tEffect.catch(failStage(path, 'encode')),\n\t\t);\n\t\tconst json = JSON.stringify(encoded, null, 2);\n\t\tconst bytes = new TextEncoder().encode(json);\n\t\tyield* atomicWriteFile(path, bytes, options);\n\t});\n\n// -----------------------------------------------------------------------------\n// Sync surface — for the cross-process modules that hold `stack.lock`\n// and must keep their critical section non-yielding. Same contract as\n// the Effect surface above.\n// -----------------------------------------------------------------------------\n\n/**\n * Internal: mkdir-parent → O_EXCL temp → write → fsync. Returns the\n * tempfile path on success; throws on any failure (and unlinks the\n * tempfile if it managed to land). The temp exists after return —\n * the caller is responsible for moving it to its final home (rename\n * for clobber semantics, link for exclusive-create semantics) AND\n * for unlinking the temp afterwards.\n *\n * Centralizes the durability boundary so both `atomicWriteFileSync`\n * (rename) and `atomicWriteFileExclusiveSync` (link+unlink) share\n * one tempfile/fsync implementation.\n */\nconst writeAndFsyncTempSync = (\n\tfinalPath: string,\n\tbytes: Uint8Array | string,\n\tmode: number,\n\tparentMode: number,\n): string => {\n\tconst tmp = `${finalPath}.tmp.${selfPid()}.${tempSuffix()}`;\n\tmkdirSync(dirname(finalPath), { recursive: true, mode: parentMode });\n\t// O_EXCL via `flag: 'wx'`. writeFileSync handles open + write +\n\t// close in one call but does NOT fsync; do it manually so we\n\t// preserve the durability boundary the Effect surface has.\n\twriteFileSync(tmp, bytes, { flag: 'wx', mode });\n\t// Tempfile exists from here — fsync failure must unlink before\n\t// rethrowing so a crashed-mid-flight writer doesn't leak a\n\t// half-written sibling.\n\ttry {\n\t\tlet fd: number | null = null;\n\t\ttry {\n\t\t\tfd = openSync(tmp, 'r');\n\t\t\tfsyncSync(fd);\n\t\t} finally {\n\t\t\tif (fd !== null) closeSync(fd);\n\t\t}\n\t} catch (cause) {\n\t\ttry {\n\t\t\tunlinkSync(tmp);\n\t\t} catch {\n\t\t\t// ignore — best-effort\n\t\t}\n\t\tthrow cause;\n\t}\n\treturn tmp;\n};\n\n/** Best-effort unlink. Swallows errors — used in cleanup paths where\n * a failure to remove the temp should NOT mask the original error. */\nconst unlinkBestEffort = (path: string): void => {\n\ttry {\n\t\tunlinkSync(path);\n\t} catch {\n\t\t// ignore\n\t}\n};\n\n/**\n * Synchronous atomic write. Same disk-side contract as\n * `atomicWriteFile` (mkdir-parent → O_EXCL temp → write → fsync →\n * rename). Returns `void` on success; throws on any failure with\n * `cause` set to the underlying `NodeJS.ErrnoException`.\n *\n * Rename CLOBBERS a pre-existing file at the final path. Callers\n * that need exclusive-create semantics (\"fail if the final path\n * already exists, atomically\") want `atomicWriteFileExclusiveSync`\n * instead — that surface uses `linkSync` which is POSIX-atomic and\n * fails with `EEXIST` rather than overwriting.\n *\n * Used inside `Effect.try` by the cross-process modules; the caller\n * maps the thrown error to a typed plugin/runtime error\n * (`RosterIoError` / `SnapshotReservationIoError` / etc.).\n */\nexport const atomicWriteFileSync = (\n\tpath: string,\n\tbytes: Uint8Array | string,\n\toptions: { readonly mode?: number; readonly parentMode?: number } = {},\n): void => {\n\tconst mode = options.mode ?? 0o600;\n\tconst parentMode = options.parentMode ?? 0o700;\n\tconst tmp = writeAndFsyncTempSync(path, bytes, mode, parentMode);\n\tlet renamed = false;\n\ttry {\n\t\trenameSync(tmp, path);\n\t\trenamed = true;\n\t} finally {\n\t\tif (!renamed) unlinkBestEffort(tmp);\n\t}\n};\n\n/**\n * Synchronous atomic EXCLUSIVE-CREATE write. Same disk-side contract\n * as `atomicWriteFileSync` (mkdir-parent → O_EXCL temp → write →\n * fsync) but uses `linkSync(tmp, final)` instead of `renameSync` for\n * the final installation step. `link` is POSIX-atomic and FAILS with\n * `EEXIST` if the target already exists — closing the rename-clobber\n * race that `existsSync` + `renameSync` cannot.\n *\n * On success: the tempfile is unlinked (the hard link satisfies the\n * final path).\n *\n * On `EEXIST`: the tempfile is unlinked and the underlying\n * `NodeJS.ErrnoException` (with `code === 'EEXIST'`) is rethrown so\n * the caller can read the winning final-path and decide what to do.\n *\n * On any other failure: the tempfile is unlinked and the error is\n * rethrown unchanged.\n *\n * POSIX `link(2)` is available on Linux + macOS + WSL. Devstack is\n * POSIX-only at runtime.\n */\nexport const atomicWriteFileExclusiveSync = (\n\tpath: string,\n\tbytes: Uint8Array | string,\n\toptions: { readonly mode?: number; readonly parentMode?: number } = {},\n): void => {\n\tconst mode = options.mode ?? 0o600;\n\tconst parentMode = options.parentMode ?? 0o700;\n\tconst tmp = writeAndFsyncTempSync(path, bytes, mode, parentMode);\n\ttry {\n\t\t// Atomic exclusive-create. On EEXIST the tempfile remains and\n\t\t// the finally below unlinks it; the EEXIST error propagates.\n\t\tlinkSync(tmp, path);\n\t} finally {\n\t\t// Always unlink the tempfile: on success the hard link IS the\n\t\t// final file; on failure (EEXIST or otherwise) the tempfile\n\t\t// should not leak.\n\t\tunlinkBestEffort(tmp);\n\t}\n};\n\n/**\n * Synchronous atomic JSON write. Stringifies `value` and routes\n * through `atomicWriteFileSync`. No Schema encoding here — the\n * cross-process modules round-trip plain JSON (their schemas are\n * Schema.Struct of primitives; `JSON.stringify` is sufficient and\n * keeps the sync path dependency-free of Effect's Schema effects).\n */\nexport const atomicWriteJsonSync = (\n\tpath: string,\n\tvalue: unknown,\n\toptions?: { readonly mode?: number; readonly parentMode?: number },\n): void => {\n\tconst json = JSON.stringify(value);\n\tatomicWriteFileSync(path, json, options);\n};\n\n// Local dirname — we don't yield Path service here because\n// atomic-write may be called outside of a path-service-having\n// context (tests, recovery scripts). Posix-only; devstack is\n// posix-only in practice.\nconst dirname = (p: string): string => {\n\tconst i = p.lastIndexOf('/');\n\treturn i <= 0 ? '/' : p.slice(0, i);\n};\n"],"mappings":";;;;;;;;;AAwDA,MAAM,mBAA2B,YAAY,CAAC,QAAQ,MAAM,GAAG,CAAC,MAAM,GAAG,EAAE;AAE3E,MAAM,aAEJ,MACA,WAEA,UACA,OAAO,KAAK,IAAI,kBAAkB;CAAE;CAAM;CAAO;CAAO,CAAC,CAAC;;;;;;;;AAS5D,MAAa,mBACZ,MACA,OACA,UAAoE,EAAE,KAEtE,OAAO,IAAI,aAAa;CACvB,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,MAAM,GAAG,KAAK,OAAO,SAAS,CAAC,GAAG,YAAY;AAGpD,QAAO,GACL,cAAc,QAAQ,KAAK,EAAE;EAAE,WAAW;EAAM,MAAM;EAAY,CAAC,CACnE,KAAK,OAAO,MAAM,UAAU,MAAM,eAAe,CAAC,CAAC;AAMrD,QAAO,OAAO,IAAI,aAAa;AAC9B,SAAO,OAAO,OACb,OAAO,IAAI,aAAa;GACvB,MAAM,OAAO,OAAO,GAClB,KAAK,KAAK;IAAE,MAAM;IAAM;IAAM,CAAC,CAC/B,KAAK,OAAO,MAAM,UAAU,MAAM,YAAY,CAAC,CAAC;AAClD,UAAO,KAAK,SAAS,MAAM,CAAC,KAAK,OAAO,MAAM,UAAU,MAAM,QAAQ,CAAC,CAAC;AAIxE,UAAO,KAAK,KAAK,KAAK,OAAO,MAAM,UAAU,MAAM,QAAQ,CAAC,CAAC;IAC5D,CACF;AACD,SAAO,GAAG,OAAO,KAAK,KAAK,CAAC,KAAK,OAAO,MAAM,UAAU,MAAM,SAAS,CAAC,CAAC;GACxE,CAAC,KAAK,OAAO,cAAc,GAAG,OAAO,KAAK,EAAE,OAAO,MAAM,CAAC,CAAC,KAAK,OAAO,OAAO,CAAC,CAAC;EACjF,CAAC,KAAK,OAAO,SAAS,6BAA6B,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC;;;;;;AAOhF,MAAa,mBACZ,MACA,QACA,OACA,YAEA,OAAO,IAAI,aAAa;CACvB,MAAM,UAAU,OAAO,OAAO,oBAAoB,OAAO,CAAC,MAAM,CAAC,KAChE,OAAO,MAAM,UAAU,MAAM,SAAS,CAAC,CACvC;CACD,MAAM,OAAO,KAAK,UAAU,SAAS,MAAM,EAAE;AAE7C,QAAO,gBAAgB,MADT,IAAI,aAAa,CAAC,OAAO,KACL,EAAE,QAAQ;EAC3C;;;;;;;;;;;;;AAoBH,MAAM,yBACL,WACA,OACA,MACA,eACY;CACZ,MAAM,MAAM,GAAG,UAAU,OAAO,SAAS,CAAC,GAAG,YAAY;AACzD,WAAU,QAAQ,UAAU,EAAE;EAAE,WAAW;EAAM,MAAM;EAAY,CAAC;AAIpE,eAAc,KAAK,OAAO;EAAE,MAAM;EAAM;EAAM,CAAC;AAI/C,KAAI;EACH,IAAI,KAAoB;AACxB,MAAI;AACH,QAAK,SAAS,KAAK,IAAI;AACvB,aAAU,GAAG;YACJ;AACT,OAAI,OAAO,KAAM,WAAU,GAAG;;UAEvB,OAAO;AACf,MAAI;AACH,cAAW,IAAI;UACR;AAGR,QAAM;;AAEP,QAAO;;;;AAKR,MAAM,oBAAoB,SAAuB;AAChD,KAAI;AACH,aAAW,KAAK;SACT;;;;;;;;;;;;;;;;;;AAqBT,MAAa,uBACZ,MACA,OACA,UAAoE,EAAE,KAC5D;CAGV,MAAM,MAAM,sBAAsB,MAAM,OAF3B,QAAQ,QAAQ,KACV,QAAQ,cAAc,IACuB;CAChE,IAAI,UAAU;AACd,KAAI;AACH,aAAW,KAAK,KAAK;AACrB,YAAU;WACD;AACT,MAAI,CAAC,QAAS,kBAAiB,IAAI;;;;;;;;;;;;;;;;;;;;;;;;AAyBrC,MAAa,gCACZ,MACA,OACA,UAAoE,EAAE,KAC5D;CAGV,MAAM,MAAM,sBAAsB,MAAM,OAF3B,QAAQ,QAAQ,KACV,QAAQ,cAAc,IACuB;AAChE,KAAI;AAGH,WAAS,KAAK,KAAK;WACV;AAIT,mBAAiB,IAAI;;;;;;;;;;AAWvB,MAAa,uBACZ,MACA,OACA,YACU;AAEV,qBAAoB,MADP,KAAK,UAAU,MACE,EAAE,QAAQ;;AAOzC,MAAM,WAAW,MAAsB;CACtC,MAAM,IAAI,EAAE,YAAY,IAAI;AAC5B,QAAO,KAAK,IAAI,MAAM,EAAE,MAAM,GAAG,EAAE"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { versionedDocSchema } from "../../versioned-doc-schema.mjs";
|
|
1
2
|
import { Schema } from "effect";
|
|
2
3
|
//#region src/substrate/runtime/cache/schema.ts
|
|
3
4
|
/** Versioned envelope. Schema-decode failure on read is treated as
|
|
4
5
|
* a cache MISS (best-effort cache contract — corruption recovers
|
|
5
6
|
* by re-producing the artifact). */
|
|
6
|
-
const CacheEntryDoc =
|
|
7
|
-
version: Schema.Literal(1),
|
|
7
|
+
const CacheEntryDoc = versionedDocSchema(1, {
|
|
8
8
|
/** Bytes, base64-encoded. Chosen over hex because typical
|
|
9
9
|
* artifacts are larger (image manifests, package digests) and
|
|
10
10
|
* base64 is 33% overhead vs hex's 100%. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.mjs","names":[],"sources":["../../../../src/substrate/runtime/cache/schema.ts"],"sourcesContent":["// On-disk shape for one cache entry.\n//\n// One file per `(namespace, chain, contentHash)` triple, at\n// `<runtime-root>/stacks/<stack>/cache/<namespace>/<chain>/<contentHash>.json`.\n//\n// The entry envelope wraps the bytes (base64-encoded — JSON has no\n// binary literal) and the metadata renderer / verify needs. Cache\n// callers don't see the envelope; they only see `CacheEntry` from\n// `primitives/cache.ts` (bytes + writtenAt).\n\nimport { Schema } from 'effect';\n\n/** Versioned envelope. Schema-decode failure on read is treated as\n * a cache MISS (best-effort cache contract — corruption recovers\n * by re-producing the artifact). */\nexport const CacheEntryDoc =
|
|
1
|
+
{"version":3,"file":"schema.mjs","names":[],"sources":["../../../../src/substrate/runtime/cache/schema.ts"],"sourcesContent":["// On-disk shape for one cache entry.\n//\n// One file per `(namespace, chain, contentHash)` triple, at\n// `<runtime-root>/stacks/<stack>/cache/<namespace>/<chain>/<contentHash>.json`.\n//\n// The entry envelope wraps the bytes (base64-encoded — JSON has no\n// binary literal) and the metadata renderer / verify needs. Cache\n// callers don't see the envelope; they only see `CacheEntry` from\n// `primitives/cache.ts` (bytes + writtenAt).\n\nimport { Schema } from 'effect';\n\nimport { versionedDocSchema } from '../../versioned-doc-schema.ts';\n\n/** Versioned envelope. Schema-decode failure on read is treated as\n * a cache MISS (best-effort cache contract — corruption recovers\n * by re-producing the artifact). */\nexport const CacheEntryDoc = versionedDocSchema(1, {\n\t/** Bytes, base64-encoded. Chosen over hex because typical\n\t * artifacts are larger (image manifests, package digests) and\n\t * base64 is 33% overhead vs hex's 100%. */\n\tbytes: Schema.String,\n\t/** Unix millis of the write. Renderers may show \"produced 5s\n\t * ago\"; substrate uses it only for diagnostics. */\n\twrittenAt: Schema.Number,\n\t/** Optional namespace-private hint (e.g. \"image-tag the cached\n\t * blob refers to\"). Substrate stores opaquely. */\n\thint: Schema.optional(Schema.String),\n});\nexport type CacheEntryDoc = typeof CacheEntryDoc.Type;\n"],"mappings":";;;;;;AAiBA,MAAa,gBAAgB,mBAAmB,GAAG;;;;CAIlD,OAAO,OAAO;;;CAGd,WAAW,OAAO;;;CAGlB,MAAM,OAAO,SAAS,OAAO,OAAO;CACpC,CAAC"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { StackPathsService } from "../paths.mjs";
|
|
2
|
+
import { SpanAttr } from "../observability/spans.mjs";
|
|
2
3
|
import { CacheError } from "../errors.mjs";
|
|
4
|
+
import { atomicWriteJson } from "../atomic-write.mjs";
|
|
3
5
|
import { decodeJsonText } from "../runtime-decode.mjs";
|
|
4
6
|
import { CacheEntryDoc } from "./schema.mjs";
|
|
5
|
-
import { atomicWriteJson } from "../atomic-write.mjs";
|
|
6
7
|
import { Context, Effect, FileSystem, Layer } from "effect";
|
|
7
8
|
//#region src/substrate/runtime/cache/service.ts
|
|
8
9
|
const base64Encode = (bytes) => Buffer.from(bytes).toString("base64");
|
|
9
10
|
const base64Decode = (s) => new Uint8Array(Buffer.from(s, "base64"));
|
|
10
|
-
var CacheService = class extends Context.Service()("@devstack
|
|
11
|
+
var CacheService = class extends Context.Service()("@devstack/substrate/Cache") {};
|
|
11
12
|
/**
|
|
12
13
|
* Cache Layer. Stateless beyond the path resolver and the platform
|
|
13
14
|
* FileSystem — every read goes to disk (no in-memory hot cache; the
|
|
@@ -26,16 +27,17 @@ const layerCache = Layer.effect(CacheService, Effect.gen(function* () {
|
|
|
26
27
|
detail: `failed to stat cache entry: ${file}`,
|
|
27
28
|
cause
|
|
28
29
|
})))))) return null;
|
|
29
|
-
const
|
|
30
|
+
const doc = yield* decodeJsonText(CacheEntryDoc, yield* fs.readFileString(file).pipe(Effect.catch((cause) => Effect.fail(new CacheError({
|
|
30
31
|
reason: "io-failed",
|
|
31
32
|
detail: `failed to read cache entry: ${file}`,
|
|
32
33
|
cause
|
|
33
|
-
}))))
|
|
34
|
-
const annotateCorruption = Effect.annotateCurrentSpan({ "cache.corruption": true });
|
|
35
|
-
const doc = yield* decodeJsonText(CacheEntryDoc, text, {
|
|
34
|
+
})))), {
|
|
36
35
|
source: file,
|
|
37
36
|
mkError: (issue) => issue
|
|
38
|
-
}).pipe(Effect.
|
|
37
|
+
}).pipe(Effect.tapCause((cause) => Effect.logDebug("cache entry decode failed; treating as miss", {
|
|
38
|
+
file,
|
|
39
|
+
cause
|
|
40
|
+
})), Effect.catch(() => Effect.annotateCurrentSpan({ [SpanAttr.cacheCorruption]: true }).pipe(Effect.as(null))));
|
|
39
41
|
if (doc === null) return null;
|
|
40
42
|
return {
|
|
41
43
|
bytes: base64Decode(doc.bytes),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.mjs","names":[],"sources":["../../../../src/substrate/runtime/cache/service.ts"],"sourcesContent":["// Cache implementation.\n//\n// Universal artifact cache, content-addressed by\n// `(namespace, chainId, contentHash)`. Per-plugin namespace; the\n// substrate folds chainId in.\n//\n// Semantics:\n//\n// - `lookup` is the precise \"is the entry present?\" query. Hit\n// returns `CacheEntry`; miss returns `null`. Schema-decode\n// failure (corruption) is treated as a MISS, not an error —\n// re-producing the artifact is always safe; surfacing decode\n// failures would force every caller to handle a recovery path\n// they don't need. The corruption is logged via the span.\n// - `write` is best-effort. The architecture is explicit: an\n// ArtifactPublisher commits the on-chain effect, THEN\n// writes the cache; a cache-write IO failure must NOT roll back\n// the on-chain effect. We surface write failures via the\n// `CacheError` channel so callers can opt into reporting them,\n// but the typical caller pipes `Effect.ignore` over the write.\n// - `delete` is a best-effort sweep.\n//\n// Tombstones: the cache does NOT use tombstones. Missing-vs-deleted\n// is meaningless for a content-addressed best-effort store —\n// re-producing the artifact has the same effect either way.\n\nimport { Context, Effect, FileSystem, Layer } from 'effect';\n\nimport type { Cache, CacheEntry, CacheKey } from '../../../primitives/cache.ts';\nimport { atomicWriteJson } from '../atomic-write.ts';\nimport { CacheError } from '../errors.ts';\nimport { StackPathsService } from '../paths.ts';\nimport { decodeJsonText } from '../runtime-decode.ts';\nimport { CacheEntryDoc } from './schema.ts';\n\nconst base64Encode = (bytes: Uint8Array): string => Buffer.from(bytes).toString('base64');\nconst base64Decode = (s: string): Uint8Array => new Uint8Array(Buffer.from(s, 'base64'));\n\nexport class CacheService extends Context.Service<CacheService, Cache>()(\n\t'@devstack
|
|
1
|
+
{"version":3,"file":"service.mjs","names":[],"sources":["../../../../src/substrate/runtime/cache/service.ts"],"sourcesContent":["// Cache implementation.\n//\n// Universal artifact cache, content-addressed by\n// `(namespace, chainId, contentHash)`. Per-plugin namespace; the\n// substrate folds chainId in.\n//\n// Semantics:\n//\n// - `lookup` is the precise \"is the entry present?\" query. Hit\n// returns `CacheEntry`; miss returns `null`. Schema-decode\n// failure (corruption) is treated as a MISS, not an error —\n// re-producing the artifact is always safe; surfacing decode\n// failures would force every caller to handle a recovery path\n// they don't need. The corruption is logged via the span.\n// - `write` is best-effort. The architecture is explicit: an\n// ArtifactPublisher commits the on-chain effect, THEN\n// writes the cache; a cache-write IO failure must NOT roll back\n// the on-chain effect. We surface write failures via the\n// `CacheError` channel so callers can opt into reporting them,\n// but the typical caller pipes `Effect.ignore` over the write.\n// - `delete` is a best-effort sweep.\n//\n// Tombstones: the cache does NOT use tombstones. Missing-vs-deleted\n// is meaningless for a content-addressed best-effort store —\n// re-producing the artifact has the same effect either way.\n\nimport { Context, Effect, FileSystem, Layer } from 'effect';\n\nimport type { Cache, CacheEntry, CacheKey } from '../../../primitives/cache.ts';\nimport { atomicWriteJson } from '../atomic-write.ts';\nimport { CacheError } from '../errors.ts';\nimport { SpanAttr } from '../observability/spans.ts';\nimport { StackPathsService } from '../paths.ts';\nimport { decodeJsonText } from '../runtime-decode.ts';\nimport { CacheEntryDoc } from './schema.ts';\n\nconst base64Encode = (bytes: Uint8Array): string => Buffer.from(bytes).toString('base64');\nconst base64Decode = (s: string): Uint8Array => new Uint8Array(Buffer.from(s, 'base64'));\n\nexport class CacheService extends Context.Service<CacheService, Cache>()(\n\t'@devstack/substrate/Cache',\n) {}\n\n/**\n * Cache Layer. Stateless beyond the path resolver and the platform\n * FileSystem — every read goes to disk (no in-memory hot cache; the\n * OS page cache is enough). If a future perf pass shows the\n * substrate is hot-path-reading the same entries repeatedly, fold\n * an LRU in here; today's call-sites read once at acquire and never\n * again.\n */\nexport const layerCache: Layer.Layer<\n\tCacheService,\n\tnever,\n\tFileSystem.FileSystem | StackPathsService\n> = Layer.effect(\n\tCacheService,\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst paths = yield* StackPathsService;\n\n\t\tconst lookup = (key: CacheKey): Effect.Effect<CacheEntry | null, CacheError> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst { file } = paths.cacheEntry(key.namespace, key.chain, key.contentHash);\n\t\t\t\tconst exists = yield* fs.exists(file).pipe(\n\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\t\tnew CacheError({\n\t\t\t\t\t\t\t\treason: 'io-failed',\n\t\t\t\t\t\t\t\tdetail: `failed to stat cache entry: ${file}`,\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tif (!exists) return null;\n\t\t\t\tconst text = yield* fs.readFileString(file).pipe(\n\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\t\tnew CacheError({\n\t\t\t\t\t\t\t\treason: 'io-failed',\n\t\t\t\t\t\t\t\tdetail: `failed to read cache entry: ${file}`,\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\t// Corruption = miss. The cache contract is\n\t\t\t\t// best-effort; surfacing decode failures would force\n\t\t\t\t// every caller to handle a recovery path that's\n\t\t\t\t// already implicit (re-produce). We annotate the span\n\t\t\t\t// AND emit a `logDebug` so the rare corruption is\n\t\t\t\t// visible in the log stream, not only via span backend.\n\t\t\t\tconst doc = yield* decodeJsonText(CacheEntryDoc, text, {\n\t\t\t\t\tsource: file,\n\t\t\t\t\tmkError: (issue) => issue,\n\t\t\t\t}).pipe(\n\t\t\t\t\tEffect.tapCause((cause) =>\n\t\t\t\t\t\tEffect.logDebug('cache entry decode failed; treating as miss', {\n\t\t\t\t\t\t\tfile,\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.catch(() =>\n\t\t\t\t\t\tEffect.annotateCurrentSpan({ [SpanAttr.cacheCorruption]: true }).pipe(\n\t\t\t\t\t\t\tEffect.as(null as CacheEntryDoc | null),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tif (doc === null) return null;\n\t\t\t\treturn {\n\t\t\t\t\tbytes: base64Decode(doc.bytes),\n\t\t\t\t\twrittenAt: doc.writtenAt,\n\t\t\t\t};\n\t\t\t}).pipe(\n\t\t\t\tEffect.withSpan('substrate.cache.lookup', {\n\t\t\t\t\tattributes: {\n\t\t\t\t\t\tnamespace: key.namespace,\n\t\t\t\t\t\tchain: key.chain,\n\t\t\t\t\t\tcontentHash: key.contentHash,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t);\n\n\t\tconst write = (key: CacheKey, bytes: Uint8Array): Effect.Effect<void, CacheError> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst { file } = paths.cacheEntry(key.namespace, key.chain, key.contentHash);\n\t\t\t\tconst doc: CacheEntryDoc = {\n\t\t\t\t\tversion: 1,\n\t\t\t\t\tbytes: base64Encode(bytes),\n\t\t\t\t\twrittenAt: Date.now(),\n\t\t\t\t};\n\t\t\t\tyield* atomicWriteJson(file, CacheEntryDoc, doc, {\n\t\t\t\t\tmode: 0o644,\n\t\t\t\t\tparentMode: 0o755,\n\t\t\t\t}).pipe(\n\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\t\tnew CacheError({\n\t\t\t\t\t\t\t\treason: 'io-failed',\n\t\t\t\t\t\t\t\tdetail: `cache write failed: ${file}`,\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.provideService(FileSystem.FileSystem, fs),\n\t\t\t\t);\n\t\t\t}).pipe(\n\t\t\t\tEffect.withSpan('substrate.cache.write', {\n\t\t\t\t\tattributes: {\n\t\t\t\t\t\tnamespace: key.namespace,\n\t\t\t\t\t\tchain: key.chain,\n\t\t\t\t\t\tcontentHash: key.contentHash,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t);\n\n\t\tconst remove = (key: CacheKey): Effect.Effect<void, CacheError> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst { file } = paths.cacheEntry(key.namespace, key.chain, key.contentHash);\n\t\t\t\tyield* fs.remove(file, { force: true }).pipe(\n\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\t\tnew CacheError({\n\t\t\t\t\t\t\t\treason: 'io-failed',\n\t\t\t\t\t\t\t\tdetail: `cache delete failed: ${file}`,\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}).pipe(\n\t\t\t\tEffect.withSpan('substrate.cache.delete', {\n\t\t\t\t\tattributes: {\n\t\t\t\t\t\tnamespace: key.namespace,\n\t\t\t\t\t\tchain: key.chain,\n\t\t\t\t\t\tcontentHash: key.contentHash,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t);\n\n\t\treturn CacheService.of({\n\t\t\tlookup,\n\t\t\twrite,\n\t\t\tdelete: remove,\n\t\t});\n\t}),\n);\n"],"mappings":";;;;;;;;AAoCA,MAAM,gBAAgB,UAA8B,OAAO,KAAK,MAAM,CAAC,SAAS,SAAS;AACzF,MAAM,gBAAgB,MAA0B,IAAI,WAAW,OAAO,KAAK,GAAG,SAAS,CAAC;AAExF,IAAa,eAAb,cAAkC,QAAQ,SAA8B,CACvE,4BACA,CAAC;;;;;;;;;AAUF,MAAa,aAIT,MAAM,OACT,cACA,OAAO,IAAI,aAAa;CACvB,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,QAAQ,OAAO;CAErB,MAAM,UAAU,QACf,OAAO,IAAI,aAAa;EACvB,MAAM,EAAE,SAAS,MAAM,WAAW,IAAI,WAAW,IAAI,OAAO,IAAI,YAAY;AAY5E,MAAI,EAAC,OAXiB,GAAG,OAAO,KAAK,CAAC,KACrC,OAAO,OAAO,UACb,OAAO,KACN,IAAI,WAAW;GACd,QAAQ;GACR,QAAQ,+BAA+B;GACvC;GACA,CAAC,CACF,CACD,CACD,EACY,QAAO;EAkBpB,MAAM,MAAM,OAAO,eAAe,eAAe,OAjB7B,GAAG,eAAe,KAAK,CAAC,KAC3C,OAAO,OAAO,UACb,OAAO,KACN,IAAI,WAAW;GACd,QAAQ;GACR,QAAQ,+BAA+B;GACvC;GACA,CAAC,CACF,CACD,CACD,EAOsD;GACtD,QAAQ;GACR,UAAU,UAAU;GACpB,CAAC,CAAC,KACF,OAAO,UAAU,UAChB,OAAO,SAAS,+CAA+C;GAC9D;GACA;GACA,CAAC,CACF,EACD,OAAO,YACN,OAAO,oBAAoB,GAAG,SAAS,kBAAkB,MAAM,CAAC,CAAC,KAChE,OAAO,GAAG,KAA6B,CACvC,CACD,CACD;AACD,MAAI,QAAQ,KAAM,QAAO;AACzB,SAAO;GACN,OAAO,aAAa,IAAI,MAAM;GAC9B,WAAW,IAAI;GACf;GACA,CAAC,KACF,OAAO,SAAS,0BAA0B,EACzC,YAAY;EACX,WAAW,IAAI;EACf,OAAO,IAAI;EACX,aAAa,IAAI;EACjB,EACD,CAAC,CACF;CAEF,MAAM,SAAS,KAAe,UAC7B,OAAO,IAAI,aAAa;EACvB,MAAM,EAAE,SAAS,MAAM,WAAW,IAAI,WAAW,IAAI,OAAO,IAAI,YAAY;AAM5E,SAAO,gBAAgB,MAAM,eAAe;GAJ3C,SAAS;GACT,OAAO,aAAa,MAAM;GAC1B,WAAW,KAAK,KAAK;GAEyB,EAAE;GAChD,MAAM;GACN,YAAY;GACZ,CAAC,CAAC,KACF,OAAO,OAAO,UACb,OAAO,KACN,IAAI,WAAW;GACd,QAAQ;GACR,QAAQ,uBAAuB;GAC/B;GACA,CAAC,CACF,CACD,EACD,OAAO,eAAe,WAAW,YAAY,GAAG,CAChD;GACA,CAAC,KACF,OAAO,SAAS,yBAAyB,EACxC,YAAY;EACX,WAAW,IAAI;EACf,OAAO,IAAI;EACX,aAAa,IAAI;EACjB,EACD,CAAC,CACF;CAEF,MAAM,UAAU,QACf,OAAO,IAAI,aAAa;EACvB,MAAM,EAAE,SAAS,MAAM,WAAW,IAAI,WAAW,IAAI,OAAO,IAAI,YAAY;AAC5E,SAAO,GAAG,OAAO,MAAM,EAAE,OAAO,MAAM,CAAC,CAAC,KACvC,OAAO,OAAO,UACb,OAAO,KACN,IAAI,WAAW;GACd,QAAQ;GACR,QAAQ,wBAAwB;GAChC;GACA,CAAC,CACF,CACD,CACD;GACA,CAAC,KACF,OAAO,SAAS,0BAA0B,EACzC,YAAY;EACX,WAAW,IAAI;EACf,OAAO,IAAI;EACX,aAAa,IAAI;EACjB,EACD,CAAC,CACF;AAEF,QAAO,aAAa,GAAG;EACtB;EACA;EACA,QAAQ;EACR,CAAC;EACD,CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import { CapabilitySink, ContributionKind, HarvestContext } from "./service.mjs";
|
|
1
|
+
import { AnyContribution, CapabilitySink, CapabilitySinksService, CapabilitySinksShape, ContributionKind, ContributionSinkFailed, HarvestContext, UnknownContributionKind } from "./service.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { FormatterRegistryService, layerFormatterRegistry } from "../observability/formatter-registry.mjs";
|
|
2
1
|
import { CapabilitySinksService, layerCapabilitySinks } from "./service.mjs";
|
|
2
|
+
import { FormatterRegistryService, layerFormatterRegistry } from "../observability/formatter-registry.mjs";
|
|
3
3
|
import { Effect, Layer } from "effect";
|
|
4
4
|
//#region src/substrate/runtime/capability-sinks/layer.ts
|
|
5
5
|
const errorContributionSink = (formatters) => ({
|
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import { PluginKey } from "../../brand.mjs";
|
|
2
2
|
import { EngineEvent } from "../../events.mjs";
|
|
3
3
|
import { StrategyContributorDecl } from "../../../contracts/strategy-contributor.mjs";
|
|
4
|
+
import { CapabilityDecl } from "../../../contracts/capability-decl.mjs";
|
|
4
5
|
import { Identity } from "../../identity.mjs";
|
|
6
|
+
import { PluginErrorContribution } from "../../plugin.mjs";
|
|
5
7
|
import { Context, Effect, Layer, Scope } from "effect";
|
|
8
|
+
import * as _$effect_Cause0 from "effect/Cause";
|
|
9
|
+
import * as _$effect_Types0 from "effect/Types";
|
|
10
|
+
|
|
6
11
|
//#region src/substrate/runtime/capability-sinks/service.d.ts
|
|
12
|
+
/** A single harvested item — either a capability decl OR an error
|
|
13
|
+
* contribution. Discriminated by the literal `kind`. The supervisor
|
|
14
|
+
* produces one of these for each item it pulls off a plugin during
|
|
15
|
+
* the harvest loop. */
|
|
16
|
+
type AnyContribution = {
|
|
17
|
+
readonly source: 'capability';
|
|
18
|
+
readonly decl: CapabilityDecl;
|
|
19
|
+
} | {
|
|
20
|
+
readonly source: 'error';
|
|
21
|
+
readonly contribution: PluginErrorContribution;
|
|
22
|
+
};
|
|
7
23
|
/** The discriminator the sinks register against. Substrate-owned
|
|
8
24
|
* literal vocabulary — plugins authoring a custom contract add a new
|
|
9
25
|
* literal and a sink. */
|
|
10
|
-
type ContributionKind = 'snapshotable' | 'routable' | 'codegenable' | 'projection' | 'strategy-contributor' | '
|
|
26
|
+
type ContributionKind = 'snapshotable' | 'routable' | 'codegenable' | 'projection' | 'strategy-contributor' | 'error-contribution' | (string & {
|
|
11
27
|
readonly __extensionKind?: never;
|
|
12
28
|
});
|
|
13
29
|
/** Context the harvest loop supplies to each sink invocation. Carries
|
|
@@ -31,6 +47,49 @@ interface CapabilitySink<K extends ContributionKind, TDecl> {
|
|
|
31
47
|
readonly kind: K;
|
|
32
48
|
readonly accept: (decl: TDecl, ctx: HarvestContext) => Effect.Effect<void, unknown, Scope.Scope>;
|
|
33
49
|
}
|
|
50
|
+
declare const UnknownContributionKind_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
51
|
+
readonly _tag: "UnknownContributionKind";
|
|
52
|
+
} & Readonly<A>;
|
|
53
|
+
/** Surfaced when `dispatch` receives a kind no sink is registered for.
|
|
54
|
+
* Substrate stays open by default — unknown kinds are no-ops at the
|
|
55
|
+
* supervisor's call site (see `dispatchOrIgnore`); but tests + custom
|
|
56
|
+
* callers can drive the strict surface via `dispatch`. */
|
|
57
|
+
declare class UnknownContributionKind extends UnknownContributionKind_base<{
|
|
58
|
+
readonly kind: string;
|
|
59
|
+
readonly known: ReadonlyArray<string>;
|
|
60
|
+
}> {}
|
|
61
|
+
declare const ContributionSinkFailed_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
62
|
+
readonly _tag: "ContributionSinkFailed";
|
|
63
|
+
} & Readonly<A>;
|
|
64
|
+
/** Surfaced when a registered sink rejects while handling a known
|
|
65
|
+
* contribution kind. The original failure stays attached as `cause`
|
|
66
|
+
* so the supervisor's structured error renderer shows the domain
|
|
67
|
+
* error, such as `RouteCollision`, underneath the dispatch wrapper. */
|
|
68
|
+
declare class ContributionSinkFailed extends ContributionSinkFailed_base<{
|
|
69
|
+
readonly kind: string;
|
|
70
|
+
readonly message: string;
|
|
71
|
+
readonly cause: unknown;
|
|
72
|
+
}> {}
|
|
73
|
+
interface CapabilitySinksShape {
|
|
74
|
+
/** Register a sink for a contribution kind. Last-write-wins on
|
|
75
|
+
* duplicate kinds: a plugin-author overlay shadows the built-in
|
|
76
|
+
* (the highest-seq surviving registration wins at `dispatch`).
|
|
77
|
+
* Scope-bound: registering inside a `Layer` and providing the
|
|
78
|
+
* layer to the supervisor's scope reaps the sink on shutdown.
|
|
79
|
+
* Closing an overlay's scope un-shadows the built-in WITHOUT
|
|
80
|
+
* clobbering any newer sibling registration for the same kind —
|
|
81
|
+
* the scoped-multimap drops only this registration's own entry. */
|
|
82
|
+
readonly registerSink: <K extends ContributionKind, TDecl>(sink: CapabilitySink<K, TDecl>) => Effect.Effect<void, never, Scope.Scope>;
|
|
83
|
+
/** Dispatch a contribution to its registered sink. Errors with
|
|
84
|
+
* `UnknownContributionKind` when no sink matches; the supervisor's
|
|
85
|
+
* loop catches and downgrades to a no-op (per the
|
|
86
|
+
* substrate-open-by-default rule). */
|
|
87
|
+
readonly dispatch: (contribution: AnyContribution, ctx: HarvestContext) => Effect.Effect<void, UnknownContributionKind | ContributionSinkFailed, Scope.Scope>;
|
|
88
|
+
/** Snapshot the kinds the registry currently knows. Diagnostic. */
|
|
89
|
+
readonly knownKinds: Effect.Effect<ReadonlyArray<string>>;
|
|
90
|
+
}
|
|
91
|
+
declare const CapabilitySinksService_base: Context.ServiceClass<CapabilitySinksService, "@devstack/substrate/CapabilitySinks", CapabilitySinksShape>;
|
|
92
|
+
declare class CapabilitySinksService extends CapabilitySinksService_base {}
|
|
34
93
|
//#endregion
|
|
35
|
-
export { CapabilitySink, ContributionKind, HarvestContext };
|
|
94
|
+
export { AnyContribution, CapabilitySink, CapabilitySinksService, CapabilitySinksShape, ContributionKind, ContributionSinkFailed, HarvestContext, UnknownContributionKind };
|
|
36
95
|
//# sourceMappingURL=service.d.mts.map
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SpanAttr } from "../observability/spans.mjs";
|
|
2
|
+
import { makeScopedMultimap } from "../scoped-multimap/service.mjs";
|
|
3
|
+
import "../scoped-multimap/index.mjs";
|
|
4
|
+
import { Context, Data, Effect, Layer } from "effect";
|
|
2
5
|
//#region src/substrate/runtime/capability-sinks/service.ts
|
|
3
6
|
/** Surfaced when `dispatch` receives a kind no sink is registered for.
|
|
4
7
|
* Substrate stays open by default — unknown kinds are no-ops at the
|
|
@@ -10,7 +13,7 @@ var UnknownContributionKind = class extends Data.TaggedError("UnknownContributio
|
|
|
10
13
|
* so the supervisor's structured error renderer shows the domain
|
|
11
14
|
* error, such as `RouteCollision`, underneath the dispatch wrapper. */
|
|
12
15
|
var ContributionSinkFailed = class extends Data.TaggedError("ContributionSinkFailed") {};
|
|
13
|
-
var CapabilitySinksService = class extends Context.Service()("@devstack
|
|
16
|
+
var CapabilitySinksService = class extends Context.Service()("@devstack/substrate/CapabilitySinks") {};
|
|
14
17
|
/** Derive the discriminator literal from a harvested contribution.
|
|
15
18
|
* Substrate name-blindness: this is the ONE place the substrate
|
|
16
19
|
* reads a `kind` off a decl — every other site routes through the
|
|
@@ -24,44 +27,36 @@ const kindOf = (contribution) => {
|
|
|
24
27
|
* authored sinks compose by providing a Layer that yields the
|
|
25
28
|
* service and calls `registerSink`. */
|
|
26
29
|
const layerCapabilitySinks = Layer.effect(CapabilitySinksService, Effect.gen(function* () {
|
|
27
|
-
const
|
|
30
|
+
const store = yield* makeScopedMultimap();
|
|
28
31
|
const registerSink = (sink) => Effect.gen(function* () {
|
|
29
32
|
const accept = sink.accept;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
36
|
-
yield* Effect.addFinalizer((_exit) => Ref.update(sinksRef, (current) => {
|
|
37
|
-
const next = new Map(current);
|
|
38
|
-
if (prior === null) next.delete(sink.kind);
|
|
39
|
-
else next.set(sink.kind, prior);
|
|
40
|
-
return next;
|
|
41
|
-
}));
|
|
42
|
-
yield* Effect.annotateCurrentSpan({ "capability-sinks.kind": sink.kind });
|
|
33
|
+
yield* store.register([{
|
|
34
|
+
key: sink.kind,
|
|
35
|
+
value: accept
|
|
36
|
+
}]);
|
|
37
|
+
yield* Effect.annotateCurrentSpan({ [SpanAttr.capabilitySinksKind]: sink.kind });
|
|
43
38
|
}).pipe(Effect.withSpan("substrate.capabilitySinks.registerSink"));
|
|
44
39
|
const dispatch = (contribution, ctx) => Effect.gen(function* () {
|
|
45
40
|
const kind = kindOf(contribution);
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
if (accept === void 0) return yield* new UnknownContributionKind({
|
|
41
|
+
const entries = yield* store.entriesFor(kind);
|
|
42
|
+
if (entries.length === 0) return yield* new UnknownContributionKind({
|
|
49
43
|
kind,
|
|
50
|
-
known:
|
|
44
|
+
known: yield* store.keys
|
|
51
45
|
});
|
|
46
|
+
let chosen = entries[0];
|
|
47
|
+
for (let i = 1; i < entries.length; i++) if (entries[i].seq > chosen.seq) chosen = entries[i];
|
|
48
|
+
const accept = chosen.value;
|
|
52
49
|
yield* accept(contribution.source === "error" ? contribution.contribution : contribution.decl, ctx).pipe(Effect.mapError((cause) => new ContributionSinkFailed({
|
|
53
50
|
kind,
|
|
54
51
|
message: `capability sink '${kind}' failed`,
|
|
55
52
|
cause
|
|
56
53
|
})));
|
|
57
54
|
yield* Effect.annotateCurrentSpan({
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
[SpanAttr.capabilitySinksKind]: kind,
|
|
56
|
+
[SpanAttr.plugin]: ctx.pluginKey
|
|
60
57
|
});
|
|
61
58
|
}).pipe(Effect.withSpan("substrate.capabilitySinks.dispatch"));
|
|
62
|
-
const knownKinds =
|
|
63
|
-
return [...(yield* Ref.get(sinksRef)).keys()];
|
|
64
|
-
});
|
|
59
|
+
const knownKinds = store.keys;
|
|
65
60
|
return CapabilitySinksService.of({
|
|
66
61
|
registerSink,
|
|
67
62
|
dispatch,
|
|
@@ -69,6 +64,6 @@ const layerCapabilitySinks = Layer.effect(CapabilitySinksService, Effect.gen(fun
|
|
|
69
64
|
});
|
|
70
65
|
}));
|
|
71
66
|
//#endregion
|
|
72
|
-
export { CapabilitySinksService, layerCapabilitySinks };
|
|
67
|
+
export { CapabilitySinksService, ContributionSinkFailed, UnknownContributionKind, kindOf, layerCapabilitySinks };
|
|
73
68
|
|
|
74
69
|
//# sourceMappingURL=service.mjs.map
|