@mysten-incubation/devstack 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/dashboard-ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dashboard-ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dashboard-ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dashboard-ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-cyrillic-ext-wght-normal-I4S5GZfc.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-cyrillic-wght-normal-BmXc_FBt.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-latin-ext-wght-normal-DrnZ1wKl.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-latin-wght-normal-B_7UjwxQ.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-symbols2-wght-normal-GZpp1pK2.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-vietnamese-wght-normal-D8KDMBhC.woff2 +0 -0
- package/dashboard-ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dashboard-ui/assets/index-Bmi1UtAg.js +1356 -0
- package/dashboard-ui/assets/index-D5EShVt4.js +3 -0
- package/dashboard-ui/assets/index-Deml9drg.css +1 -0
- package/dashboard-ui/index.html +13 -0
- package/dist/api/define-capabilities.d.mts +14 -3
- package/dist/api/define-capabilities.mjs +18 -1
- package/dist/api/define-capabilities.mjs.map +1 -1
- package/dist/api/define-devstack.d.mts +1 -1
- package/dist/api/define-devstack.mjs +6 -26
- package/dist/api/define-devstack.mjs.map +1 -1
- package/dist/api/define-plugin.d.mts +1 -0
- package/dist/api/define-plugin.mjs +2 -0
- package/dist/api/inference-network.d.mts +132 -0
- package/dist/api/inference-network.mjs +105 -16
- package/dist/api/inference-network.mjs.map +1 -1
- package/dist/api/plugin-errors.d.mts +7 -0
- package/dist/api/run-stack.d.mts +20 -9
- package/dist/api/run-stack.mjs +56 -29
- package/dist/api/run-stack.mjs.map +1 -1
- package/dist/build-integrations/playwright/config.d.mts +6 -4
- package/dist/build-integrations/playwright/config.mjs +5 -3
- package/dist/build-integrations/playwright/config.mjs.map +1 -1
- package/dist/build-integrations/playwright/errors.d.mts +1 -1
- package/dist/build-integrations/playwright/global-setup.d.mts +43 -17
- package/dist/build-integrations/playwright/global-setup.mjs +250 -13
- package/dist/build-integrations/playwright/global-setup.mjs.map +1 -1
- package/dist/build-integrations/playwright/index.d.mts +5 -3
- package/dist/build-integrations/playwright/index.mjs +4 -2
- package/dist/build-integrations/playwright/stack-context.d.mts +0 -1
- package/dist/build-integrations/playwright/stack-context.mjs +24 -63
- package/dist/build-integrations/playwright/stack-context.mjs.map +1 -1
- package/dist/build-integrations/playwright/wallet-context.d.mts +7 -10
- package/dist/build-integrations/playwright/wallet-context.mjs +8 -13
- package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -1
- package/dist/build-integrations/runtime/cold-start-url.d.mts +5 -0
- package/dist/build-integrations/runtime/cold-start-url.mjs +4 -3
- package/dist/build-integrations/runtime/cold-start-url.mjs.map +1 -1
- package/dist/build-integrations/runtime/conventional-routes.d.mts +51 -0
- package/dist/build-integrations/runtime/conventional-routes.mjs +92 -0
- package/dist/build-integrations/runtime/conventional-routes.mjs.map +1 -0
- package/dist/build-integrations/runtime/discover.d.mts +38 -30
- package/dist/build-integrations/runtime/discover.mjs +73 -70
- package/dist/build-integrations/runtime/discover.mjs.map +1 -1
- package/dist/build-integrations/runtime/endpoint-registry.d.mts +3 -1
- package/dist/build-integrations/runtime/endpoint-registry.mjs +3 -1
- package/dist/build-integrations/runtime/endpoint-registry.mjs.map +1 -1
- package/dist/build-integrations/runtime/index.d.mts +7 -2
- package/dist/build-integrations/runtime/index.mjs +9 -2
- package/dist/build-integrations/runtime/manifest-types.d.mts +2 -0
- package/dist/build-integrations/runtime/manifest-types.mjs +3 -0
- package/dist/build-integrations/runtime/playwright-stack-context-slot.d.mts +29 -0
- package/dist/build-integrations/runtime/playwright-stack-context-slot.mjs +10 -0
- package/dist/build-integrations/runtime/playwright-stack-context-slot.mjs.map +1 -0
- package/dist/build-integrations/runtime/read-stack-context.d.mts +1 -1
- package/dist/build-integrations/runtime/read-stack-context.mjs +10 -4
- package/dist/build-integrations/runtime/read-stack-context.mjs.map +1 -1
- package/dist/build-integrations/runtime/resolve-discovery-env.d.mts +48 -0
- package/dist/build-integrations/runtime/resolve-discovery-env.mjs +42 -0
- package/dist/build-integrations/runtime/resolve-discovery-env.mjs.map +1 -0
- package/dist/build-integrations/runtime/stack-context.d.mts +1 -1
- package/dist/build-integrations/runtime/wallet-paths.mjs +2 -0
- package/dist/build-integrations/vite/index.d.mts +51 -0
- package/dist/build-integrations/vite/index.mjs +73 -0
- package/dist/build-integrations/vite/index.mjs.map +1 -0
- package/dist/build-integrations/vitest/env.d.mts +4 -3
- package/dist/build-integrations/vitest/env.mjs +6 -4
- package/dist/build-integrations/vitest/env.mjs.map +1 -1
- package/dist/build-integrations/vitest/errors.d.mts +25 -4
- package/dist/build-integrations/vitest/errors.mjs +15 -2
- package/dist/build-integrations/vitest/errors.mjs.map +1 -1
- package/dist/build-integrations/vitest/index.d.mts +2 -2
- package/dist/build-integrations/vitest/index.mjs +3 -3
- package/dist/build-integrations/vitest/setup.d.mts +4 -2
- package/dist/build-integrations/vitest/setup.mjs +2 -2
- package/dist/build-integrations/vitest/setup.mjs.map +1 -1
- package/dist/build-integrations/vitest/stack-context.d.mts +0 -1
- package/dist/build-integrations/vitest/stack-context.mjs +33 -13
- package/dist/build-integrations/vitest/stack-context.mjs.map +1 -1
- package/dist/cli/bail.mjs +23 -0
- package/dist/cli/bail.mjs.map +1 -0
- package/dist/{surfaces/cli/commands → cli}/doctor-probes.mjs +26 -16
- package/dist/cli/doctor-probes.mjs.map +1 -0
- package/dist/cli/main.d.mts +15 -1
- package/dist/cli/main.mjs +166 -482
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/prune-direct.mjs +31 -201
- package/dist/cli/prune-direct.mjs.map +1 -1
- package/dist/cli/snapshot-reader.mjs +14 -11
- package/dist/cli/snapshot-reader.mjs.map +1 -1
- package/dist/cli/up-lifecycle.mjs +4 -1
- package/dist/cli/up-lifecycle.mjs.map +1 -1
- package/dist/cli/wirings/apply.mjs +95 -0
- package/dist/cli/wirings/apply.mjs.map +1 -0
- package/dist/cli/wirings/build-verb-layers.mjs +42 -0
- package/dist/cli/wirings/build-verb-layers.mjs.map +1 -0
- package/dist/cli/wirings/config-loader.mjs +69 -0
- package/dist/cli/wirings/config-loader.mjs.map +1 -0
- package/dist/cli/wirings/engine-command.mjs +30 -0
- package/dist/cli/wirings/engine-command.mjs.map +1 -0
- package/dist/cli/wirings/identity.mjs +72 -0
- package/dist/cli/wirings/identity.mjs.map +1 -0
- package/dist/cli/wirings/provide-file-system.mjs +7 -0
- package/dist/cli/wirings/provide-file-system.mjs.map +1 -0
- package/dist/cli/wirings/snapshot.mjs +177 -0
- package/dist/cli/wirings/snapshot.mjs.map +1 -0
- package/dist/cli/wirings/up.mjs +287 -0
- package/dist/cli/wirings/up.mjs.map +1 -0
- package/dist/cli/wirings/wipe.mjs +40 -0
- package/dist/cli/wirings/wipe.mjs.map +1 -0
- package/dist/contracts/capability-decl.d.mts +0 -2
- package/dist/contracts/codegenable.d.mts +43 -0
- package/dist/contracts/container-runtime.d.mts +26 -1
- package/dist/contracts/faucet-strategy.d.mts +25 -0
- package/dist/contracts/funding-strategy.d.mts +42 -0
- package/dist/contracts/plugin-expander.mjs +60 -0
- package/dist/contracts/plugin-expander.mjs.map +1 -0
- package/dist/contracts/projection.d.mts +5 -3
- package/dist/contracts/renderer.d.mts +23 -0
- package/dist/contracts/routable.d.mts +14 -1
- package/dist/contracts/snapshotable.d.mts +25 -3
- package/dist/contracts/wallet-protocol.d.mts +52 -0
- package/dist/contracts/wallet-protocol.mjs +52 -0
- package/dist/contracts/wallet-protocol.mjs.map +1 -0
- package/dist/index.d.mts +26 -25
- package/dist/index.mjs +20 -13
- package/dist/{runtime → orchestrators}/built-in-plugin-layers.mjs +10 -5
- package/dist/orchestrators/built-in-plugin-layers.mjs.map +1 -0
- package/dist/orchestrators/codegen/bindings.d.mts +1 -2
- package/dist/orchestrators/codegen/bindings.mjs +52 -186
- package/dist/orchestrators/codegen/bindings.mjs.map +1 -1
- package/dist/orchestrators/codegen/emit.mjs +19 -1
- package/dist/orchestrators/codegen/emit.mjs.map +1 -1
- package/dist/orchestrators/codegen/errors.mjs +8 -3
- package/dist/orchestrators/codegen/errors.mjs.map +1 -1
- package/dist/orchestrators/codegen/format.mjs +47 -39
- package/dist/orchestrators/codegen/format.mjs.map +1 -1
- package/dist/orchestrators/codegen/gitignore.mjs +3 -1
- package/dist/orchestrators/codegen/gitignore.mjs.map +1 -1
- package/dist/orchestrators/codegen/output-location.mjs +41 -0
- package/dist/orchestrators/codegen/output-location.mjs.map +1 -0
- package/dist/orchestrators/codegen/paths.d.mts +1 -1
- package/dist/orchestrators/codegen/paths.mjs +40 -14
- package/dist/orchestrators/codegen/paths.mjs.map +1 -1
- package/dist/orchestrators/codegen/permissions.mjs +3 -1
- package/dist/orchestrators/codegen/permissions.mjs.map +1 -1
- package/dist/orchestrators/codegen/service.mjs +157 -105
- package/dist/orchestrators/codegen/service.mjs.map +1 -1
- package/dist/orchestrators/lifecycle-prune/errors.mjs +31 -0
- package/dist/orchestrators/lifecycle-prune/errors.mjs.map +1 -0
- package/dist/orchestrators/lifecycle-prune/index.mjs +350 -0
- package/dist/orchestrators/lifecycle-prune/index.mjs.map +1 -0
- package/dist/orchestrators/router/cleanup.mjs +26 -27
- package/dist/orchestrators/router/cleanup.mjs.map +1 -1
- package/dist/orchestrators/router/entrypoints.mjs +2 -2
- package/dist/orchestrators/router/entrypoints.mjs.map +1 -1
- package/dist/orchestrators/router/file-provider.mjs +14 -5
- package/dist/orchestrators/router/file-provider.mjs.map +1 -1
- package/dist/orchestrators/router/hostname.mjs +19 -55
- package/dist/orchestrators/router/hostname.mjs.map +1 -1
- package/dist/orchestrators/router/index.mjs +11 -0
- package/dist/orchestrators/router/profile.mjs +2 -2
- package/dist/orchestrators/router/sentinels.mjs +26 -0
- package/dist/orchestrators/router/sentinels.mjs.map +1 -0
- package/dist/orchestrators/router/service.mjs +40 -37
- package/dist/orchestrators/router/service.mjs.map +1 -1
- package/dist/orchestrators/router/traefik-container.mjs +27 -16
- package/dist/orchestrators/router/traefik-container.mjs.map +1 -1
- package/dist/{substrate/runtime → orchestrators}/run.mjs +26 -21
- package/dist/orchestrators/run.mjs.map +1 -0
- package/dist/orchestrators/runtime-composition.d.mts +1 -1
- package/dist/orchestrators/runtime-composition.mjs +123 -39
- package/dist/orchestrators/runtime-composition.mjs.map +1 -1
- package/dist/orchestrators/snapshot/capture-command.d.mts +1 -0
- package/dist/orchestrators/snapshot/capture-command.mjs +25 -0
- package/dist/orchestrators/snapshot/capture-command.mjs.map +1 -0
- package/dist/orchestrators/snapshot/capture.mjs +42 -28
- package/dist/orchestrators/snapshot/capture.mjs.map +1 -1
- package/dist/orchestrators/snapshot/descriptor.mjs +35 -10
- package/dist/orchestrators/snapshot/descriptor.mjs.map +1 -1
- package/dist/orchestrators/snapshot/identity-guard.mjs +1 -1
- package/dist/orchestrators/snapshot/index.mjs +13 -0
- package/dist/orchestrators/snapshot/integrity.d.mts +1 -1
- package/dist/orchestrators/snapshot/integrity.mjs +73 -17
- package/dist/orchestrators/snapshot/integrity.mjs.map +1 -1
- package/dist/orchestrators/snapshot/pending-marker.d.mts +1 -0
- package/dist/orchestrators/snapshot/pending-marker.mjs +99 -0
- package/dist/orchestrators/snapshot/pending-marker.mjs.map +1 -0
- package/dist/orchestrators/snapshot/phase-error.mjs +23 -0
- package/dist/orchestrators/snapshot/phase-error.mjs.map +1 -0
- package/dist/orchestrators/snapshot/prune.mjs +20 -55
- package/dist/orchestrators/snapshot/prune.mjs.map +1 -1
- package/dist/orchestrators/snapshot/recover-pending.d.mts +1 -0
- package/dist/orchestrators/snapshot/recover-pending.mjs +223 -0
- package/dist/orchestrators/snapshot/recover-pending.mjs.map +1 -0
- package/dist/orchestrators/snapshot/restore.mjs +118 -84
- package/dist/orchestrators/snapshot/restore.mjs.map +1 -1
- package/dist/orchestrators/snapshot/service.mjs +53 -56
- package/dist/orchestrators/snapshot/service.mjs.map +1 -1
- package/dist/orchestrators/snapshot/state-document.d.mts +1 -0
- package/dist/orchestrators/snapshot/state-document.mjs +47 -7
- package/dist/orchestrators/snapshot/state-document.mjs.map +1 -1
- package/dist/orchestrators/snapshot/wipe.mjs +67 -10
- package/dist/orchestrators/snapshot/wipe.mjs.map +1 -1
- package/dist/plugins/account/codegen.mjs +9 -8
- package/dist/plugins/account/codegen.mjs.map +1 -1
- package/dist/plugins/account/errors.d.mts +33 -6
- package/dist/plugins/account/errors.mjs.map +1 -1
- package/dist/plugins/account/funding.d.mts +29 -19
- package/dist/plugins/account/funding.mjs +84 -66
- package/dist/plugins/account/funding.mjs.map +1 -1
- package/dist/plugins/account/index.d.mts +2 -4
- package/dist/plugins/account/index.mjs +64 -31
- package/dist/plugins/account/index.mjs.map +1 -1
- package/dist/plugins/account/keypair.mjs +6 -9
- package/dist/plugins/account/keypair.mjs.map +1 -1
- package/dist/plugins/account/lease.mjs +1 -0
- package/dist/plugins/account/lease.mjs.map +1 -1
- package/dist/plugins/account/registry.d.mts +14 -1
- package/dist/plugins/account/registry.mjs +18 -18
- package/dist/plugins/account/registry.mjs.map +1 -1
- package/dist/plugins/account/service.d.mts +23 -4
- package/dist/plugins/account/service.mjs +83 -45
- package/dist/plugins/account/service.mjs.map +1 -1
- package/dist/plugins/account/snapshot.mjs +2 -5
- package/dist/plugins/account/snapshot.mjs.map +1 -1
- package/dist/plugins/account/spans.mjs +16 -0
- package/dist/plugins/account/spans.mjs.map +1 -0
- package/dist/plugins/account/variants/keystore.mjs +6 -8
- package/dist/plugins/account/variants/keystore.mjs.map +1 -1
- package/dist/plugins/action/build-context.d.mts +3 -2
- package/dist/plugins/action/errors.d.mts +16 -12
- package/dist/plugins/action/errors.mjs.map +1 -1
- package/dist/plugins/action/execute.mjs +65 -103
- package/dist/plugins/action/execute.mjs.map +1 -1
- package/dist/plugins/action/index.d.mts +0 -2
- package/dist/plugins/action/index.mjs +17 -13
- package/dist/plugins/action/index.mjs.map +1 -1
- package/dist/plugins/action/service.mjs +38 -35
- package/dist/plugins/action/service.mjs.map +1 -1
- package/dist/plugins/action/spans.mjs +11 -0
- package/dist/plugins/action/spans.mjs.map +1 -0
- package/dist/plugins/coin/codegen.mjs +9 -8
- package/dist/plugins/coin/codegen.mjs.map +1 -1
- package/dist/plugins/coin/discovery.mjs +8 -4
- package/dist/plugins/coin/discovery.mjs.map +1 -1
- package/dist/plugins/coin/index.d.mts +11 -29
- package/dist/plugins/coin/index.mjs +56 -27
- package/dist/plugins/coin/index.mjs.map +1 -1
- package/dist/plugins/coin/metadata.mjs +8 -15
- package/dist/plugins/coin/metadata.mjs.map +1 -1
- package/dist/plugins/coin/mint.d.mts +30 -11
- package/dist/plugins/coin/mint.mjs +78 -74
- package/dist/plugins/coin/mint.mjs.map +1 -1
- package/dist/plugins/coin/registry.mjs +4 -7
- package/dist/plugins/coin/registry.mjs.map +1 -1
- package/dist/plugins/coin/service.d.mts +39 -1
- package/dist/plugins/coin/service.mjs +11 -4
- package/dist/plugins/coin/service.mjs.map +1 -1
- package/dist/plugins/coin/spans.mjs +20 -0
- package/dist/plugins/coin/spans.mjs.map +1 -0
- package/dist/plugins/dashboard/domain.mjs +402 -0
- package/dist/plugins/dashboard/domain.mjs.map +1 -0
- package/dist/plugins/dashboard/index.d.mts +29 -0
- package/dist/plugins/dashboard/index.mjs +91 -0
- package/dist/plugins/dashboard/index.mjs.map +1 -0
- package/dist/plugins/dashboard/origin-policy.mjs +36 -0
- package/dist/plugins/dashboard/origin-policy.mjs.map +1 -0
- package/dist/plugins/dashboard/routable.mjs +27 -0
- package/dist/plugins/dashboard/routable.mjs.map +1 -0
- package/dist/plugins/dashboard/schema/builder.mjs +21 -0
- package/dist/plugins/dashboard/schema/builder.mjs.map +1 -0
- package/dist/plugins/dashboard/schema/enums.mjs +72 -0
- package/dist/plugins/dashboard/schema/enums.mjs.map +1 -0
- package/dist/plugins/dashboard/schema/root.mjs +351 -0
- package/dist/plugins/dashboard/schema/root.mjs.map +1 -0
- package/dist/plugins/dashboard/schema/types.mjs +508 -0
- package/dist/plugins/dashboard/schema/types.mjs.map +1 -0
- package/dist/plugins/dashboard/schema.mjs +11 -0
- package/dist/plugins/dashboard/schema.mjs.map +1 -0
- package/dist/plugins/dashboard/server.mjs +166 -0
- package/dist/plugins/dashboard/server.mjs.map +1 -0
- package/dist/plugins/deepbook/deploy.mjs +53 -115
- package/dist/plugins/deepbook/deploy.mjs.map +1 -1
- package/dist/plugins/deepbook/errors.mjs +1 -0
- package/dist/plugins/deepbook/errors.mjs.map +1 -1
- package/dist/plugins/deepbook/faucet-strategy.mjs +47 -34
- package/dist/plugins/deepbook/faucet-strategy.mjs.map +1 -1
- package/dist/plugins/deepbook/hash.mjs +9 -0
- package/dist/plugins/deepbook/hash.mjs.map +1 -0
- package/dist/plugins/deepbook/index.d.mts +1 -10
- package/dist/plugins/deepbook/index.mjs +25 -22
- package/dist/plugins/deepbook/index.mjs.map +1 -1
- package/dist/plugins/deepbook/pyth/index.mjs +26 -55
- package/dist/plugins/deepbook/pyth/index.mjs.map +1 -1
- package/dist/plugins/deepbook/snapshot.mjs +17 -29
- package/dist/plugins/deepbook/snapshot.mjs.map +1 -1
- package/dist/plugins/deepbook/spans.mjs +18 -0
- package/dist/plugins/deepbook/spans.mjs.map +1 -0
- package/dist/plugins/deepbook/type-strings.mjs +65 -0
- package/dist/plugins/deepbook/type-strings.mjs.map +1 -0
- package/dist/plugins/deepbook/types.d.mts +2 -3
- package/dist/plugins/deepbook/types.mjs.map +1 -1
- package/dist/plugins/faucet/dispatcher.d.mts +21 -2
- package/dist/plugins/faucet/dispatcher.mjs +1 -2
- package/dist/plugins/faucet/dispatcher.mjs.map +1 -1
- package/dist/plugins/faucet/errors.d.mts +11 -24
- package/dist/plugins/faucet/errors.mjs.map +1 -1
- package/dist/plugins/faucet/http.mjs +33 -14
- package/dist/plugins/faucet/http.mjs.map +1 -1
- package/dist/plugins/faucet/index.d.mts +15 -5
- package/dist/plugins/faucet/index.mjs +3 -0
- package/dist/plugins/faucet/index.mjs.map +1 -1
- package/dist/plugins/faucet/spans.mjs +12 -0
- package/dist/plugins/faucet/spans.mjs.map +1 -0
- package/dist/plugins/host-service/errors.d.mts +1 -1
- package/dist/plugins/host-service/index.mjs +34 -8
- package/dist/plugins/host-service/index.mjs.map +1 -1
- package/dist/plugins/host-service/routable.mjs +2 -1
- package/dist/plugins/host-service/routable.mjs.map +1 -1
- package/dist/plugins/host-service/service.d.mts +21 -0
- package/dist/plugins/host-service/service.mjs +136 -81
- package/dist/plugins/host-service/service.mjs.map +1 -1
- package/dist/plugins/internal/acquire-on-chain-artifact.mjs +23 -0
- package/dist/plugins/internal/acquire-on-chain-artifact.mjs.map +1 -0
- package/dist/plugins/internal/codegen-helpers.mjs +18 -0
- package/dist/plugins/internal/codegen-helpers.mjs.map +1 -0
- package/dist/plugins/internal/funding-failure-error.mjs +32 -0
- package/dist/plugins/internal/funding-failure-error.mjs.map +1 -0
- package/dist/plugins/package/build.mjs +1 -1
- package/dist/plugins/package/build.mjs.map +1 -1
- package/dist/plugins/package/codegen.mjs +26 -0
- package/dist/plugins/package/codegen.mjs.map +1 -1
- package/dist/plugins/package/errors.d.mts +16 -6
- package/dist/plugins/package/errors.mjs.map +1 -1
- package/dist/plugins/package/index.d.mts +0 -4
- package/dist/plugins/package/index.mjs +27 -21
- package/dist/plugins/package/index.mjs.map +1 -1
- package/dist/plugins/package/mode-known.mjs +26 -12
- package/dist/plugins/package/mode-known.mjs.map +1 -1
- package/dist/plugins/package/mode-local.mjs +47 -41
- package/dist/plugins/package/mode-local.mjs.map +1 -1
- package/dist/plugins/package/publish-executor.mjs +55 -114
- package/dist/plugins/package/publish-executor.mjs.map +1 -1
- package/dist/plugins/package/registry.mjs +2 -1
- package/dist/plugins/package/registry.mjs.map +1 -1
- package/dist/plugins/package/service.mjs.map +1 -1
- package/dist/plugins/package/spans.mjs +14 -0
- package/dist/plugins/package/spans.mjs.map +1 -0
- package/dist/plugins/postgres/db-ensure.mjs +19 -12
- package/dist/plugins/postgres/db-ensure.mjs.map +1 -1
- package/dist/plugins/postgres/errors.d.mts +5 -0
- package/dist/plugins/postgres/errors.mjs.map +1 -1
- package/dist/plugins/postgres/index.mjs +10 -17
- package/dist/plugins/postgres/index.mjs.map +1 -1
- package/dist/plugins/postgres/service.mjs +58 -24
- package/dist/plugins/postgres/service.mjs.map +1 -1
- package/dist/plugins/postgres/snapshot.mjs.map +1 -1
- package/dist/plugins/postgres/spans.mjs +11 -0
- package/dist/plugins/postgres/spans.mjs.map +1 -0
- package/dist/plugins/router-entrypoints.mjs +4 -4
- package/dist/plugins/router-entrypoints.mjs.map +1 -1
- package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs +15 -13
- package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs.map +1 -1
- package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs +15 -19
- package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs.map +1 -1
- package/dist/plugins/seal/codegen.d.mts +2 -3
- package/dist/plugins/seal/codegen.mjs.map +1 -1
- package/dist/plugins/seal/config-render.mjs +35 -8
- package/dist/plugins/seal/config-render.mjs.map +1 -1
- package/dist/plugins/seal/deploy.mjs +31 -39
- package/dist/plugins/seal/deploy.mjs.map +1 -1
- package/dist/plugins/seal/errors.d.mts +1 -3
- package/dist/plugins/seal/errors.mjs +7 -1
- package/dist/plugins/seal/errors.mjs.map +1 -1
- package/dist/plugins/seal/index.d.mts +1 -6
- package/dist/plugins/seal/index.mjs +40 -14
- package/dist/plugins/seal/index.mjs.map +1 -1
- package/dist/plugins/seal/key-manager.d.mts +1 -9
- package/dist/plugins/seal/key-manager.mjs +2 -18
- package/dist/plugins/seal/key-manager.mjs.map +1 -1
- package/dist/plugins/seal/key-server.mjs +37 -36
- package/dist/plugins/seal/key-server.mjs.map +1 -1
- package/dist/plugins/seal/keygen.mjs +16 -17
- package/dist/plugins/seal/keygen.mjs.map +1 -1
- package/dist/plugins/seal/mode/fork-known.mjs +36 -14
- package/dist/plugins/seal/mode/fork-known.mjs.map +1 -1
- package/dist/plugins/seal/mode/live.d.mts +6 -4
- package/dist/plugins/seal/mode/live.mjs +14 -21
- package/dist/plugins/seal/mode/live.mjs.map +1 -1
- package/dist/plugins/seal/mode/local-keygen.mjs +57 -21
- package/dist/plugins/seal/mode/local-keygen.mjs.map +1 -1
- package/dist/plugins/seal/registry-publish.mjs +1 -0
- package/dist/plugins/seal/registry-publish.mjs.map +1 -1
- package/dist/plugins/seal/routable.mjs.map +1 -1
- package/dist/plugins/seal/service.mjs.map +1 -1
- package/dist/plugins/seal/spans.mjs +18 -0
- package/dist/plugins/seal/spans.mjs.map +1 -0
- package/dist/plugins/sui/auto-tick.mjs +17 -23
- package/dist/plugins/sui/auto-tick.mjs.map +1 -1
- package/dist/plugins/sui/chain-build-container.mjs +18 -0
- package/dist/plugins/sui/chain-build-container.mjs.map +1 -0
- package/dist/plugins/sui/chain-probe.d.mts +5 -5
- package/dist/plugins/sui/chain-probe.mjs +28 -58
- package/dist/plugins/sui/chain-probe.mjs.map +1 -1
- package/dist/plugins/sui/codegen.mjs +26 -0
- package/dist/plugins/sui/codegen.mjs.map +1 -1
- package/dist/plugins/sui/errors.d.mts +2 -29
- package/dist/plugins/sui/errors.mjs +1 -3
- package/dist/plugins/sui/errors.mjs.map +1 -1
- package/dist/plugins/sui/fork-faucet-strategy.d.mts +1 -0
- package/dist/plugins/sui/fork-faucet-strategy.mjs +55 -0
- package/dist/plugins/sui/fork-faucet-strategy.mjs.map +1 -0
- package/dist/plugins/sui/fork-orchestration.d.mts +1 -0
- package/dist/plugins/sui/fork-orchestration.mjs +170 -2
- package/dist/plugins/sui/fork-orchestration.mjs.map +1 -1
- package/dist/plugins/sui/fork-transaction.d.mts +2 -0
- package/dist/plugins/sui/fork-transaction.mjs +92 -28
- package/dist/plugins/sui/fork-transaction.mjs.map +1 -1
- package/dist/plugins/sui/index.d.mts +33 -70
- package/dist/plugins/sui/index.mjs +58 -35
- package/dist/plugins/sui/index.mjs.map +1 -1
- package/dist/plugins/{faucet/strategies/sui-local.mjs → sui/local-faucet-strategy.mjs} +10 -7
- package/dist/plugins/sui/local-faucet-strategy.mjs.map +1 -0
- package/dist/plugins/sui/mode/external.mjs +7 -13
- package/dist/plugins/sui/mode/external.mjs.map +1 -1
- package/dist/plugins/sui/mode/fork.mjs +136 -59
- package/dist/plugins/sui/mode/fork.mjs.map +1 -1
- package/dist/plugins/sui/mode/live.mjs +10 -16
- package/dist/plugins/sui/mode/live.mjs.map +1 -1
- package/dist/plugins/sui/mode/local.mjs +37 -36
- package/dist/plugins/sui/mode/local.mjs.map +1 -1
- package/dist/plugins/sui/mode/shared-boot.mjs +38 -115
- package/dist/plugins/sui/mode/shared-boot.mjs.map +1 -1
- package/dist/plugins/sui/mode/shared.d.mts +1 -5
- package/dist/plugins/sui/mode/shared.mjs.map +1 -1
- package/dist/plugins/sui/mode/spec.d.mts +18 -0
- package/dist/plugins/sui/move-summary-runner.mjs +207 -0
- package/dist/plugins/sui/move-summary-runner.mjs.map +1 -0
- package/dist/plugins/sui/network-resolver.d.mts +4 -3
- package/dist/plugins/sui/service.mjs.map +1 -1
- package/dist/plugins/sui/snapshot.mjs +3 -0
- package/dist/plugins/sui/snapshot.mjs.map +1 -1
- package/dist/plugins/sui/spans.mjs +17 -0
- package/dist/plugins/sui/spans.mjs.map +1 -0
- package/dist/plugins/wallet/codegen.d.mts +0 -1
- package/dist/plugins/wallet/codegen.mjs +8 -9
- package/dist/plugins/wallet/codegen.mjs.map +1 -1
- package/dist/plugins/wallet/index.d.mts +6 -8
- package/dist/plugins/wallet/index.mjs +46 -35
- package/dist/plugins/wallet/index.mjs.map +1 -1
- package/dist/plugins/wallet/origin-policy.mjs +12 -28
- package/dist/plugins/wallet/origin-policy.mjs.map +1 -1
- package/dist/plugins/wallet/pairing.mjs +6 -4
- package/dist/plugins/wallet/pairing.mjs.map +1 -1
- package/dist/plugins/wallet/protocol.d.mts +1 -0
- package/dist/plugins/wallet/protocol.mjs +5 -44
- package/dist/plugins/wallet/protocol.mjs.map +1 -1
- package/dist/plugins/wallet/routable.mjs +2 -8
- package/dist/plugins/wallet/routable.mjs.map +1 -1
- package/dist/plugins/wallet/server.mjs +36 -44
- package/dist/plugins/wallet/server.mjs.map +1 -1
- package/dist/plugins/wallet/service.d.mts +1 -5
- package/dist/plugins/wallet/service.mjs +22 -18
- package/dist/plugins/wallet/service.mjs.map +1 -1
- package/dist/plugins/wallet/spans.mjs +22 -0
- package/dist/plugins/wallet/spans.mjs.map +1 -0
- package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs +33 -8
- package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs.map +1 -1
- package/dist/plugins/walrus/codegen.d.mts +3 -3
- package/dist/plugins/walrus/codegen.mjs.map +1 -1
- package/dist/plugins/walrus/deploy-paths.mjs +21 -5
- package/dist/plugins/walrus/deploy-paths.mjs.map +1 -1
- package/dist/plugins/walrus/deploy.mjs +67 -69
- package/dist/plugins/walrus/deploy.mjs.map +1 -1
- package/dist/plugins/walrus/errors.mjs +1 -0
- package/dist/plugins/walrus/errors.mjs.map +1 -1
- package/dist/plugins/walrus/faucet-strategy.mjs +12 -7
- package/dist/plugins/walrus/faucet-strategy.mjs.map +1 -1
- package/dist/plugins/walrus/index.d.mts +2 -13
- package/dist/plugins/walrus/index.mjs +18 -23
- package/dist/plugins/walrus/index.mjs.map +1 -1
- package/dist/plugins/walrus/mode/known-deploy.mjs +7 -5
- package/dist/plugins/walrus/mode/known-deploy.mjs.map +1 -1
- package/dist/plugins/walrus/mode/local-cluster.mjs +26 -7
- package/dist/plugins/walrus/mode/local-cluster.mjs.map +1 -1
- package/dist/plugins/walrus/registry-publish.mjs.map +1 -1
- package/dist/plugins/walrus/routable.mjs +7 -23
- package/dist/plugins/walrus/routable.mjs.map +1 -1
- package/dist/plugins/walrus/service.mjs.map +1 -1
- package/dist/plugins/walrus/spans.mjs +18 -0
- package/dist/plugins/walrus/spans.mjs.map +1 -0
- package/dist/plugins/walrus/storage-nodes.d.mts +6 -2
- package/dist/plugins/walrus/storage-nodes.mjs +15 -8
- package/dist/plugins/walrus/storage-nodes.mjs.map +1 -1
- package/dist/plugins/walrus/wal-swap.mjs +58 -15
- package/dist/plugins/walrus/wal-swap.mjs.map +1 -1
- package/dist/primitives/artifact-publisher.d.mts +1 -1
- package/dist/primitives/artifact-publisher.mjs +15 -0
- package/dist/primitives/artifact-publisher.mjs.map +1 -0
- package/dist/runtime/docker/client.mjs +17 -4
- package/dist/runtime/docker/client.mjs.map +1 -1
- package/dist/runtime/docker/container.mjs +219 -132
- package/dist/runtime/docker/container.mjs.map +1 -1
- package/dist/runtime/docker/errors.mjs +0 -6
- package/dist/runtime/docker/errors.mjs.map +1 -1
- package/dist/runtime/docker/exec.mjs +21 -18
- package/dist/runtime/docker/exec.mjs.map +1 -1
- package/dist/runtime/docker/image.mjs +36 -13
- package/dist/runtime/docker/image.mjs.map +1 -1
- package/dist/runtime/docker/index.mjs +14 -0
- package/dist/runtime/docker/inspect-and-decode.mjs +51 -0
- package/dist/runtime/docker/inspect-and-decode.mjs.map +1 -0
- package/dist/runtime/docker/inventory.mjs +14 -11
- package/dist/runtime/docker/inventory.mjs.map +1 -1
- package/dist/runtime/docker/labels.mjs +38 -5
- package/dist/runtime/docker/labels.mjs.map +1 -1
- package/dist/runtime/docker/logs.d.mts +1 -1
- package/dist/runtime/docker/logs.mjs.map +1 -1
- package/dist/runtime/docker/network.mjs +83 -47
- package/dist/runtime/docker/network.mjs.map +1 -1
- package/dist/runtime/docker/render-run-args.mjs +82 -0
- package/dist/runtime/docker/render-run-args.mjs.map +1 -0
- package/dist/runtime/docker/service.d.mts +1 -1
- package/dist/runtime/docker/service.mjs +12 -7
- package/dist/runtime/docker/service.mjs.map +1 -1
- package/dist/runtime/docker/sweep.mjs +118 -43
- package/dist/runtime/docker/sweep.mjs.map +1 -1
- package/dist/runtime/docker/volume.mjs +2 -0
- package/dist/runtime/docker/volume.mjs.map +1 -1
- package/dist/runtime/docker/wrap.mjs +30 -1
- package/dist/runtime/docker/wrap.mjs.map +1 -1
- package/dist/substrate/cross-process.mjs +14 -8
- package/dist/substrate/cross-process.mjs.map +1 -1
- package/dist/substrate/event-time.mjs +33 -0
- package/dist/substrate/event-time.mjs.map +1 -0
- package/dist/substrate/events.d.mts +28 -8
- package/dist/substrate/identity.d.mts +2 -1
- package/dist/substrate/manifest.d.mts +68 -5
- package/dist/substrate/manifest.mjs +34 -5
- package/dist/substrate/manifest.mjs.map +1 -1
- package/dist/substrate/network.d.mts +3 -5
- package/dist/substrate/options.d.mts +0 -2
- package/dist/substrate/plugin.d.mts +13 -0
- package/dist/substrate/plugin.mjs +4 -2
- package/dist/substrate/plugin.mjs.map +1 -1
- package/dist/substrate/projection.d.mts +35 -3
- package/dist/substrate/runtime/artifact-publisher/index.mjs +19 -9
- package/dist/substrate/runtime/artifact-publisher/index.mjs.map +1 -1
- package/dist/substrate/runtime/atomic-write.mjs +97 -32
- package/dist/substrate/runtime/atomic-write.mjs.map +1 -1
- package/dist/substrate/runtime/cache/index.mjs +3 -0
- package/dist/substrate/runtime/cache/schema.mjs +2 -2
- package/dist/substrate/runtime/cache/schema.mjs.map +1 -1
- package/dist/substrate/runtime/cache/service.mjs +9 -7
- package/dist/substrate/runtime/cache/service.mjs.map +1 -1
- package/dist/substrate/runtime/capability-sinks/index.d.mts +1 -1
- package/dist/substrate/runtime/capability-sinks/index.mjs +3 -0
- package/dist/substrate/runtime/capability-sinks/layer.mjs +1 -1
- package/dist/substrate/runtime/capability-sinks/service.d.mts +61 -2
- package/dist/substrate/runtime/capability-sinks/service.mjs +21 -26
- package/dist/substrate/runtime/capability-sinks/service.mjs.map +1 -1
- package/dist/substrate/runtime/config-validation.d.mts +1 -13
- package/dist/substrate/runtime/config-validation.mjs +2 -91
- package/dist/substrate/runtime/config-validation.mjs.map +1 -1
- package/dist/substrate/runtime/container-runtime.d.mts +1 -0
- package/dist/substrate/runtime/container-runtime.mjs +2 -0
- package/dist/substrate/runtime/control-plane/domain.mjs +101 -0
- package/dist/substrate/runtime/control-plane/domain.mjs.map +1 -0
- package/dist/substrate/runtime/control-plane/service.mjs +7 -0
- package/dist/substrate/runtime/control-plane/service.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/command-channel/channel.mjs +46 -19
- package/dist/substrate/runtime/cross-process/command-channel/channel.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs +30 -12
- package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/command-channel/index.mjs +5 -0
- package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs +11 -2
- package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/command-channel/runtime-control-lock.mjs +1 -1
- package/dist/substrate/runtime/cross-process/index.mjs +11 -0
- package/dist/substrate/runtime/cross-process/live-clock.mjs +34 -0
- package/dist/substrate/runtime/cross-process/live-clock.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/liveness.mjs +52 -10
- package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/lock.d.mts +1 -0
- package/dist/substrate/runtime/cross-process/lock.mjs +23 -0
- package/dist/substrate/runtime/cross-process/lock.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs +64 -0
- package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/roster.mjs +97 -83
- package/dist/substrate/runtime/cross-process/roster.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/self-pid.mjs +8 -0
- package/dist/substrate/runtime/cross-process/self-pid.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs +24 -28
- package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/stack-lock.mjs +41 -20
- package/dist/substrate/runtime/cross-process/stack-lock.mjs.map +1 -1
- package/dist/substrate/runtime/current-plugin.mjs +2 -2
- package/dist/substrate/runtime/current-plugin.mjs.map +1 -1
- package/dist/substrate/runtime/errors.mjs +12 -6
- package/dist/substrate/runtime/errors.mjs.map +1 -1
- package/dist/substrate/runtime/format-unknown-error.mjs +30 -0
- package/dist/substrate/runtime/format-unknown-error.mjs.map +1 -0
- package/dist/substrate/runtime/host-bind-mount-owner.mjs +10 -0
- package/dist/substrate/runtime/host-bind-mount-owner.mjs.map +1 -0
- package/dist/substrate/runtime/host-gateway.mjs +20 -0
- package/dist/substrate/runtime/host-gateway.mjs.map +1 -0
- package/dist/substrate/runtime/host-tree-tar/index.mjs +61 -50
- package/dist/substrate/runtime/host-tree-tar/index.mjs.map +1 -1
- package/dist/substrate/runtime/http-probe.d.mts +1 -1
- package/dist/substrate/runtime/index.mjs +14 -0
- package/dist/substrate/runtime/lease-broker/index.d.mts +1 -1
- package/dist/substrate/runtime/lease-broker/index.mjs +2 -0
- package/dist/substrate/runtime/lease-broker/service.d.mts +1 -64
- package/dist/substrate/runtime/lease-broker/service.mjs +1 -1
- package/dist/substrate/runtime/lease-broker/service.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/dep-graph.mjs +22 -3
- package/dist/substrate/runtime/lifecycle/dep-graph.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/index.mjs +9 -0
- package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/plugin-registry.mjs +20 -3
- package/dist/substrate/runtime/lifecycle/plugin-registry.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/selective-restart.mjs +1 -1
- package/dist/substrate/runtime/lifecycle/signals.mjs +5 -5
- package/dist/substrate/runtime/lifecycle/signals.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/state-machine.mjs +1 -1
- package/dist/substrate/runtime/managed-container.d.mts +19 -1
- package/dist/substrate/runtime/managed-container.mjs +20 -1
- package/dist/substrate/runtime/managed-container.mjs.map +1 -1
- package/dist/substrate/runtime/manifest/index.mjs +3 -0
- package/dist/substrate/runtime/manifest/manifest.mjs +15 -11
- package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -1
- package/dist/substrate/runtime/mode-errors.mjs +20 -0
- package/dist/substrate/runtime/mode-errors.mjs.map +1 -0
- package/dist/substrate/runtime/observability/cascade-formatter.mjs +9 -13
- package/dist/substrate/runtime/observability/cascade-formatter.mjs.map +1 -1
- package/dist/substrate/runtime/observability/formatter-registry.mjs +19 -36
- package/dist/substrate/runtime/observability/formatter-registry.mjs.map +1 -1
- package/dist/substrate/runtime/observability/ignore-with-log.d.mts +1 -0
- package/dist/substrate/runtime/observability/ignore-with-log.mjs +17 -0
- package/dist/substrate/runtime/observability/ignore-with-log.mjs.map +1 -0
- package/dist/substrate/runtime/observability/index.d.mts +1 -1
- package/dist/substrate/runtime/observability/index.mjs +13 -0
- package/dist/substrate/runtime/observability/log-store.d.mts +1 -0
- package/dist/substrate/runtime/observability/log-store.mjs +175 -0
- package/dist/substrate/runtime/observability/log-store.mjs.map +1 -0
- package/dist/substrate/runtime/observability/logger.d.mts +2 -1
- package/dist/substrate/runtime/observability/logger.mjs +5 -9
- package/dist/substrate/runtime/observability/logger.mjs.map +1 -1
- package/dist/substrate/runtime/observability/output-truncate.mjs +35 -0
- package/dist/substrate/runtime/observability/output-truncate.mjs.map +1 -0
- package/dist/substrate/runtime/observability/pretty-error.mjs +9 -12
- package/dist/substrate/runtime/observability/pretty-error.mjs.map +1 -1
- package/dist/substrate/runtime/observability/redaction.d.mts +2 -13
- package/dist/substrate/runtime/observability/redaction.mjs +8 -14
- package/dist/substrate/runtime/observability/redaction.mjs.map +1 -1
- package/dist/substrate/runtime/observability/span-store.d.mts +1 -0
- package/dist/substrate/runtime/observability/span-store.mjs +110 -0
- package/dist/substrate/runtime/observability/span-store.mjs.map +1 -0
- package/dist/substrate/runtime/observability/spans.mjs +14 -14
- package/dist/substrate/runtime/observability/spans.mjs.map +1 -1
- package/dist/substrate/runtime/observability/subprocess-capture.mjs +1 -1
- package/dist/substrate/runtime/passthrough-or-wrap.mjs +33 -0
- package/dist/substrate/runtime/passthrough-or-wrap.mjs.map +1 -0
- package/dist/substrate/runtime/paths.d.mts +1 -1
- package/dist/substrate/runtime/paths.mjs +4 -3
- package/dist/substrate/runtime/paths.mjs.map +1 -1
- package/dist/substrate/runtime/phase-preserving-produce.mjs +40 -0
- package/dist/substrate/runtime/phase-preserving-produce.mjs.map +1 -0
- package/dist/substrate/runtime/port-broker/index.mjs +3 -0
- package/dist/substrate/runtime/port-broker/service.mjs +170 -126
- package/dist/substrate/runtime/port-broker/service.mjs.map +1 -1
- package/dist/substrate/runtime/post-acquire-tasks.mjs +1 -1
- package/dist/substrate/runtime/post-acquire-tasks.mjs.map +1 -1
- package/dist/substrate/runtime/probes.d.mts +7 -5
- package/dist/substrate/runtime/probes.mjs +3 -1
- package/dist/substrate/runtime/probes.mjs.map +1 -1
- package/dist/substrate/runtime/projection/index.mjs +4 -0
- package/dist/substrate/runtime/projection/operational-endpoints.mjs +1 -2
- package/dist/substrate/runtime/projection/operational-endpoints.mjs.map +1 -1
- package/dist/substrate/runtime/projection/persisted.mjs +33 -15
- package/dist/substrate/runtime/projection/persisted.mjs.map +1 -1
- package/dist/substrate/runtime/projection/state-ref.mjs +26 -2
- package/dist/substrate/runtime/projection/state-ref.mjs.map +1 -1
- package/dist/substrate/runtime/projection/update.mjs +88 -24
- package/dist/substrate/runtime/projection/update.mjs.map +1 -1
- package/dist/substrate/runtime/random-suffix.mjs +11 -0
- package/dist/substrate/runtime/random-suffix.mjs.map +1 -0
- package/dist/substrate/runtime/retry-policy.d.mts +59 -1
- package/dist/substrate/runtime/retry-policy.mjs +66 -1
- package/dist/substrate/runtime/retry-policy.mjs.map +1 -1
- package/dist/substrate/runtime/routed-url.d.mts +1 -0
- package/dist/substrate/runtime/routed-url.mjs +79 -0
- package/dist/substrate/runtime/routed-url.mjs.map +1 -0
- package/dist/substrate/runtime/runtime-decode.d.mts +1 -1
- package/dist/substrate/runtime/runtime-decode.mjs.map +1 -1
- package/dist/substrate/runtime/scoped-http-server.mjs +2 -3
- package/dist/substrate/runtime/scoped-http-server.mjs.map +1 -1
- package/dist/substrate/runtime/scoped-multimap/index.mjs +2 -0
- package/dist/substrate/runtime/scoped-multimap/service.mjs +52 -0
- package/dist/substrate/runtime/scoped-multimap/service.mjs.map +1 -0
- package/dist/substrate/runtime/scoped-ref-map/index.mjs +2 -0
- package/dist/substrate/runtime/scoped-ref-map/service.mjs +2 -2
- package/dist/substrate/runtime/scoped-ref-map/service.mjs.map +1 -1
- package/dist/substrate/runtime/stage-and-swap/index.mjs +72 -4
- package/dist/substrate/runtime/stage-and-swap/index.mjs.map +1 -1
- package/dist/substrate/runtime/state-store/index.d.mts +1 -0
- package/dist/substrate/runtime/state-store/index.mjs +3 -0
- package/dist/substrate/runtime/state-store/schema.d.mts +1 -0
- package/dist/substrate/runtime/state-store/schema.mjs +19 -14
- package/dist/substrate/runtime/state-store/schema.mjs.map +1 -1
- package/dist/substrate/runtime/state-store/service.d.mts +1 -0
- package/dist/substrate/runtime/state-store/service.mjs +145 -0
- package/dist/substrate/runtime/state-store/service.mjs.map +1 -0
- package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.d.mts +1 -0
- package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs +33 -0
- package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs.map +1 -0
- package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs +1 -1
- package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs.map +1 -1
- package/dist/substrate/runtime/strategy-registry/index.mjs +4 -0
- package/dist/substrate/runtime/strategy-registry/service.mjs +19 -33
- package/dist/substrate/runtime/strategy-registry/service.mjs.map +1 -1
- package/dist/substrate/runtime/subnet-broker.mjs +60 -0
- package/dist/substrate/runtime/subnet-broker.mjs.map +1 -0
- package/dist/substrate/runtime/sui-execute/index.d.mts +21 -1
- package/dist/substrate/runtime/sui-execute/index.mjs +84 -20
- package/dist/substrate/runtime/sui-execute/index.mjs.map +1 -1
- package/dist/substrate/runtime/sui-execute/sign-and-dispatch.d.mts +1 -0
- package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs +32 -0
- package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs.map +1 -0
- package/dist/substrate/runtime/sui-ledger/object-ref.mjs +32 -0
- package/dist/substrate/runtime/sui-ledger/object-ref.mjs.map +1 -0
- package/dist/substrate/runtime/sui-move-build/index.mjs +93 -19
- package/dist/substrate/runtime/sui-move-build/index.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/acquire-node.mjs +181 -0
- package/dist/substrate/runtime/supervisor/acquire-node.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/background-tasks.mjs +144 -0
- package/dist/substrate/runtime/supervisor/background-tasks.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/command-loop.mjs +68 -0
- package/dist/substrate/runtime/supervisor/command-loop.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs +151 -0
- package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/errors.d.mts +1 -0
- package/dist/substrate/runtime/supervisor/errors.mjs +9 -0
- package/dist/substrate/runtime/supervisor/errors.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/index.d.mts +1 -0
- package/dist/substrate/runtime/supervisor/index.mjs +3 -0
- package/dist/substrate/runtime/supervisor/shutdown.mjs +51 -0
- package/dist/substrate/runtime/supervisor/shutdown.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/start-supervisor.d.mts +1 -0
- package/dist/substrate/runtime/supervisor/start-supervisor.mjs +250 -0
- package/dist/substrate/runtime/supervisor/start-supervisor.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/state.d.mts +1 -0
- package/dist/substrate/runtime/supervisor/state.mjs +22 -0
- package/dist/substrate/runtime/supervisor/state.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/teardown.mjs +57 -0
- package/dist/substrate/runtime/supervisor/teardown.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/types.d.mts +1 -0
- package/dist/substrate/runtime/supervisor/wiring.mjs +133 -0
- package/dist/substrate/runtime/supervisor/wiring.mjs.map +1 -0
- package/dist/substrate/runtime/typed-env.mjs +8 -0
- package/dist/substrate/runtime/typed-env.mjs.map +1 -0
- package/dist/substrate/state-store.d.mts +1 -0
- package/dist/substrate/versioned-doc-schema.mjs +17 -0
- package/dist/substrate/versioned-doc-schema.mjs.map +1 -0
- package/dist/substrate/versioned-doc-sync.mjs +94 -0
- package/dist/substrate/versioned-doc-sync.mjs.map +1 -0
- package/dist/surfaces/cli/command-tree.mjs.map +1 -1
- package/dist/surfaces/cli/commands/index.mjs +9 -0
- package/dist/surfaces/cli/commands/prune-picker-entry.mjs +13 -0
- package/dist/surfaces/cli/commands/prune-picker-entry.mjs.map +1 -0
- package/dist/surfaces/cli/commands/prune-picker.mjs +3 -2
- package/dist/surfaces/cli/commands/prune-picker.mjs.map +1 -1
- package/dist/surfaces/cli/commands/prune.mjs +34 -17
- package/dist/surfaces/cli/commands/prune.mjs.map +1 -1
- package/dist/surfaces/cli/commands/status.mjs +2 -2
- package/dist/surfaces/cli/commands/status.mjs.map +1 -1
- package/dist/surfaces/cli/commands/supervisor-presence.mjs +5 -3
- package/dist/surfaces/cli/commands/supervisor-presence.mjs.map +1 -1
- package/dist/surfaces/cli/commands/wipe.mjs +38 -4
- package/dist/surfaces/cli/commands/wipe.mjs.map +1 -1
- package/dist/surfaces/cli/errors.mjs +1 -1
- package/dist/surfaces/cli/errors.mjs.map +1 -1
- package/dist/surfaces/cli/flags.mjs +0 -1
- package/dist/surfaces/cli/flags.mjs.map +1 -1
- package/dist/surfaces/cli/index.mjs +98 -18
- package/dist/surfaces/cli/index.mjs.map +1 -1
- package/dist/surfaces/cli/output.mjs +1 -0
- package/dist/surfaces/cli/output.mjs.map +1 -1
- package/dist/surfaces/cli/sysexits.mjs +2 -1
- package/dist/surfaces/cli/sysexits.mjs.map +1 -1
- package/dist/surfaces/tui/app.mjs +25 -3
- package/dist/surfaces/tui/app.mjs.map +1 -1
- package/dist/surfaces/tui/display-derivation.mjs +23 -74
- package/dist/surfaces/tui/display-derivation.mjs.map +1 -1
- package/dist/surfaces/tui/event-log.mjs +35 -23
- package/dist/surfaces/tui/event-log.mjs.map +1 -1
- package/dist/surfaces/tui/input.mjs +5 -1
- package/dist/surfaces/tui/input.mjs.map +1 -1
- package/dist/surfaces/tui/plain-renderer.mjs +70 -34
- package/dist/surfaces/tui/plain-renderer.mjs.map +1 -1
- package/package.json +24 -4
- package/dist/contracts/liveness-classifier.d.mts +0 -19
- package/dist/contracts/network-resolver.d.mts +0 -15
- package/dist/contracts/network-resolver.mjs +0 -7
- package/dist/contracts/network-resolver.mjs.map +0 -1
- package/dist/orchestrators/codegen/extras.mjs +0 -16
- package/dist/orchestrators/codegen/extras.mjs.map +0 -1
- package/dist/plugins/deepbook/routable.mjs +0 -21
- package/dist/plugins/deepbook/routable.mjs.map +0 -1
- package/dist/plugins/faucet/service.d.mts +0 -20
- package/dist/plugins/faucet/strategies/sui-local.d.mts +0 -23
- package/dist/plugins/faucet/strategies/sui-local.mjs.map +0 -1
- package/dist/plugins/sui/seed-objects.d.mts +0 -18
- package/dist/plugins/sui/seed-objects.mjs +0 -25
- package/dist/plugins/sui/seed-objects.mjs.map +0 -1
- package/dist/runtime/built-in-plugin-layers.mjs.map +0 -1
- package/dist/substrate/runtime/context-helpers.mjs +0 -32
- package/dist/substrate/runtime/context-helpers.mjs.map +0 -1
- package/dist/substrate/runtime/run.mjs.map +0 -1
- package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs +0 -30
- package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs.map +0 -1
- package/dist/substrate/runtime/supervisor.d.mts +0 -1
- package/dist/substrate/runtime/supervisor.mjs +0 -831
- package/dist/substrate/runtime/supervisor.mjs.map +0 -1
- package/dist/surfaces/cli/commands/doctor-probes.mjs.map +0 -1
- /package/dist/{substrate/runtime/strategy-registry/faucet-capability-for.d.mts → plugins/faucet/http.d.mts} +0 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { logDebugAndFallback } from "../../substrate/runtime/observability/ignore-with-log.mjs";
|
|
2
|
+
import { LivenessProbeScope, layerLivenessProbeScope } from "../../substrate/runtime/cross-process/liveness.mjs";
|
|
3
|
+
import { readRoster } from "../../substrate/runtime/cross-process/roster.mjs";
|
|
4
|
+
import "../../substrate/runtime/observability/index.mjs";
|
|
5
|
+
import { DockerSpawner, layerDockerHostDefault } from "../../runtime/docker/client.mjs";
|
|
6
|
+
import { LabelKey } from "../../runtime/docker/labels.mjs";
|
|
7
|
+
import { listDevstackContainers, listDevstackContainersByKind, listDevstackImages, listDevstackNetworks, listDevstackVolumes } from "../../runtime/docker/inventory.mjs";
|
|
8
|
+
import { removeDevstackContainers, removeDevstackContainersByKindAndName, removeDevstackImages, removeDevstackNetworksBestEffort, removeDevstackVolumes } from "../../runtime/docker/sweep.mjs";
|
|
9
|
+
import "../../runtime/docker/index.mjs";
|
|
10
|
+
import { ROUTER_KIND_LABEL_VALUE, ROUTER_SHARED_APP } from "../router/sentinels.mjs";
|
|
11
|
+
import "../../substrate/runtime/managed-container.mjs";
|
|
12
|
+
import "../../substrate/runtime/cross-process/index.mjs";
|
|
13
|
+
import { removeRouterProfileStateForDockerStack } from "../router/cleanup.mjs";
|
|
14
|
+
import { failPhase } from "./errors.mjs";
|
|
15
|
+
import { Effect, Layer } from "effect";
|
|
16
|
+
import { existsSync } from "node:fs";
|
|
17
|
+
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
import * as NodePath from "@effect/platform-node/NodePath";
|
|
20
|
+
import * as NodeChildProcessSpawner from "@effect/platform-node/NodeChildProcessSpawner";
|
|
21
|
+
import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner";
|
|
22
|
+
//#region src/orchestrators/lifecycle-prune/index.ts
|
|
23
|
+
const layerDockerSpawnerFromNode = Layer.effect(DockerSpawner, Effect.gen(function* () {
|
|
24
|
+
return yield* ChildProcessSpawner;
|
|
25
|
+
}));
|
|
26
|
+
const dockerLayer = Layer.merge(layerDockerHostDefault, layerDockerSpawnerFromNode.pipe(Layer.provideMerge(NodeChildProcessSpawner.layer.pipe(Layer.provideMerge(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer))))));
|
|
27
|
+
const emptyBucket = () => ({
|
|
28
|
+
containers: 0,
|
|
29
|
+
runningContainers: 0,
|
|
30
|
+
networks: 0,
|
|
31
|
+
volumes: 0,
|
|
32
|
+
images: 0
|
|
33
|
+
});
|
|
34
|
+
/** Public, parser-safe key for a `(app, stack)` group. Display-only —
|
|
35
|
+
* internal grouping uses the structural `{app, stack}` tuple via the
|
|
36
|
+
* `GroupBuckets` map below, so a slash inside `app` or `stack` cannot
|
|
37
|
+
* produce a wrong tuple. The separator is a forward slash for human-
|
|
38
|
+
* readable log/JSON output (`arena/main`); membership tests round-trip
|
|
39
|
+
* through this same constructor so callers never re-split the key —
|
|
40
|
+
* any potential `/`-in-app-or-stack ambiguity is resolved structurally
|
|
41
|
+
* by `GroupBuckets`, not by string-splitting the key. */
|
|
42
|
+
const GROUP_KEY_SEPARATOR = "/";
|
|
43
|
+
const lifecyclePruneGroupKey = (app, stack) => `${app}${GROUP_KEY_SEPARATOR}${stack}`;
|
|
44
|
+
/** Map keyed on `(app, stack)` tuples without string encoding. Tuple
|
|
45
|
+
* equality is achieved by interning each `(app, stack)` pair through
|
|
46
|
+
* a nested `app → stack → bucket` index, so the map is collision-
|
|
47
|
+
* free even when `app` or `stack` contains a separator character. */
|
|
48
|
+
var GroupBuckets = class {
|
|
49
|
+
buckets = /* @__PURE__ */ new Map();
|
|
50
|
+
get(identity) {
|
|
51
|
+
let perApp = this.buckets.get(identity.app);
|
|
52
|
+
if (perApp === void 0) {
|
|
53
|
+
perApp = /* @__PURE__ */ new Map();
|
|
54
|
+
this.buckets.set(identity.app, perApp);
|
|
55
|
+
}
|
|
56
|
+
let bucket = perApp.get(identity.stack);
|
|
57
|
+
if (bucket === void 0) {
|
|
58
|
+
bucket = emptyBucket();
|
|
59
|
+
perApp.set(identity.stack, bucket);
|
|
60
|
+
}
|
|
61
|
+
return bucket;
|
|
62
|
+
}
|
|
63
|
+
*entries() {
|
|
64
|
+
for (const [app, perApp] of this.buckets) for (const [stack, bucket] of perApp) yield {
|
|
65
|
+
app,
|
|
66
|
+
stack,
|
|
67
|
+
bucket
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const readAppStack = (labels) => {
|
|
72
|
+
const app = labels[LabelKey.app];
|
|
73
|
+
const stack = labels[LabelKey.stack];
|
|
74
|
+
if (app === void 0 || app.length === 0) return null;
|
|
75
|
+
if (stack === void 0 || stack.length === 0) return null;
|
|
76
|
+
return {
|
|
77
|
+
app,
|
|
78
|
+
stack
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
const routerStackForContainer = (name) => ({
|
|
82
|
+
app: ROUTER_SHARED_APP,
|
|
83
|
+
stack: name
|
|
84
|
+
});
|
|
85
|
+
/** Read the live-PID set for a stack from its roster.
|
|
86
|
+
*
|
|
87
|
+
* Path policy: `<runtimeRoot>/stacks/<stack>/roster.json`. This is the
|
|
88
|
+
* same key the entire substrate uses — see `substrate/runtime/paths.ts`
|
|
89
|
+
* (`path.join(root, 'stacks', identity.stack)`) — so the path here
|
|
90
|
+
* carries no NEW cross-attribution risk beyond the substrate-wide
|
|
91
|
+
* decision to key per-stack-disk-state on `stack` alone. (Two stacks
|
|
92
|
+
* in DIFFERENT apps that pick the SAME stack name would already share
|
|
93
|
+
* every other on-disk artifact — `state.json`, `cache/`, `snapshots/`,
|
|
94
|
+
* `stack.lock`, `container-claims.json`. The Docker-level discriminator
|
|
95
|
+
* is the container/network/volume NAME, which IS `<app>-<stack>-…`
|
|
96
|
+
* composed inside each plugin; so cross-attribution at the daemon
|
|
97
|
+
* level is prevented by name uniqueness even when disk state collides.)
|
|
98
|
+
*
|
|
99
|
+
* This function deliberately omits `app` from the path so the
|
|
100
|
+
* prune sweeper agrees with every other consumer (`cli/main.ts`,
|
|
101
|
+
* `cli/wirings/identity.ts`, `cli/wirings/up.ts`, the roster module
|
|
102
|
+
* itself). Promoting `app` into the stack-state path is an
|
|
103
|
+
* architectural change that must update all of those call sites in
|
|
104
|
+
* lockstep — out of scope for this orchestrator. */
|
|
105
|
+
const livePidsForStack = (runtimeRoot, stack) => {
|
|
106
|
+
const rosterFile = join(runtimeRoot, "stacks", stack, "roster.json");
|
|
107
|
+
if (!existsSync(rosterFile)) return Effect.succeed([]);
|
|
108
|
+
return Effect.gen(function* () {
|
|
109
|
+
const doc = yield* readRoster(rosterFile).pipe(logDebugAndFallback(null, "lifecycle-prune: roster read failed; treating as empty", { rosterFile }));
|
|
110
|
+
if (doc === null) return [];
|
|
111
|
+
const probe = yield* LivenessProbeScope;
|
|
112
|
+
const pids = [];
|
|
113
|
+
for (const holder of doc.holders) if ((yield* probe.probeHolderLiveness(holder).pipe(logDebugAndFallback("alive", "lifecycle-prune: liveness check failed; assuming alive", { pid: holder.pid }))) === "alive") pids.push(holder.pid);
|
|
114
|
+
return pids;
|
|
115
|
+
}).pipe(Effect.provide(layerLivenessProbeScope));
|
|
116
|
+
};
|
|
117
|
+
/** True when the group is one of the two shared shapes the
|
|
118
|
+
* orchestrator knows about: `_per-app_` (cross-stack-per-app shared)
|
|
119
|
+
* or the router-singleton (`ROUTER_SHARED_APP`). Surfaces consult
|
|
120
|
+
* this instead of recomputing the predicate. */
|
|
121
|
+
const isSharedLifecyclePruneGroup = (app, stack) => stack === "_per-app_" || app === "devstack-router";
|
|
122
|
+
const sharedKindFor = (app, stack) => {
|
|
123
|
+
if (app === "devstack-router") return "router";
|
|
124
|
+
if (stack === "_per-app_") return "per-app-shared";
|
|
125
|
+
return null;
|
|
126
|
+
};
|
|
127
|
+
/** True when the group represents a router-singleton resource set.
|
|
128
|
+
* Exported so L4 surfaces can call this rather than re-implement the
|
|
129
|
+
* router-stack naming predicate. */
|
|
130
|
+
const isRouterLifecyclePruneGroup = (group) => group.app === "devstack-router" && group.stack.startsWith(`devstack-router-`);
|
|
131
|
+
const isRouterGroup = isRouterLifecyclePruneGroup;
|
|
132
|
+
const isSharedGroup = isSharedLifecyclePruneGroup;
|
|
133
|
+
/** Label-tuple match for the `removeDevstack*` sweepers — router-shared
|
|
134
|
+
* resources stamp `{app: ROUTER_SHARED_APP, stack: <profile-name>}`
|
|
135
|
+
* (see `traefik-container.ts:ensureNetwork`) and the inventory pass
|
|
136
|
+
* buckets them under `routerStackForContainer(container.name)`, so for
|
|
137
|
+
* router groups the tuple is `{app: ROUTER_SHARED_APP, stack: group.stack}`.
|
|
138
|
+
* For non-router groups it's the bucket's literal `{app, stack}`. The
|
|
139
|
+
* branch is structurally identical to the container-removal branch's
|
|
140
|
+
* router-specific dispatch via `removeDevstackContainersByKindAndName`,
|
|
141
|
+
* so dry-run inventory counts and real-run removal stay in lockstep
|
|
142
|
+
* for router resources. Exported so tests can pin the dry-run ↔
|
|
143
|
+
* real-run parity without standing up a Docker daemon. */
|
|
144
|
+
const lifecyclePruneRemovalMatchTuple = (group) => isRouterGroup(group) ? {
|
|
145
|
+
app: ROUTER_SHARED_APP,
|
|
146
|
+
stack: group.stack
|
|
147
|
+
} : {
|
|
148
|
+
app: group.app,
|
|
149
|
+
stack: group.stack
|
|
150
|
+
};
|
|
151
|
+
const matchTupleForGroup = lifecyclePruneRemovalMatchTuple;
|
|
152
|
+
const collectLifecyclePruneInventory = (options) => Effect.gen(function* () {
|
|
153
|
+
const [containers, routerContainers, networks, volumes, images] = yield* Effect.all([
|
|
154
|
+
listDevstackContainers(),
|
|
155
|
+
listDevstackContainersByKind(ROUTER_KIND_LABEL_VALUE),
|
|
156
|
+
listDevstackNetworks(),
|
|
157
|
+
listDevstackVolumes(),
|
|
158
|
+
listDevstackImages()
|
|
159
|
+
], { concurrency: "unbounded" }).pipe(Effect.provide(dockerLayer), Effect.mapError(failPhase("inventory")));
|
|
160
|
+
const buckets = new GroupBuckets();
|
|
161
|
+
for (const container of containers) {
|
|
162
|
+
const identity = readAppStack(container.labels);
|
|
163
|
+
if (identity === null) continue;
|
|
164
|
+
const bucket = buckets.get(identity);
|
|
165
|
+
bucket.containers += 1;
|
|
166
|
+
if (container.state === "running") bucket.runningContainers += 1;
|
|
167
|
+
}
|
|
168
|
+
for (const container of routerContainers) {
|
|
169
|
+
const bucket = buckets.get(routerStackForContainer(container.name));
|
|
170
|
+
bucket.containers += 1;
|
|
171
|
+
if (container.state === "running") bucket.runningContainers += 1;
|
|
172
|
+
}
|
|
173
|
+
for (const network of networks) {
|
|
174
|
+
const identity = readAppStack(network.labels);
|
|
175
|
+
if (identity === null) continue;
|
|
176
|
+
buckets.get(identity).networks += 1;
|
|
177
|
+
}
|
|
178
|
+
for (const volume of volumes) {
|
|
179
|
+
const identity = readAppStack(volume.labels);
|
|
180
|
+
if (identity === null) continue;
|
|
181
|
+
buckets.get(identity).volumes += 1;
|
|
182
|
+
}
|
|
183
|
+
for (const image of images) {
|
|
184
|
+
const identity = readAppStack(image.labels);
|
|
185
|
+
if (identity === null) continue;
|
|
186
|
+
buckets.get(identity).images += 1;
|
|
187
|
+
}
|
|
188
|
+
const groups = [];
|
|
189
|
+
for (const entry of buckets.entries()) {
|
|
190
|
+
const { app, stack, bucket } = entry;
|
|
191
|
+
const livePids = app === "devstack-router" ? [] : yield* livePidsForStack(options.runtimeRoot, stack);
|
|
192
|
+
const live = livePids.length > 0 || bucket.runningContainers > 0;
|
|
193
|
+
groups.push({
|
|
194
|
+
key: lifecyclePruneGroupKey(app, stack),
|
|
195
|
+
app,
|
|
196
|
+
stack,
|
|
197
|
+
live,
|
|
198
|
+
livePids,
|
|
199
|
+
shared: isSharedGroup(app, stack),
|
|
200
|
+
sharedKind: sharedKindFor(app, stack),
|
|
201
|
+
autoPrunable: isRouterGroup({
|
|
202
|
+
app,
|
|
203
|
+
stack
|
|
204
|
+
}),
|
|
205
|
+
containers: bucket.containers,
|
|
206
|
+
runningContainers: bucket.runningContainers,
|
|
207
|
+
networks: bucket.networks,
|
|
208
|
+
volumes: bucket.volumes,
|
|
209
|
+
images: bucket.images
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
groups.sort((a, b) => {
|
|
213
|
+
if (a.app !== b.app) return a.app < b.app ? -1 : 1;
|
|
214
|
+
return a.stack < b.stack ? -1 : a.stack > b.stack ? 1 : 0;
|
|
215
|
+
});
|
|
216
|
+
return { groups };
|
|
217
|
+
}).pipe(Effect.withSpan("orchestrator.lifecycle-prune.inventory"));
|
|
218
|
+
const selectedGroups = (inventory, selection) => {
|
|
219
|
+
const selected = new Set(selection.groupKeys);
|
|
220
|
+
return inventory.groups.filter((group) => selected.has(group.key));
|
|
221
|
+
};
|
|
222
|
+
/** Defensive enforcement of the per-app-shared pinning rule for a
|
|
223
|
+
* caller-supplied selection. Resolves `selection.groupKeys` against
|
|
224
|
+
* `inventory` and drops any `per-app-shared` group whose app still has
|
|
225
|
+
* a live sibling — reusing the SAME predicate as
|
|
226
|
+
* `defaultLifecyclePruneSelection` so a selection built outside the
|
|
227
|
+
* default path (interactive picker, scripted, programmatic) can never
|
|
228
|
+
* remove a shared group a live sibling depends on. Exported so the
|
|
229
|
+
* invariant is unit-testable without a Docker daemon. */
|
|
230
|
+
const enforcePinnedLifecyclePruneSelection = (inventory, selection) => {
|
|
231
|
+
const pinned = lifecyclePruneAppsWithLiveSiblings(inventory);
|
|
232
|
+
return selectedGroups(inventory, selection).filter((group) => !(group.sharedKind === "per-app-shared" && pinned.has(group.app)));
|
|
233
|
+
};
|
|
234
|
+
/** Default resource scope for the lifecycle-prune orchestrator —
|
|
235
|
+
* containers + networks + volumes, never images. Surfaces consume
|
|
236
|
+
* this directly so the default never drifts between L3 and L4. */
|
|
237
|
+
const DEFAULT_LIFECYCLE_PRUNE_RESOURCES = {
|
|
238
|
+
containers: true,
|
|
239
|
+
networks: true,
|
|
240
|
+
volumes: true,
|
|
241
|
+
images: false
|
|
242
|
+
};
|
|
243
|
+
/** Apps with at least one live non-shared group — their `_per-app_`
|
|
244
|
+
* shared resources stay pinned because something under the app is
|
|
245
|
+
* still running. Exported so L4 surfaces consume the orchestrator's
|
|
246
|
+
* pinning predicate. */
|
|
247
|
+
const lifecyclePruneAppsWithLiveSiblings = (inventory) => {
|
|
248
|
+
const apps = /* @__PURE__ */ new Set();
|
|
249
|
+
for (const group of inventory.groups) if (!group.shared && group.live) apps.add(group.app);
|
|
250
|
+
return apps;
|
|
251
|
+
};
|
|
252
|
+
const groupHasResource = (group, resources) => resources.containers && group.containers > 0 || resources.networks && group.networks > 0 || resources.volumes && group.volumes > 0 || resources.images && group.images > 0;
|
|
253
|
+
/** Default selection: every non-live group whose shared shape is
|
|
254
|
+
* prunable in non-interactive flows. Surfaces and the live-supervisor
|
|
255
|
+
* command handler share this so the orchestrator owns the policy. */
|
|
256
|
+
const defaultLifecyclePruneSelection = (inventory, resources = DEFAULT_LIFECYCLE_PRUNE_RESOURCES) => {
|
|
257
|
+
const pinned = lifecyclePruneAppsWithLiveSiblings(inventory);
|
|
258
|
+
const keys = [];
|
|
259
|
+
for (const group of inventory.groups) {
|
|
260
|
+
if (group.live) continue;
|
|
261
|
+
if (!groupHasResource(group, resources)) continue;
|
|
262
|
+
if (group.sharedKind === "per-app-shared" && pinned.has(group.app)) continue;
|
|
263
|
+
if (group.shared && !group.autoPrunable && group.sharedKind !== "per-app-shared") continue;
|
|
264
|
+
keys.push(group.key);
|
|
265
|
+
}
|
|
266
|
+
return keys;
|
|
267
|
+
};
|
|
268
|
+
const runLifecyclePrune = (options, selection) => Effect.gen(function* () {
|
|
269
|
+
const groups = enforcePinnedLifecyclePruneSelection(yield* collectLifecyclePruneInventory(options), selection);
|
|
270
|
+
let skippedLiveGroups = 0;
|
|
271
|
+
let containersRemoved = 0;
|
|
272
|
+
let networksRemoved = 0;
|
|
273
|
+
let networksSkipped = 0;
|
|
274
|
+
let volumesRemoved = 0;
|
|
275
|
+
let imagesRemoved = 0;
|
|
276
|
+
const prunableGroups = [];
|
|
277
|
+
for (const group of groups) {
|
|
278
|
+
if (group.live) {
|
|
279
|
+
skippedLiveGroups += 1;
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (selection.dryRun) {
|
|
283
|
+
prunableGroups.push(group);
|
|
284
|
+
if (selection.resources.containers) containersRemoved += group.containers;
|
|
285
|
+
if (selection.resources.networks) networksRemoved += group.networks;
|
|
286
|
+
if (selection.resources.volumes) volumesRemoved += group.volumes;
|
|
287
|
+
if (selection.resources.images) imagesRemoved += group.images;
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
if (!isRouterGroup(group)) {
|
|
291
|
+
if ((yield* livePidsForStack(options.runtimeRoot, group.stack)).length > 0) {
|
|
292
|
+
skippedLiveGroups += 1;
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
prunableGroups.push(group);
|
|
297
|
+
}
|
|
298
|
+
const foreignNetworkHolders = [];
|
|
299
|
+
const staleNetworkEndpoints = [];
|
|
300
|
+
yield* Effect.gen(function* () {
|
|
301
|
+
if (!selection.dryRun && selection.resources.containers) for (const group of prunableGroups) if (isRouterGroup(group)) containersRemoved += yield* removeDevstackContainersByKindAndName(ROUTER_KIND_LABEL_VALUE, group.stack).pipe(Effect.mapError(failPhase("remove-containers")));
|
|
302
|
+
else {
|
|
303
|
+
const match = {
|
|
304
|
+
app: group.app,
|
|
305
|
+
stack: group.stack
|
|
306
|
+
};
|
|
307
|
+
containersRemoved += yield* removeDevstackContainers(match).pipe(Effect.mapError(failPhase("remove-containers")));
|
|
308
|
+
}
|
|
309
|
+
if (!selection.dryRun && selection.resources.networks) for (const group of prunableGroups) {
|
|
310
|
+
const result = yield* removeDevstackNetworksBestEffort(matchTupleForGroup(group)).pipe(Effect.mapError(failPhase("remove-networks")));
|
|
311
|
+
networksRemoved += result.removed;
|
|
312
|
+
networksSkipped += result.skippedInUse;
|
|
313
|
+
for (const holder of result.foreignHolders) foreignNetworkHolders.push(holder);
|
|
314
|
+
for (const ep of result.staleEndpoints) staleNetworkEndpoints.push(ep);
|
|
315
|
+
}
|
|
316
|
+
if (!selection.dryRun && selection.resources.volumes) for (const group of prunableGroups) {
|
|
317
|
+
const match = matchTupleForGroup(group);
|
|
318
|
+
volumesRemoved += yield* removeDevstackVolumes(match).pipe(Effect.mapError(failPhase("remove-volumes")));
|
|
319
|
+
}
|
|
320
|
+
if (!selection.dryRun && selection.resources.images) for (const group of prunableGroups) {
|
|
321
|
+
const match = matchTupleForGroup(group);
|
|
322
|
+
imagesRemoved += yield* removeDevstackImages(match).pipe(Effect.mapError(failPhase("remove-images")));
|
|
323
|
+
}
|
|
324
|
+
}).pipe(Effect.provide(dockerLayer));
|
|
325
|
+
if (!selection.dryRun) yield* Effect.gen(function* () {
|
|
326
|
+
for (const group of prunableGroups) {
|
|
327
|
+
if (!isRouterGroup(group)) continue;
|
|
328
|
+
yield* removeRouterProfileStateForDockerStack({
|
|
329
|
+
runtimeRoot: options.runtimeRoot,
|
|
330
|
+
routerStack: group.stack
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}).pipe(Effect.provide(NodeFileSystem.layer), Effect.withSpan("orchestrator.lifecycle-prune.removeRouterProfileState"));
|
|
334
|
+
return {
|
|
335
|
+
inspectedGroups: groups.length,
|
|
336
|
+
selectedGroups: prunableGroups.length,
|
|
337
|
+
skippedLiveGroups,
|
|
338
|
+
containersRemoved,
|
|
339
|
+
networksRemoved,
|
|
340
|
+
networksSkipped,
|
|
341
|
+
volumesRemoved,
|
|
342
|
+
imagesRemoved,
|
|
343
|
+
foreignNetworkHolders,
|
|
344
|
+
staleNetworkEndpoints
|
|
345
|
+
};
|
|
346
|
+
}).pipe(Effect.withSpan("orchestrator.lifecycle-prune.run"));
|
|
347
|
+
//#endregion
|
|
348
|
+
export { DEFAULT_LIFECYCLE_PRUNE_RESOURCES, collectLifecyclePruneInventory, defaultLifecyclePruneSelection, lifecyclePruneAppsWithLiveSiblings, runLifecyclePrune };
|
|
349
|
+
|
|
350
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["joinPath"],"sources":["../../../src/orchestrators/lifecycle-prune/index.ts"],"sourcesContent":["// Lifecycle prune orchestrator — cross-stack Docker sweep.\n//\n// Architecture § L3 orchestrators. Walks the L1 docker inventory (the\n// reference `ContainerRuntime` adapter), groups labelled resources by\n// `(app, stack)`, consults the cross-process roster + claim ledger for\n// liveness, and dispatches removal through the sibling router cleanup\n// helper for router-shared resources.\n//\n// Name-blindness: this orchestrator never names a service. It walks\n// the engine label tuple (`LabelKey.app` / `LabelKey.stack`) which is\n// L1-substrate-level vocabulary and dispatches on the\n// `ROUTER_SHARED_APP` sentinel exported by the router orchestrator.\n//\n// Consumers: `cli/prune-direct.ts` (the L4-adjacent CLI infrastructure\n// for the `devstack prune` verb) calls `runLifecyclePrune` /\n// `collectLifecyclePruneInventory`. No L4 surface module imports L1\n// runtime adapters directly; that boundary lives here.\n\nimport { existsSync } from 'node:fs';\nimport { join as joinPath } from 'node:path';\n\nimport * as NodeChildProcessSpawner from '@effect/platform-node/NodeChildProcessSpawner';\nimport * as NodeFileSystem from '@effect/platform-node/NodeFileSystem';\nimport * as NodePath from '@effect/platform-node/NodePath';\nimport { Effect, Layer } from 'effect';\nimport { ChildProcessSpawner } from 'effect/unstable/process/ChildProcessSpawner';\n\nimport {\n\tDockerHost,\n\tDockerSpawner,\n\ttype ForeignNetworkHolder,\n\tLabelKey,\n\tlayerDockerHostDefault,\n\tlistDevstackContainers,\n\tlistDevstackContainersByKind,\n\tlistDevstackImages,\n\tlistDevstackNetworks,\n\tlistDevstackVolumes,\n\tremoveDevstackContainers,\n\tremoveDevstackContainersByKindAndName,\n\tremoveDevstackImages,\n\tremoveDevstackNetworksBestEffort,\n\tremoveDevstackVolumes,\n\ttype StaleNetworkEndpoint,\n} from '../../runtime/docker/index.ts';\nimport {\n\tlayerLivenessProbeScope,\n\tLivenessProbeScope,\n\treadRoster,\n} from '../../substrate/runtime/cross-process/index.ts';\nimport { PER_APP_SHARED_STACK } from '../../substrate/runtime/managed-container.ts';\nimport { logDebugAndFallback } from '../../substrate/runtime/observability/index.ts';\nimport { ROUTER_SHARED_APP, removeRouterProfileStateForDockerStack } from '../router/cleanup.ts';\n// Import the kind sentinel from its canonical source rather than the\n// `traefik-container.ts` re-export — the router-singleton label literals\n// live in `sentinels.ts`, and lifecycle-prune only needs the constant\n// (not the container ops), so depending on the sentinel module keeps the\n// import edge minimal and the source-of-truth direct.\nimport { ROUTER_KIND_LABEL_VALUE } from '../router/sentinels.ts';\nimport { failPhase, type LifecyclePruneError } from './errors.ts';\n\nexport { LifecyclePruneError, LifecyclePrunePhase } from './errors.ts';\n\n// -----------------------------------------------------------------------------\n// Public shapes — mirror `surfaces/cli/commands/prune.ts` field-for-field so\n// the verb dispatcher can consume the orchestrator output without an adapter.\n// -----------------------------------------------------------------------------\n\nexport interface LifecyclePruneOptions {\n\treadonly runtimeRoot: string;\n}\n\nexport interface LifecyclePruneGroup {\n\treadonly key: string;\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly live: boolean;\n\treadonly livePids: ReadonlyArray<number>;\n\treadonly shared: boolean;\n\t/** Discriminator for shared-resource groups. `'per-app-shared'`\n\t * groups (the `_per-app_` synthetic stack) stay pinned while any\n\t * sibling stack under the same app is live; `'router'` groups are\n\t * auto-prunable when no app pins them. `null` for normal groups. */\n\treadonly sharedKind: SharedGroupKind | null;\n\t/** True when the group represents a router-shared resource set that\n\t * is auto-prunable in non-interactive flows (`devstack prune --all`).\n\t * Computed by the orchestrator so surfaces never recompute the\n\t * router-stack naming predicate. */\n\treadonly autoPrunable: boolean;\n\treadonly containers: number;\n\treadonly runningContainers: number;\n\treadonly networks: number;\n\treadonly volumes: number;\n\treadonly images: number;\n}\n\n/** Kind of a shared-resource group. `'per-app-shared'` groups belong\n * to the `PER_APP_SHARED_STACK` synthetic stack under a real app and\n * stay pinned while a sibling stack is live. `'router'` groups are\n * router-singleton resources. */\nexport type SharedGroupKind = 'per-app-shared' | 'router';\n\nexport interface LifecyclePruneInventory {\n\treadonly groups: ReadonlyArray<LifecyclePruneGroup>;\n}\n\nexport interface LifecyclePruneResourceScope {\n\treadonly containers: boolean;\n\treadonly networks: boolean;\n\treadonly volumes: boolean;\n\treadonly images: boolean;\n}\n\nexport interface LifecyclePruneSelection {\n\treadonly groupKeys: ReadonlyArray<string>;\n\treadonly resources: LifecyclePruneResourceScope;\n\treadonly dryRun: boolean;\n}\n\nexport interface LifecyclePruneSummary {\n\treadonly inspectedGroups: number;\n\treadonly selectedGroups: number;\n\treadonly skippedLiveGroups: number;\n\treadonly containersRemoved: number;\n\treadonly networksRemoved: number;\n\treadonly networksSkipped: number;\n\treadonly volumesRemoved: number;\n\treadonly imagesRemoved: number;\n\t/** Non-devstack containers still holding networks that prune could\n\t * not remove. Empty when every network came down cleanly. */\n\treadonly foreignNetworkHolders: ReadonlyArray<ForeignNetworkHolder>;\n\t/** Endpoints Docker insists exist on a network but which no CLI/API\n\t * path can address — symptom of a Docker engine bug (the bridge\n\t * driver leaked endpoint metadata after a container was reaped).\n\t * Only a Docker daemon restart clears these. */\n\treadonly staleNetworkEndpoints: ReadonlyArray<StaleNetworkEndpoint>;\n}\n\n// -----------------------------------------------------------------------------\n// Internal helpers\n// -----------------------------------------------------------------------------\n\ntype ResourceBucket = {\n\tcontainers: number;\n\trunningContainers: number;\n\tnetworks: number;\n\tvolumes: number;\n\timages: number;\n};\n\nconst layerDockerSpawnerFromNode: Layer.Layer<DockerSpawner, never, ChildProcessSpawner> =\n\tLayer.effect(\n\t\tDockerSpawner,\n\t\tEffect.gen(function* () {\n\t\t\treturn yield* ChildProcessSpawner;\n\t\t}),\n\t);\n\nconst dockerLayer: Layer.Layer<DockerHost | DockerSpawner> = Layer.merge(\n\tlayerDockerHostDefault,\n\tlayerDockerSpawnerFromNode.pipe(\n\t\tLayer.provideMerge(\n\t\t\tNodeChildProcessSpawner.layer.pipe(\n\t\t\t\tLayer.provideMerge(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer)),\n\t\t\t),\n\t\t),\n\t),\n);\n\nconst emptyBucket = (): ResourceBucket => ({\n\tcontainers: 0,\n\trunningContainers: 0,\n\tnetworks: 0,\n\tvolumes: 0,\n\timages: 0,\n});\n\n/** Public, parser-safe key for a `(app, stack)` group. Display-only —\n * internal grouping uses the structural `{app, stack}` tuple via the\n * `GroupBuckets` map below, so a slash inside `app` or `stack` cannot\n * produce a wrong tuple. The separator is a forward slash for human-\n * readable log/JSON output (`arena/main`); membership tests round-trip\n * through this same constructor so callers never re-split the key —\n * any potential `/`-in-app-or-stack ambiguity is resolved structurally\n * by `GroupBuckets`, not by string-splitting the key. */\nconst GROUP_KEY_SEPARATOR = '/';\n\nexport const lifecyclePruneGroupKey = (app: string, stack: string): string =>\n\t`${app}${GROUP_KEY_SEPARATOR}${stack}`;\n\n/** Map keyed on `(app, stack)` tuples without string encoding. Tuple\n * equality is achieved by interning each `(app, stack)` pair through\n * a nested `app → stack → bucket` index, so the map is collision-\n * free even when `app` or `stack` contains a separator character. */\nclass GroupBuckets {\n\tprivate readonly buckets = new Map<string, Map<string, ResourceBucket>>();\n\n\tget(identity: { readonly app: string; readonly stack: string }): ResourceBucket {\n\t\tlet perApp = this.buckets.get(identity.app);\n\t\tif (perApp === undefined) {\n\t\t\tperApp = new Map<string, ResourceBucket>();\n\t\t\tthis.buckets.set(identity.app, perApp);\n\t\t}\n\t\tlet bucket = perApp.get(identity.stack);\n\t\tif (bucket === undefined) {\n\t\t\tbucket = emptyBucket();\n\t\t\tperApp.set(identity.stack, bucket);\n\t\t}\n\t\treturn bucket;\n\t}\n\n\t*entries(): Iterable<{\n\t\treadonly app: string;\n\t\treadonly stack: string;\n\t\treadonly bucket: ResourceBucket;\n\t}> {\n\t\tfor (const [app, perApp] of this.buckets) {\n\t\t\tfor (const [stack, bucket] of perApp) {\n\t\t\t\tyield { app, stack, bucket };\n\t\t\t}\n\t\t}\n\t}\n}\n\nconst readAppStack = (\n\tlabels: Readonly<Record<string, string>>,\n): { readonly app: string; readonly stack: string } | null => {\n\tconst app = labels[LabelKey.app];\n\tconst stack = labels[LabelKey.stack];\n\tif (app === undefined || app.length === 0) return null;\n\tif (stack === undefined || stack.length === 0) return null;\n\treturn { app, stack };\n};\n\nconst routerStackForContainer = (\n\tname: string,\n): { readonly app: string; readonly stack: string } => ({\n\tapp: ROUTER_SHARED_APP,\n\tstack: name,\n});\n\n/** Read the live-PID set for a stack from its roster.\n *\n * Path policy: `<runtimeRoot>/stacks/<stack>/roster.json`. This is the\n * same key the entire substrate uses — see `substrate/runtime/paths.ts`\n * (`path.join(root, 'stacks', identity.stack)`) — so the path here\n * carries no NEW cross-attribution risk beyond the substrate-wide\n * decision to key per-stack-disk-state on `stack` alone. (Two stacks\n * in DIFFERENT apps that pick the SAME stack name would already share\n * every other on-disk artifact — `state.json`, `cache/`, `snapshots/`,\n * `stack.lock`, `container-claims.json`. The Docker-level discriminator\n * is the container/network/volume NAME, which IS `<app>-<stack>-…`\n * composed inside each plugin; so cross-attribution at the daemon\n * level is prevented by name uniqueness even when disk state collides.)\n *\n * This function deliberately omits `app` from the path so the\n * prune sweeper agrees with every other consumer (`cli/main.ts`,\n * `cli/wirings/identity.ts`, `cli/wirings/up.ts`, the roster module\n * itself). Promoting `app` into the stack-state path is an\n * architectural change that must update all of those call sites in\n * lockstep — out of scope for this orchestrator. */\nconst livePidsForStack = (\n\truntimeRoot: string,\n\tstack: string,\n): Effect.Effect<ReadonlyArray<number>> => {\n\tconst rosterFile = joinPath(runtimeRoot, 'stacks', stack, 'roster.json');\n\tif (!existsSync(rosterFile)) return Effect.succeed([]);\n\t// Yield a fresh `LivenessProbeScope` so a recycled-PID corner case\n\t// (multiple holders sharing one pid in the same roster) forks the OS\n\t// liveness probe once per pid across this scan.\n\treturn Effect.gen(function* () {\n\t\tconst doc = yield* readRoster(rosterFile).pipe(\n\t\t\tlogDebugAndFallback(null, 'lifecycle-prune: roster read failed; treating as empty', {\n\t\t\t\trosterFile,\n\t\t\t}),\n\t\t);\n\t\tif (doc === null) return [];\n\t\tconst probe = yield* LivenessProbeScope;\n\t\tconst pids: Array<number> = [];\n\t\tfor (const holder of doc.holders) {\n\t\t\tconst live = yield* probe\n\t\t\t\t.probeHolderLiveness(holder)\n\t\t\t\t.pipe(\n\t\t\t\t\tlogDebugAndFallback(\n\t\t\t\t\t\t'alive' as const,\n\t\t\t\t\t\t'lifecycle-prune: liveness check failed; assuming alive',\n\t\t\t\t\t\t{ pid: holder.pid },\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\tif (live === 'alive') pids.push(holder.pid);\n\t\t}\n\t\treturn pids;\n\t}).pipe(Effect.provide(layerLivenessProbeScope));\n};\n\n/** True when the group is one of the two shared shapes the\n * orchestrator knows about: `_per-app_` (cross-stack-per-app shared)\n * or the router-singleton (`ROUTER_SHARED_APP`). Surfaces consult\n * this instead of recomputing the predicate. */\nexport const isSharedLifecyclePruneGroup = (app: string, stack: string): boolean =>\n\tstack === PER_APP_SHARED_STACK || app === ROUTER_SHARED_APP;\n\nconst sharedKindFor = (app: string, stack: string): SharedGroupKind | null => {\n\tif (app === ROUTER_SHARED_APP) return 'router';\n\tif (stack === PER_APP_SHARED_STACK) return 'per-app-shared';\n\treturn null;\n};\n\n/** True when the group represents a router-singleton resource set.\n * Exported so L4 surfaces can call this rather than re-implement the\n * router-stack naming predicate. */\nexport const isRouterLifecyclePruneGroup = (\n\tgroup: Pick<LifecyclePruneGroup, 'app' | 'stack'>,\n): boolean => group.app === ROUTER_SHARED_APP && group.stack.startsWith(`${ROUTER_SHARED_APP}-`);\n\n// Internal aliases for back-compat call sites in this file.\nconst isRouterGroup = isRouterLifecyclePruneGroup;\nconst isSharedGroup = isSharedLifecyclePruneGroup;\n\n/** Label-tuple match for the `removeDevstack*` sweepers — router-shared\n * resources stamp `{app: ROUTER_SHARED_APP, stack: <profile-name>}`\n * (see `traefik-container.ts:ensureNetwork`) and the inventory pass\n * buckets them under `routerStackForContainer(container.name)`, so for\n * router groups the tuple is `{app: ROUTER_SHARED_APP, stack: group.stack}`.\n * For non-router groups it's the bucket's literal `{app, stack}`. The\n * branch is structurally identical to the container-removal branch's\n * router-specific dispatch via `removeDevstackContainersByKindAndName`,\n * so dry-run inventory counts and real-run removal stay in lockstep\n * for router resources. Exported so tests can pin the dry-run ↔\n * real-run parity without standing up a Docker daemon. */\nexport const lifecyclePruneRemovalMatchTuple = (\n\tgroup: Pick<LifecyclePruneGroup, 'app' | 'stack'>,\n): { readonly app: string; readonly stack: string } =>\n\tisRouterGroup(group)\n\t\t? { app: ROUTER_SHARED_APP, stack: group.stack }\n\t\t: { app: group.app, stack: group.stack };\n\nconst matchTupleForGroup = lifecyclePruneRemovalMatchTuple;\n\n// -----------------------------------------------------------------------------\n// Orchestrator entry points\n// -----------------------------------------------------------------------------\n\nexport const collectLifecyclePruneInventory = (\n\toptions: LifecyclePruneOptions,\n): Effect.Effect<LifecyclePruneInventory, LifecyclePruneError> =>\n\tEffect.gen(function* () {\n\t\tconst [containers, routerContainers, networks, volumes, images] = yield* Effect.all(\n\t\t\t[\n\t\t\t\tlistDevstackContainers(),\n\t\t\t\tlistDevstackContainersByKind(ROUTER_KIND_LABEL_VALUE),\n\t\t\t\tlistDevstackNetworks(),\n\t\t\t\tlistDevstackVolumes(),\n\t\t\t\tlistDevstackImages(),\n\t\t\t],\n\t\t\t{ concurrency: 'unbounded' },\n\t\t).pipe(Effect.provide(dockerLayer), Effect.mapError(failPhase('inventory')));\n\n\t\tconst buckets = new GroupBuckets();\n\n\t\tfor (const container of containers) {\n\t\t\tconst identity = readAppStack(container.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tconst bucket = buckets.get(identity);\n\t\t\tbucket.containers += 1;\n\t\t\tif (container.state === 'running') bucket.runningContainers += 1;\n\t\t}\n\t\tfor (const container of routerContainers) {\n\t\t\tconst bucket = buckets.get(routerStackForContainer(container.name));\n\t\t\tbucket.containers += 1;\n\t\t\tif (container.state === 'running') bucket.runningContainers += 1;\n\t\t}\n\t\tfor (const network of networks) {\n\t\t\tconst identity = readAppStack(network.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tbuckets.get(identity).networks += 1;\n\t\t}\n\t\tfor (const volume of volumes) {\n\t\t\tconst identity = readAppStack(volume.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tbuckets.get(identity).volumes += 1;\n\t\t}\n\t\tfor (const image of images) {\n\t\t\tconst identity = readAppStack(image.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tbuckets.get(identity).images += 1;\n\t\t}\n\n\t\tconst groups: Array<LifecyclePruneGroup> = [];\n\t\tfor (const entry of buckets.entries()) {\n\t\t\tconst { app, stack, bucket } = entry;\n\t\t\tconst routerGroup = app === ROUTER_SHARED_APP;\n\t\t\tconst livePids = routerGroup ? [] : yield* livePidsForStack(options.runtimeRoot, stack);\n\t\t\t// Liveness is the OR of two independent signals so a live stack\n\t\t\t// is never mis-reported `live:false` (and so never swept by\n\t\t\t// `devstack prune --all`):\n\t\t\t//\n\t\t\t// 1. The per-stack roster carries a live holder (`livePids`\n\t\t\t// — the SAME `<root>/stacks/<stack>/roster.json` probe\n\t\t\t// `ensureNoLiveSupervisor` / `probeSupervisorPresence` use;\n\t\t\t// see `livePidsForStack`). Authoritative supervisor-presence\n\t\t\t// signal; also yields the PID detail.\n\t\t\t// 2. The Docker daemon reports a RUNNING container for the\n\t\t\t// group. A `devstack up` supervisor always has running\n\t\t\t// containers, so this catches a live stack even when the\n\t\t\t// roster probe transiently comes back empty — a missing/\n\t\t\t// stale roster file, a holder reaped by a peer sweep, or a\n\t\t\t// `ps -o lstart` start-time hiccup that harvests a live\n\t\t\t// holder as dead. Running containers are a daemon-\n\t\t\t// authoritative \"not idle\" signal, so this only ever ADDS\n\t\t\t// protection; it never reclassifies as idle a stack the\n\t\t\t// roster probe alone would have protected.\n\t\t\t//\n\t\t\t// The router branch already keyed liveness on\n\t\t\t// `runningContainers > 0` (router singletons carry no per-stack\n\t\t\t// roster); folding the same signal into normal groups makes the\n\t\t\t// classification uniform and closes the under-report.\n\t\t\tconst live = livePids.length > 0 || bucket.runningContainers > 0;\n\t\t\tgroups.push({\n\t\t\t\tkey: lifecyclePruneGroupKey(app, stack),\n\t\t\t\tapp,\n\t\t\t\tstack,\n\t\t\t\tlive,\n\t\t\t\tlivePids,\n\t\t\t\tshared: isSharedGroup(app, stack),\n\t\t\t\tsharedKind: sharedKindFor(app, stack),\n\t\t\t\tautoPrunable: isRouterGroup({ app, stack }),\n\t\t\t\tcontainers: bucket.containers,\n\t\t\t\trunningContainers: bucket.runningContainers,\n\t\t\t\tnetworks: bucket.networks,\n\t\t\t\tvolumes: bucket.volumes,\n\t\t\t\timages: bucket.images,\n\t\t\t});\n\t\t}\n\n\t\tgroups.sort((a, b) => {\n\t\t\tif (a.app !== b.app) return a.app < b.app ? -1 : 1;\n\t\t\treturn a.stack < b.stack ? -1 : a.stack > b.stack ? 1 : 0;\n\t\t});\n\n\t\treturn { groups };\n\t}).pipe(Effect.withSpan('orchestrator.lifecycle-prune.inventory'));\n\nconst selectedGroups = (\n\tinventory: LifecyclePruneInventory,\n\tselection: LifecyclePruneSelection,\n): ReadonlyArray<LifecyclePruneGroup> => {\n\tconst selected = new Set(selection.groupKeys);\n\treturn inventory.groups.filter((group) => selected.has(group.key));\n};\n\n/** Defensive enforcement of the per-app-shared pinning rule for a\n * caller-supplied selection. Resolves `selection.groupKeys` against\n * `inventory` and drops any `per-app-shared` group whose app still has\n * a live sibling — reusing the SAME predicate as\n * `defaultLifecyclePruneSelection` so a selection built outside the\n * default path (interactive picker, scripted, programmatic) can never\n * remove a shared group a live sibling depends on. Exported so the\n * invariant is unit-testable without a Docker daemon. */\nexport const enforcePinnedLifecyclePruneSelection = (\n\tinventory: LifecyclePruneInventory,\n\tselection: LifecyclePruneSelection,\n): ReadonlyArray<LifecyclePruneGroup> => {\n\tconst pinned = lifecyclePruneAppsWithLiveSiblings(inventory);\n\treturn selectedGroups(inventory, selection).filter(\n\t\t(group) => !(group.sharedKind === 'per-app-shared' && pinned.has(group.app)),\n\t);\n};\n\n/** Default resource scope for the lifecycle-prune orchestrator —\n * containers + networks + volumes, never images. Surfaces consume\n * this directly so the default never drifts between L3 and L4. */\nexport const DEFAULT_LIFECYCLE_PRUNE_RESOURCES: LifecyclePruneResourceScope = {\n\tcontainers: true,\n\tnetworks: true,\n\tvolumes: true,\n\timages: false,\n};\n\n/** Apps with at least one live non-shared group — their `_per-app_`\n * shared resources stay pinned because something under the app is\n * still running. Exported so L4 surfaces consume the orchestrator's\n * pinning predicate. */\nexport const lifecyclePruneAppsWithLiveSiblings = (\n\tinventory: LifecyclePruneInventory,\n): ReadonlySet<string> => {\n\tconst apps = new Set<string>();\n\tfor (const group of inventory.groups) {\n\t\tif (!group.shared && group.live) apps.add(group.app);\n\t}\n\treturn apps;\n};\n\nconst groupHasResource = (\n\tgroup: LifecyclePruneGroup,\n\tresources: LifecyclePruneResourceScope,\n): boolean =>\n\t(resources.containers && group.containers > 0) ||\n\t(resources.networks && group.networks > 0) ||\n\t(resources.volumes && group.volumes > 0) ||\n\t(resources.images && group.images > 0);\n\n/** Default selection: every non-live group whose shared shape is\n * prunable in non-interactive flows. Surfaces and the live-supervisor\n * command handler share this so the orchestrator owns the policy. */\nexport const defaultLifecyclePruneSelection = (\n\tinventory: LifecyclePruneInventory,\n\tresources: LifecyclePruneResourceScope = DEFAULT_LIFECYCLE_PRUNE_RESOURCES,\n): ReadonlyArray<string> => {\n\tconst pinned = lifecyclePruneAppsWithLiveSiblings(inventory);\n\tconst keys: string[] = [];\n\tfor (const group of inventory.groups) {\n\t\tif (group.live) continue;\n\t\tif (!groupHasResource(group, resources)) continue;\n\t\tif (group.sharedKind === 'per-app-shared' && pinned.has(group.app)) continue;\n\t\tif (group.shared && !group.autoPrunable && group.sharedKind !== 'per-app-shared') continue;\n\t\tkeys.push(group.key);\n\t}\n\treturn keys;\n};\n\nexport const runLifecyclePrune = (\n\toptions: LifecyclePruneOptions,\n\tselection: LifecyclePruneSelection,\n): Effect.Effect<LifecyclePruneSummary, LifecyclePruneError> =>\n\tEffect.gen(function* () {\n\t\tconst inventory = yield* collectLifecyclePruneInventory(options);\n\t\t// Defensive enforcement: a caller-supplied selection (interactive\n\t\t// picker, scripted `--all`, or a programmatic caller) must never\n\t\t// remove a `per-app-shared` group pinned by a live sibling. The\n\t\t// default selection already drops these (see\n\t\t// `defaultLifecyclePruneSelection`); re-applying the SAME predicate\n\t\t// here guarantees the rule holds even when the selection was built\n\t\t// elsewhere.\n\t\tconst groups = enforcePinnedLifecyclePruneSelection(inventory, selection);\n\t\tlet skippedLiveGroups = 0;\n\t\tlet containersRemoved = 0;\n\t\tlet networksRemoved = 0;\n\t\tlet networksSkipped = 0;\n\t\tlet volumesRemoved = 0;\n\t\tlet imagesRemoved = 0;\n\n\t\tconst prunableGroups: Array<LifecyclePruneGroup> = [];\n\t\tfor (const group of groups) {\n\t\t\tif (group.live) {\n\t\t\t\tskippedLiveGroups += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (selection.dryRun) {\n\t\t\t\tprunableGroups.push(group);\n\t\t\t\tif (selection.resources.containers) containersRemoved += group.containers;\n\t\t\t\tif (selection.resources.networks) networksRemoved += group.networks;\n\t\t\t\tif (selection.resources.volumes) volumesRemoved += group.volumes;\n\t\t\t\tif (selection.resources.images) imagesRemoved += group.images;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// TOCTOU close: `collectLifecyclePruneInventory` probed liveness\n\t\t\t// ONCE at the top of this run. A stack that booted in the window\n\t\t\t// between that snapshot and here (a concurrent `devstack up` of a\n\t\t\t// previously-dead stack) would have its containers/networks/volumes\n\t\t\t// removed out from under the freshly-started supervisor. Re-probe\n\t\t\t// each non-router victim's roster via the SAME helper the inventory\n\t\t\t// pass used (`livePidsForStack`) immediately before committing it to\n\t\t\t// removal, and skip any group that came alive since. Router groups\n\t\t\t// carry no per-stack roster (their inventory liveness is\n\t\t\t// `runningContainers`), so they keep the snapshot classification —\n\t\t\t// mirroring the `routerGroup` branch in the inventory pass.\n\t\t\tif (!isRouterGroup(group)) {\n\t\t\t\tconst livePids = yield* livePidsForStack(options.runtimeRoot, group.stack);\n\t\t\t\tif (livePids.length > 0) {\n\t\t\t\t\tskippedLiveGroups += 1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tprunableGroups.push(group);\n\t\t}\n\n\t\tconst foreignNetworkHolders: Array<ForeignNetworkHolder> = [];\n\t\tconst staleNetworkEndpoints: Array<StaleNetworkEndpoint> = [];\n\t\t// Provide `dockerLayer` ONCE around the whole removal section rather\n\t\t// than re-providing it per group per resource. The inventory pass\n\t\t// already provides it once for its parallel scan; the removal loops\n\t\t// mirror that so the layer's docker-host resolution is built a single\n\t\t// time, not rebuilt for every container/network/volume/image removal.\n\t\t// The per-call `Effect.mapError(failPhase(...))` stays inside each\n\t\t// loop so each removal still carries its phase-specific tag.\n\t\tyield* Effect.gen(function* () {\n\t\t\tif (!selection.dryRun && selection.resources.containers) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tif (isRouterGroup(group)) {\n\t\t\t\t\t\tcontainersRemoved += yield* removeDevstackContainersByKindAndName(\n\t\t\t\t\t\t\tROUTER_KIND_LABEL_VALUE,\n\t\t\t\t\t\t\tgroup.stack,\n\t\t\t\t\t\t).pipe(Effect.mapError(failPhase('remove-containers')));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst match = { app: group.app, stack: group.stack };\n\t\t\t\t\t\tcontainersRemoved += yield* removeDevstackContainers(match).pipe(\n\t\t\t\t\t\t\tEffect.mapError(failPhase('remove-containers')),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!selection.dryRun && selection.resources.networks) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tconst match = matchTupleForGroup(group);\n\t\t\t\t\tconst result = yield* removeDevstackNetworksBestEffort(match).pipe(\n\t\t\t\t\t\tEffect.mapError(failPhase('remove-networks')),\n\t\t\t\t\t);\n\t\t\t\t\tnetworksRemoved += result.removed;\n\t\t\t\t\tnetworksSkipped += result.skippedInUse;\n\t\t\t\t\tfor (const holder of result.foreignHolders) foreignNetworkHolders.push(holder);\n\t\t\t\t\tfor (const ep of result.staleEndpoints) staleNetworkEndpoints.push(ep);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!selection.dryRun && selection.resources.volumes) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tconst match = matchTupleForGroup(group);\n\t\t\t\t\tvolumesRemoved += yield* removeDevstackVolumes(match).pipe(\n\t\t\t\t\t\tEffect.mapError(failPhase('remove-volumes')),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!selection.dryRun && selection.resources.images) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tconst match = matchTupleForGroup(group);\n\t\t\t\t\timagesRemoved += yield* removeDevstackImages(match).pipe(\n\t\t\t\t\t\tEffect.mapError(failPhase('remove-images')),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}).pipe(Effect.provide(dockerLayer));\n\n\t\tif (!selection.dryRun) {\n\t\t\tyield* Effect.gen(function* () {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tif (!isRouterGroup(group)) continue;\n\t\t\t\t\tyield* removeRouterProfileStateForDockerStack({\n\t\t\t\t\t\truntimeRoot: options.runtimeRoot,\n\t\t\t\t\t\trouterStack: group.stack,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}).pipe(\n\t\t\t\tEffect.provide(NodeFileSystem.layer),\n\t\t\t\tEffect.withSpan('orchestrator.lifecycle-prune.removeRouterProfileState'),\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tinspectedGroups: groups.length,\n\t\t\t// Groups actually selected for pruning — `groups.length` minus\n\t\t\t// the live ones skipped above, NOT the inspected total.\n\t\t\tselectedGroups: prunableGroups.length,\n\t\t\tskippedLiveGroups,\n\t\t\tcontainersRemoved,\n\t\t\tnetworksRemoved,\n\t\t\tnetworksSkipped,\n\t\t\tvolumesRemoved,\n\t\t\timagesRemoved,\n\t\t\tforeignNetworkHolders,\n\t\t\tstaleNetworkEndpoints,\n\t\t};\n\t}).pipe(Effect.withSpan('orchestrator.lifecycle-prune.run'));\n"],"mappings":"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"}
|
|
@@ -1,58 +1,57 @@
|
|
|
1
|
+
import { ROUTER_CONTAINER_NAME_PREFIX } from "./sentinels.mjs";
|
|
1
2
|
import { dispatchFileIdFromFilename, parseDispatchRouteFile } from "./file-provider.mjs";
|
|
2
|
-
import { Effect } from "effect";
|
|
3
|
-
import { existsSync, readFileSync, readdirSync, rmSync, statSync } from "node:fs";
|
|
3
|
+
import { Effect, FileSystem } from "effect";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
//#region src/orchestrators/router/cleanup.ts
|
|
6
|
-
const ROUTER_SHARED_APP = "devstack-router";
|
|
7
|
-
const ROUTER_CONTAINER_NAME_PREFIX = "devstack-router-";
|
|
8
6
|
const routerStateRoot = (runtimeRoot) => join(runtimeRoot, "router");
|
|
9
|
-
const safeDirectories = (dir) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
const safeDirectories = (fs, dir) => Effect.gen(function* () {
|
|
8
|
+
const entries = yield* fs.readDirectory(dir).pipe(Effect.catch(() => Effect.succeed([])));
|
|
9
|
+
const directories = [];
|
|
10
|
+
for (const entry of entries) {
|
|
11
|
+
const stat = yield* fs.stat(join(dir, entry)).pipe(Effect.catch(() => Effect.succeed(null)));
|
|
12
|
+
if (stat !== null && stat.type === "Directory") directories.push(entry);
|
|
14
13
|
}
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
return directories;
|
|
15
|
+
});
|
|
16
|
+
const removeRouterDispatchFilesForStack = (inputs) => Effect.gen(function* () {
|
|
17
|
+
const fs = yield* FileSystem.FileSystem;
|
|
17
18
|
let removed = 0;
|
|
18
|
-
for (const profileDir of safeDirectories(routerStateRoot(inputs.runtimeRoot))) {
|
|
19
|
+
for (const profileDir of yield* safeDirectories(fs, routerStateRoot(inputs.runtimeRoot))) {
|
|
19
20
|
const dispatchDir = join(routerStateRoot(inputs.runtimeRoot), profileDir, "dispatch");
|
|
20
|
-
if (!
|
|
21
|
-
|
|
21
|
+
if (!(yield* fs.exists(dispatchDir).pipe(Effect.catch(() => Effect.succeed(false))))) continue;
|
|
22
|
+
const filenames = yield* fs.readDirectory(dispatchDir).pipe(Effect.catch(() => Effect.succeed([])));
|
|
23
|
+
for (const filename of filenames) {
|
|
22
24
|
const dispatchFileId = dispatchFileIdFromFilename(filename);
|
|
23
25
|
if (dispatchFileId === null) continue;
|
|
24
26
|
const file = join(dispatchDir, filename);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
body = readFileSync(file, "utf8");
|
|
28
|
-
} catch {
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
27
|
+
const body = yield* fs.readFileString(file).pipe(Effect.catch(() => Effect.succeed(null)));
|
|
28
|
+
if (body === null) continue;
|
|
31
29
|
const parsed = parseDispatchRouteFile(body, dispatchFileId);
|
|
32
30
|
if (parsed._tag === "valid" && parsed.route.lease?.app === inputs.app && parsed.route.lease.stack === inputs.stack) {
|
|
33
|
-
|
|
31
|
+
yield* fs.remove(file, { force: true }).pipe(Effect.ignore);
|
|
34
32
|
removed += 1;
|
|
35
33
|
}
|
|
36
34
|
}
|
|
37
35
|
}
|
|
38
36
|
return removed;
|
|
39
37
|
});
|
|
40
|
-
const removeRouterProfileStateForDockerStack = (inputs) => Effect.
|
|
38
|
+
const removeRouterProfileStateForDockerStack = (inputs) => Effect.gen(function* () {
|
|
41
39
|
if (!inputs.routerStack.startsWith("devstack-router-")) return 0;
|
|
42
|
-
const fingerprint = inputs.routerStack.slice(
|
|
40
|
+
const fingerprint = inputs.routerStack.slice(ROUTER_CONTAINER_NAME_PREFIX.length);
|
|
43
41
|
if (fingerprint.length === 0) return 0;
|
|
42
|
+
const fs = yield* FileSystem.FileSystem;
|
|
44
43
|
let removed = 0;
|
|
45
|
-
for (const profileDir of safeDirectories(routerStateRoot(inputs.runtimeRoot))) {
|
|
44
|
+
for (const profileDir of yield* safeDirectories(fs, routerStateRoot(inputs.runtimeRoot))) {
|
|
46
45
|
if (!profileDir.endsWith(`-${fingerprint}`)) continue;
|
|
47
|
-
|
|
46
|
+
yield* fs.remove(join(routerStateRoot(inputs.runtimeRoot), profileDir), {
|
|
48
47
|
recursive: true,
|
|
49
48
|
force: true
|
|
50
|
-
});
|
|
49
|
+
}).pipe(Effect.ignore);
|
|
51
50
|
removed += 1;
|
|
52
51
|
}
|
|
53
52
|
return removed;
|
|
54
53
|
});
|
|
55
54
|
//#endregion
|
|
56
|
-
export {
|
|
55
|
+
export { removeRouterDispatchFilesForStack, removeRouterProfileStateForDockerStack };
|
|
57
56
|
|
|
58
57
|
//# sourceMappingURL=cleanup.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanup.mjs","names":[],"sources":["../../../src/orchestrators/router/cleanup.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"cleanup.mjs","names":[],"sources":["../../../src/orchestrators/router/cleanup.ts"],"sourcesContent":["import { join } from 'node:path';\n\nimport { Effect, FileSystem } from 'effect';\n\nimport { dispatchFileIdFromFilename, parseDispatchRouteFile } from './file-provider.ts';\nimport { ROUTER_CONTAINER_NAME_PREFIX } from './sentinels.ts';\n\nexport { ROUTER_CONTAINER_NAME_PREFIX, ROUTER_SHARED_APP } from './sentinels.ts';\n\nconst routerStateRoot = (runtimeRoot: string): string => join(runtimeRoot, 'router');\n\nconst safeDirectories = (\n\tfs: FileSystem.FileSystem,\n\tdir: string,\n): Effect.Effect<ReadonlyArray<string>> =>\n\tEffect.gen(function* () {\n\t\tconst entries = yield* fs\n\t\t\t.readDirectory(dir)\n\t\t\t.pipe(Effect.catch(() => Effect.succeed<ReadonlyArray<string>>([])));\n\t\tconst directories: Array<string> = [];\n\t\tfor (const entry of entries) {\n\t\t\tconst stat = yield* fs.stat(join(dir, entry)).pipe(Effect.catch(() => Effect.succeed(null)));\n\t\t\tif (stat !== null && stat.type === 'Directory') {\n\t\t\t\tdirectories.push(entry);\n\t\t\t}\n\t\t}\n\t\treturn directories;\n\t});\n\nexport const removeRouterDispatchFilesForStack = (inputs: {\n\treadonly runtimeRoot: string;\n\treadonly app: string;\n\treadonly stack: string;\n}): Effect.Effect<number, never, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tlet removed = 0;\n\t\tfor (const profileDir of yield* safeDirectories(fs, routerStateRoot(inputs.runtimeRoot))) {\n\t\t\tconst dispatchDir = join(routerStateRoot(inputs.runtimeRoot), profileDir, 'dispatch');\n\t\t\tconst exists = yield* fs.exists(dispatchDir).pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\tif (!exists) continue;\n\t\t\tconst filenames = yield* fs\n\t\t\t\t.readDirectory(dispatchDir)\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed<ReadonlyArray<string>>([])));\n\t\t\tfor (const filename of filenames) {\n\t\t\t\tconst dispatchFileId = dispatchFileIdFromFilename(filename);\n\t\t\t\tif (dispatchFileId === null) continue;\n\t\t\t\tconst file = join(dispatchDir, filename);\n\t\t\t\tconst body = yield* fs.readFileString(file).pipe(Effect.catch(() => Effect.succeed(null)));\n\t\t\t\tif (body === null) continue;\n\t\t\t\tconst parsed = parseDispatchRouteFile(body, dispatchFileId);\n\t\t\t\tif (\n\t\t\t\t\tparsed._tag === 'valid' &&\n\t\t\t\t\tparsed.route.lease?.app === inputs.app &&\n\t\t\t\t\tparsed.route.lease.stack === inputs.stack\n\t\t\t\t) {\n\t\t\t\t\tyield* fs.remove(file, { force: true }).pipe(Effect.ignore);\n\t\t\t\t\tremoved += 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn removed;\n\t});\n\nexport const removeRouterProfileStateForDockerStack = (inputs: {\n\treadonly runtimeRoot: string;\n\treadonly routerStack: string;\n}): Effect.Effect<number, never, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tif (!inputs.routerStack.startsWith(ROUTER_CONTAINER_NAME_PREFIX)) return 0;\n\t\tconst fingerprint = inputs.routerStack.slice(ROUTER_CONTAINER_NAME_PREFIX.length);\n\t\tif (fingerprint.length === 0) return 0;\n\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tlet removed = 0;\n\t\tfor (const profileDir of yield* safeDirectories(fs, routerStateRoot(inputs.runtimeRoot))) {\n\t\t\tif (!profileDir.endsWith(`-${fingerprint}`)) continue;\n\t\t\tyield* fs\n\t\t\t\t.remove(join(routerStateRoot(inputs.runtimeRoot), profileDir), {\n\t\t\t\t\trecursive: true,\n\t\t\t\t\tforce: true,\n\t\t\t\t})\n\t\t\t\t.pipe(Effect.ignore);\n\t\t\tremoved += 1;\n\t\t}\n\t\treturn removed;\n\t});\n"],"mappings":";;;;;AASA,MAAM,mBAAmB,gBAAgC,KAAK,aAAa,SAAS;AAEpF,MAAM,mBACL,IACA,QAEA,OAAO,IAAI,aAAa;CACvB,MAAM,UAAU,OAAO,GACrB,cAAc,IAAI,CAClB,KAAK,OAAO,YAAY,OAAO,QAA+B,EAAE,CAAC,CAAC,CAAC;CACrE,MAAM,cAA6B,EAAE;AACrC,MAAK,MAAM,SAAS,SAAS;EAC5B,MAAM,OAAO,OAAO,GAAG,KAAK,KAAK,KAAK,MAAM,CAAC,CAAC,KAAK,OAAO,YAAY,OAAO,QAAQ,KAAK,CAAC,CAAC;AAC5F,MAAI,SAAS,QAAQ,KAAK,SAAS,YAClC,aAAY,KAAK,MAAM;;AAGzB,QAAO;EACN;AAEH,MAAa,qCAAqC,WAKjD,OAAO,IAAI,aAAa;CACvB,MAAM,KAAK,OAAO,WAAW;CAC7B,IAAI,UAAU;AACd,MAAK,MAAM,cAAc,OAAO,gBAAgB,IAAI,gBAAgB,OAAO,YAAY,CAAC,EAAE;EACzF,MAAM,cAAc,KAAK,gBAAgB,OAAO,YAAY,EAAE,YAAY,WAAW;AAErF,MAAI,EAAC,OADiB,GAAG,OAAO,YAAY,CAAC,KAAK,OAAO,YAAY,OAAO,QAAQ,MAAM,CAAC,CAAC,EAC/E;EACb,MAAM,YAAY,OAAO,GACvB,cAAc,YAAY,CAC1B,KAAK,OAAO,YAAY,OAAO,QAA+B,EAAE,CAAC,CAAC,CAAC;AACrE,OAAK,MAAM,YAAY,WAAW;GACjC,MAAM,iBAAiB,2BAA2B,SAAS;AAC3D,OAAI,mBAAmB,KAAM;GAC7B,MAAM,OAAO,KAAK,aAAa,SAAS;GACxC,MAAM,OAAO,OAAO,GAAG,eAAe,KAAK,CAAC,KAAK,OAAO,YAAY,OAAO,QAAQ,KAAK,CAAC,CAAC;AAC1F,OAAI,SAAS,KAAM;GACnB,MAAM,SAAS,uBAAuB,MAAM,eAAe;AAC3D,OACC,OAAO,SAAS,WAChB,OAAO,MAAM,OAAO,QAAQ,OAAO,OACnC,OAAO,MAAM,MAAM,UAAU,OAAO,OACnC;AACD,WAAO,GAAG,OAAO,MAAM,EAAE,OAAO,MAAM,CAAC,CAAC,KAAK,OAAO,OAAO;AAC3D,eAAW;;;;AAId,QAAO;EACN;AAEH,MAAa,0CAA0C,WAItD,OAAO,IAAI,aAAa;AACvB,KAAI,CAAC,OAAO,YAAY,WAAA,mBAAwC,CAAE,QAAO;CACzE,MAAM,cAAc,OAAO,YAAY,MAAM,6BAA6B,OAAO;AACjF,KAAI,YAAY,WAAW,EAAG,QAAO;CAErC,MAAM,KAAK,OAAO,WAAW;CAC7B,IAAI,UAAU;AACd,MAAK,MAAM,cAAc,OAAO,gBAAgB,IAAI,gBAAgB,OAAO,YAAY,CAAC,EAAE;AACzF,MAAI,CAAC,WAAW,SAAS,IAAI,cAAc,CAAE;AAC7C,SAAO,GACL,OAAO,KAAK,gBAAgB,OAAO,YAAY,EAAE,WAAW,EAAE;GAC9D,WAAW;GACX,OAAO;GACP,CAAC,CACD,KAAK,OAAO,OAAO;AACrB,aAAW;;AAEZ,QAAO;EACN"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EntrypointConflict, UnknownEntrypoint } from "./errors.mjs";
|
|
2
2
|
import { Context, Effect, Layer } from "effect";
|
|
3
3
|
//#region src/orchestrators/router/entrypoints.ts
|
|
4
|
-
var EntrypointRegistry = class extends Context.Service()("@devstack
|
|
4
|
+
var EntrypointRegistry = class extends Context.Service()("@devstack/orchestrators/router/EntrypointRegistry") {};
|
|
5
5
|
const entrypointFamily = (protocol) => protocol === "tcp" ? "tcp" : "http";
|
|
6
6
|
/** Build a registry from a literal seed. Idempotent on identical
|
|
7
7
|
* `(name, port, protocol)` triples; throws synchronously on conflict
|
|
@@ -75,6 +75,6 @@ const makeEntrypointRegistry = (seed) => {
|
|
|
75
75
|
* registry — they pick names from it via their Routable contributions. */
|
|
76
76
|
const layerEntrypointRegistry = (seed) => Layer.succeed(EntrypointRegistry)(makeEntrypointRegistry(seed));
|
|
77
77
|
//#endregion
|
|
78
|
-
export { EntrypointRegistry, layerEntrypointRegistry };
|
|
78
|
+
export { EntrypointRegistry, layerEntrypointRegistry, makeEntrypointRegistry };
|
|
79
79
|
|
|
80
80
|
//# sourceMappingURL=entrypoints.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entrypoints.mjs","names":[],"sources":["../../../src/orchestrators/router/entrypoints.ts"],"sourcesContent":["// Entrypoint registry — a process-global allowlist of named host\n// ports the Traefik container will listen on.\n//\n// Architecture invariants:\n// #6 — Routables select an entrypoint by *name* from a closed set\n// populated at module-load time. The set is read ONCE at\n// router launch.\n// #12 — Every entrypoint is plain HTTP / h2c / TCP; no TLS surface.\n// TCP entrypoints bind a host port and dispatch raw bytes to a\n// single backend (postgres, redis, …); they cannot share the\n// port across parallel stacks because TCP has no virtual-host\n// concept. HTTP/h2c entrypoints DO share ports via Host-header\n// dispatch.\n//\n// The registry is intentionally a typed Context service (Effect v4\n// idiom) rather than a module-mutable map: that lets tests build a\n// fresh registry per scenario, lets the orchestrator carry the\n// registration set forward immutably, and removes the\n// `ATTACHED_FOLLOWERS`-style hidden globals the architecture's\n// \"What's NOT in it\" section forbids.\n//\n// Construction surface:\n// `makeEntrypointRegistry(...)` — pure constructor; throws synchronously\n// on conflict registration (architecture invariant #6 is \"throw at\n// registration time\", not at acquire time).\n//\n// Lookup surface:\n// `EntrypointRegistry.byName(name)` — typed yieldable that fails\n// `UnknownEntrypoint` if the caller spliced an unregistered name.\n\nimport { Context, Effect, Layer } from 'effect';\n\nimport type { EntrypointDecl } from '../../contracts/routable.ts';\nimport { EntrypointConflict, UnknownEntrypoint } from './errors.ts';\n\n/** A named entrypoint — a host port the Traefik container binds and\n * listens on. `http`/`h2c` entrypoints support Host-header dispatch\n * (one port → many backends). `tcp` entrypoints are point-to-point\n * (one port → one backend) — siblings dial them directly. */\nexport type Entrypoint = EntrypointDecl;\n\n/** Registry shape. Closed map from declared name → canonical listener.\n * HTTP-family declarations may share a port; Traefik still needs one\n * listener name for that port, so aliases resolve to the first\n * registered listener. */\nexport interface EntrypointRegistryShape {\n\treadonly byName: (name: string) => Effect.Effect<Entrypoint, UnknownEntrypoint>;\n\treadonly all: () => ReadonlyArray<Entrypoint>;\n}\n\nexport class EntrypointRegistry extends Context.Service<\n\tEntrypointRegistry,\n\tEntrypointRegistryShape\n>()('@devstack
|
|
1
|
+
{"version":3,"file":"entrypoints.mjs","names":[],"sources":["../../../src/orchestrators/router/entrypoints.ts"],"sourcesContent":["// Entrypoint registry — a process-global allowlist of named host\n// ports the Traefik container will listen on.\n//\n// Architecture invariants:\n// #6 — Routables select an entrypoint by *name* from a closed set\n// populated at module-load time. The set is read ONCE at\n// router launch.\n// #12 — Every entrypoint is plain HTTP / h2c / TCP; no TLS surface.\n// TCP entrypoints bind a host port and dispatch raw bytes to a\n// single backend (postgres, redis, …); they cannot share the\n// port across parallel stacks because TCP has no virtual-host\n// concept. HTTP/h2c entrypoints DO share ports via Host-header\n// dispatch.\n//\n// The registry is intentionally a typed Context service (Effect v4\n// idiom) rather than a module-mutable map: that lets tests build a\n// fresh registry per scenario, lets the orchestrator carry the\n// registration set forward immutably, and removes the\n// `ATTACHED_FOLLOWERS`-style hidden globals the architecture's\n// \"What's NOT in it\" section forbids.\n//\n// Construction surface:\n// `makeEntrypointRegistry(...)` — pure constructor; throws synchronously\n// on conflict registration (architecture invariant #6 is \"throw at\n// registration time\", not at acquire time).\n//\n// Lookup surface:\n// `EntrypointRegistry.byName(name)` — typed yieldable that fails\n// `UnknownEntrypoint` if the caller spliced an unregistered name.\n\nimport { Context, Effect, Layer } from 'effect';\n\nimport type { EntrypointDecl } from '../../contracts/routable.ts';\nimport { EntrypointConflict, UnknownEntrypoint } from './errors.ts';\n\n/** A named entrypoint — a host port the Traefik container binds and\n * listens on. `http`/`h2c` entrypoints support Host-header dispatch\n * (one port → many backends). `tcp` entrypoints are point-to-point\n * (one port → one backend) — siblings dial them directly. */\nexport type Entrypoint = EntrypointDecl;\n\n/** Registry shape. Closed map from declared name → canonical listener.\n * HTTP-family declarations may share a port; Traefik still needs one\n * listener name for that port, so aliases resolve to the first\n * registered listener. */\nexport interface EntrypointRegistryShape {\n\treadonly byName: (name: string) => Effect.Effect<Entrypoint, UnknownEntrypoint>;\n\treadonly all: () => ReadonlyArray<Entrypoint>;\n}\n\nexport class EntrypointRegistry extends Context.Service<\n\tEntrypointRegistry,\n\tEntrypointRegistryShape\n>()('@devstack/orchestrators/router/EntrypointRegistry') {}\n\nconst entrypointFamily = (protocol: Entrypoint['protocol']): 'http' | 'tcp' =>\n\tprotocol === 'tcp' ? 'tcp' : 'http';\n\n/** Build a registry from a literal seed. Idempotent on identical\n * `(name, port, protocol)` triples; throws synchronously on conflict\n * per architecture invariant #6. The reason this is synchronous\n * rather than yielded: registration is module-load wiring, not a\n * runtime effect — the failure mode is \"two callers wired the same\n * name to different ports\", which is a build-time bug.\n *\n * Traefik cannot bind multiple entrypoints to the same container port.\n * For HTTP-family aliases on one port, keep every declared name\n * lookupable but resolve those aliases to the first listener registered\n * for the port. */\nexport const makeEntrypointRegistry = (\n\tseed: ReadonlyArray<Entrypoint>,\n): EntrypointRegistryShape => {\n\tconst declaredByName = new Map<string, Entrypoint>();\n\tconst lookupByName = new Map<string, Entrypoint>();\n\tconst listenerByPort = new Map<number, Entrypoint>();\n\tfor (const e of seed) {\n\t\tconst existing = declaredByName.get(e.name);\n\t\tif (existing) {\n\t\t\tif (existing.port === e.port && existing.protocol === e.protocol) continue;\n\t\t\tthrow new EntrypointConflict({\n\t\t\t\tname: e.name,\n\t\t\t\texisting: { port: existing.port, protocol: existing.protocol },\n\t\t\t\tattempted: { port: e.port, protocol: e.protocol },\n\t\t\t});\n\t\t}\n\t\tdeclaredByName.set(e.name, e);\n\n\t\tconst listener = listenerByPort.get(e.port);\n\t\tif (listener) {\n\t\t\tconst listenerFamily = entrypointFamily(listener.protocol);\n\t\t\tconst attemptedFamily = entrypointFamily(e.protocol);\n\t\t\tif (listenerFamily === 'tcp' || listenerFamily !== attemptedFamily) {\n\t\t\t\tthrow new EntrypointConflict({\n\t\t\t\t\tname: e.name,\n\t\t\t\t\texisting: { port: listener.port, protocol: listener.protocol },\n\t\t\t\t\tattempted: { port: e.port, protocol: e.protocol },\n\t\t\t\t});\n\t\t\t}\n\t\t\tlookupByName.set(e.name, listener);\n\t\t\tcontinue;\n\t\t}\n\n\t\tlistenerByPort.set(e.port, e);\n\t\tlookupByName.set(e.name, e);\n\t}\n\tconst frozenListeners: ReadonlyArray<Entrypoint> = Array.from(listenerByPort.values());\n\tconst knownNames = Array.from(declaredByName.keys());\n\treturn {\n\t\tbyName: (name) => {\n\t\t\tconst hit = lookupByName.get(name);\n\t\t\tif (hit) return Effect.succeed(hit);\n\t\t\treturn Effect.fail(\n\t\t\t\tnew UnknownEntrypoint({\n\t\t\t\t\tname,\n\t\t\t\t\tknown: knownNames,\n\t\t\t\t}),\n\t\t\t);\n\t\t},\n\t\tall: () => frozenListeners,\n\t};\n};\n\n/** Layer that pins the registry from a literal seed. The orchestrator\n * takes this as a substrate dependency; plugins do NOT mutate the\n * registry — they pick names from it via their Routable contributions. */\nexport const layerEntrypointRegistry = (\n\tseed: ReadonlyArray<Entrypoint>,\n): Layer.Layer<EntrypointRegistry> =>\n\tLayer.succeed(EntrypointRegistry)(makeEntrypointRegistry(seed));\n"],"mappings":";;;AAkDA,IAAa,qBAAb,cAAwC,QAAQ,SAG7C,CAAC,oDAAoD,CAAC;AAEzD,MAAM,oBAAoB,aACzB,aAAa,QAAQ,QAAQ;;;;;;;;;;;;AAa9B,MAAa,0BACZ,SAC6B;CAC7B,MAAM,iCAAiB,IAAI,KAAyB;CACpD,MAAM,+BAAe,IAAI,KAAyB;CAClD,MAAM,iCAAiB,IAAI,KAAyB;AACpD,MAAK,MAAM,KAAK,MAAM;EACrB,MAAM,WAAW,eAAe,IAAI,EAAE,KAAK;AAC3C,MAAI,UAAU;AACb,OAAI,SAAS,SAAS,EAAE,QAAQ,SAAS,aAAa,EAAE,SAAU;AAClE,SAAM,IAAI,mBAAmB;IAC5B,MAAM,EAAE;IACR,UAAU;KAAE,MAAM,SAAS;KAAM,UAAU,SAAS;KAAU;IAC9D,WAAW;KAAE,MAAM,EAAE;KAAM,UAAU,EAAE;KAAU;IACjD,CAAC;;AAEH,iBAAe,IAAI,EAAE,MAAM,EAAE;EAE7B,MAAM,WAAW,eAAe,IAAI,EAAE,KAAK;AAC3C,MAAI,UAAU;GACb,MAAM,iBAAiB,iBAAiB,SAAS,SAAS;GAC1D,MAAM,kBAAkB,iBAAiB,EAAE,SAAS;AACpD,OAAI,mBAAmB,SAAS,mBAAmB,gBAClD,OAAM,IAAI,mBAAmB;IAC5B,MAAM,EAAE;IACR,UAAU;KAAE,MAAM,SAAS;KAAM,UAAU,SAAS;KAAU;IAC9D,WAAW;KAAE,MAAM,EAAE;KAAM,UAAU,EAAE;KAAU;IACjD,CAAC;AAEH,gBAAa,IAAI,EAAE,MAAM,SAAS;AAClC;;AAGD,iBAAe,IAAI,EAAE,MAAM,EAAE;AAC7B,eAAa,IAAI,EAAE,MAAM,EAAE;;CAE5B,MAAM,kBAA6C,MAAM,KAAK,eAAe,QAAQ,CAAC;CACtF,MAAM,aAAa,MAAM,KAAK,eAAe,MAAM,CAAC;AACpD,QAAO;EACN,SAAS,SAAS;GACjB,MAAM,MAAM,aAAa,IAAI,KAAK;AAClC,OAAI,IAAK,QAAO,OAAO,QAAQ,IAAI;AACnC,UAAO,OAAO,KACb,IAAI,kBAAkB;IACrB;IACA,OAAO;IACP,CAAC,CACF;;EAEF,WAAW;EACX;;;;;AAMF,MAAa,2BACZ,SAEA,MAAM,QAAQ,mBAAmB,CAAC,uBAAuB,KAAK,CAAC"}
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { RouteCollision, RouterValidationError } from "./errors.mjs";
|
|
2
|
-
import { dispatchFileId, renderUrl, routerHostname } from "./hostname.mjs";
|
|
3
2
|
import { CORS_MIDDLEWARE_NAME } from "./cors.mjs";
|
|
3
|
+
import { renderUrl } from "../../substrate/runtime/routed-url.mjs";
|
|
4
|
+
import { dispatchFileId, routerHostname } from "./hostname.mjs";
|
|
4
5
|
import { Effect } from "effect";
|
|
5
6
|
//#region src/orchestrators/router/file-provider.ts
|
|
6
7
|
const ROUTE_READINESS_HEADER = "X-Devstack-Route-Id";
|
|
8
|
+
/** Dispatch-file filename prefix. `10-` sorts behind the shared CORS
|
|
9
|
+
* middleware (`00-`) so Traefik's file-provider loads the middleware
|
|
10
|
+
* before any router that references it. Single source of truth for
|
|
11
|
+
* the file-write side (`dispatchFilename`) and the file-read side
|
|
12
|
+
* (`dispatchFileIdFromFilename`) — keep them in lockstep here. */
|
|
13
|
+
const DISPATCH_FILENAME_PREFIX = "10-";
|
|
7
14
|
/** Filename within the dispatch directory for a given file-id. The
|
|
8
|
-
* `
|
|
9
|
-
* Traefik picks up the middleware before any
|
|
10
|
-
|
|
15
|
+
* prefix (`DISPATCH_FILENAME_PREFIX`) sorts behind the shared CORS
|
|
16
|
+
* middleware (`00-`) so Traefik picks up the middleware before any
|
|
17
|
+
* router referencing it. */
|
|
18
|
+
const dispatchFilename = (fileId) => `${DISPATCH_FILENAME_PREFIX}${fileId}.yml`;
|
|
11
19
|
/** Resolve a single Routable into a ResolvedRoute. Pure-ish: the only
|
|
12
20
|
* effectful bit is the upstream resolver (which talks to docker /
|
|
13
21
|
* the port broker) and the entrypoint lookup.
|
|
@@ -143,7 +151,8 @@ const renderTcpRouteYaml = (route, lease) => {
|
|
|
143
151
|
].join("\n");
|
|
144
152
|
};
|
|
145
153
|
const dispatchFileIdFromFilename = (filename) => {
|
|
146
|
-
|
|
154
|
+
const escapedPrefix = DISPATCH_FILENAME_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
155
|
+
return new RegExp(`^${escapedPrefix}(.+)\\.yml$`).exec(filename)?.[1] ?? null;
|
|
147
156
|
};
|
|
148
157
|
const commentValue = (body, key) => {
|
|
149
158
|
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|