@lostgradient/weft 0.23.1 → 0.24.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 +72 -4
- package/dist/cli/codegen-emit-dedup.d.ts +81 -0
- package/dist/cli/codegen-emit-dedup.js +45 -0
- package/dist/cli/codegen-emit-registry.d.ts +90 -0
- package/dist/cli/codegen-emit-registry.js +50 -0
- package/dist/cli/codegen-emit.d.ts +26 -39
- package/dist/cli/codegen-emit.js +4 -27
- package/dist/cli/codegen-validate.d.ts +49 -0
- package/dist/cli/codegen-validate.js +186 -0
- package/dist/cli/codegen.js +6 -87
- package/dist/cli/conformance.js +5 -2
- package/dist/cli/generated/operation-catalog.snapshot.json +381 -3
- package/dist/cli/generated/operation-client.generated.d.ts +350 -10
- package/dist/cli/generated/operation-client.generated.js +14 -0
- package/dist/cli/output.js +1 -1
- package/dist/cli/parse-schedule-arguments.js +15 -3
- package/dist/cli/schedule.js +2 -1
- package/dist/cli/storage-factory.js +2 -0
- package/dist/cli/types.d.ts +2 -1
- package/dist/cli/workflow-commands.js +2 -0
- package/dist/cli-main.js +1 -1
- package/dist/client/http-client-requests.js +6 -9
- package/dist/client/start-body.js +1 -0
- package/dist/core/application-payload-digest.d.ts +42 -0
- package/dist/core/application-payload-digest.js +76 -0
- package/dist/core/application-primitive-abort.d.ts +50 -0
- package/dist/core/application-primitive-abort.js +40 -0
- package/dist/core/application-primitive-attempt-registry.d.ts +74 -0
- package/dist/core/application-primitive-attempt-registry.js +73 -0
- package/dist/core/application-primitive-codec.d.ts +36 -0
- package/dist/core/application-primitive-codec.js +56 -0
- package/dist/core/application-primitive-commit.d.ts +67 -0
- package/dist/core/application-primitive-commit.js +66 -0
- package/dist/core/application-primitive-guards.d.ts +62 -0
- package/dist/core/application-primitive-guards.js +89 -0
- package/dist/core/application-primitive-payload.d.ts +29 -0
- package/dist/core/application-primitive-payload.js +65 -0
- package/dist/core/application-primitive-timing.d.ts +18 -0
- package/dist/core/application-primitive-timing.js +22 -0
- package/dist/core/atomic-state.js +2 -0
- package/dist/core/catalog/codec.d.ts +40 -0
- package/dist/core/catalog/codec.js +45 -0
- package/dist/core/catalog/errors.d.ts +104 -0
- package/dist/core/catalog/errors.js +41 -0
- package/dist/core/catalog/index.d.ts +25 -0
- package/dist/core/catalog/index.js +22 -0
- package/dist/core/catalog/reference-counts.d.ts +120 -0
- package/dist/core/catalog/reference-counts.js +28 -0
- package/dist/core/catalog/removal.d.ts +105 -0
- package/dist/core/catalog/removal.js +30 -0
- package/dist/core/catalog/storage-io.d.ts +101 -0
- package/dist/core/catalog/storage-io.js +106 -0
- package/dist/core/catalog/types.d.ts +108 -0
- package/dist/core/catalog/types.js +0 -0
- package/dist/core/catalog/workflow-catalog.d.ts +165 -0
- package/dist/core/catalog/workflow-catalog.js +198 -0
- package/dist/core/compare-codepoint.d.ts +13 -0
- package/dist/core/compare-codepoint.js +7 -0
- package/dist/core/compression.js +2 -0
- package/dist/core/context/attributes.d.ts +1 -1
- package/dist/core/context/durable-operations.d.ts +2 -2
- package/dist/core/context/index.js +5 -3
- package/dist/core/context/state-namespace.d.ts +1 -1
- package/dist/core/contract/build.d.ts +64 -0
- package/dist/core/contract/build.js +99 -0
- package/dist/core/contract/compatibility.d.ts +187 -0
- package/dist/core/contract/compatibility.js +37 -0
- package/dist/core/contract/failure.d.ts +64 -0
- package/dist/core/contract/failure.js +4 -0
- package/dist/core/contract/hash.d.ts +92 -0
- package/dist/core/contract/hash.js +34 -0
- package/dist/core/contract/index.d.ts +21 -0
- package/dist/core/contract/index.js +22 -0
- package/dist/core/contract/limits.d.ts +74 -0
- package/dist/core/contract/limits.js +1 -0
- package/dist/core/contract/manifest-parse-schema.d.ts +37 -0
- package/dist/core/contract/manifest-parse-schema.js +106 -0
- package/dist/core/contract/manifest-parse.d.ts +76 -0
- package/dist/core/contract/manifest-parse.js +189 -0
- package/dist/core/contract/manifest.d.ts +56 -0
- package/dist/core/contract/manifest.js +31 -0
- package/dist/core/contract/normalize.d.ts +97 -0
- package/dist/core/contract/normalize.js +127 -0
- package/dist/core/contract/revision.d.ts +29 -0
- package/dist/core/contract/revision.js +5 -0
- package/dist/core/contract/types.d.ts +274 -0
- package/dist/core/contract/types.js +1 -0
- package/dist/core/engine/activity-resolution.js +17 -17
- package/dist/core/engine/bulk-operations-purge.js +14 -20
- package/dist/core/engine/bulk-operations-retry.d.ts +9 -0
- package/dist/core/engine/bulk-operations-retry.js +177 -0
- package/dist/core/engine/bulk-operations-shared.d.ts +18 -0
- package/dist/core/engine/bulk-operations-shared.js +14 -0
- package/dist/core/engine/bulk-operations.js +8 -167
- package/dist/core/engine/callback-creators-bundles.js +4 -2
- package/dist/core/engine/callback-creators-core.js +9 -1
- package/dist/core/engine/callback-creators-schedule.js +2 -2
- package/dist/core/engine/catalog-activation.d.ts +40 -0
- package/dist/core/engine/catalog-activation.js +17 -0
- package/dist/core/engine/catalog-events.d.ts +36 -0
- package/dist/core/engine/catalog-events.js +15 -0
- package/dist/core/engine/catalog-readiness.d.ts +49 -0
- package/dist/core/engine/catalog-readiness.js +61 -0
- package/dist/core/engine/catalog-removal.d.ts +236 -0
- package/dist/core/engine/catalog-removal.js +123 -0
- package/dist/core/engine/catalog-tombstone-recovery.d.ts +56 -0
- package/dist/core/engine/catalog-tombstone-recovery.js +39 -0
- package/dist/core/engine/constraints.js +5 -1
- package/dist/core/engine/construction.d.ts +2 -2
- package/dist/core/engine/construction.js +4 -2
- package/dist/core/engine/decode-revision.d.ts +32 -0
- package/dist/core/engine/decode-revision.js +9 -0
- package/dist/core/engine/disposal.d.ts +6 -2
- package/dist/core/engine/disposal.js +26 -3
- package/dist/core/engine/dynamic-source-errors.d.ts +81 -0
- package/dist/core/engine/dynamic-source-errors.js +25 -0
- package/dist/core/engine/dynamic-source-execution.d.ts +173 -0
- package/dist/core/engine/dynamic-source-execution.js +108 -0
- package/dist/core/engine/engine-internal-types.d.ts +16 -0
- package/dist/core/engine/engine-workflows-namespace.d.ts +140 -0
- package/dist/core/engine/engine-workflows-namespace.js +51 -0
- package/dist/core/engine/finalizer-metadata.d.ts +22 -0
- package/dist/core/engine/finalizer-metadata.js +12 -0
- package/dist/core/engine/handle-result.js +2 -0
- package/dist/core/engine/index.d.ts +91 -0
- package/dist/core/engine/index.js +78 -6
- package/dist/core/engine/inline-launch-queue.js +1 -1
- package/dist/core/engine/internals.d.ts +54 -15
- package/dist/core/engine/internals.js +6 -0
- package/dist/core/engine/lifecycle/checkpoint-launch.d.ts +29 -0
- package/dist/core/engine/lifecycle/checkpoint-launch.js +75 -0
- package/dist/core/engine/lifecycle/delayed-start-registration.d.ts +30 -0
- package/dist/core/engine/lifecycle/delayed-start-registration.js +11 -0
- package/dist/core/engine/lifecycle/fork-helpers.d.ts +33 -1
- package/dist/core/engine/lifecycle/fork-helpers.js +3 -1
- package/dist/core/engine/lifecycle/recovery-revision-groups.d.ts +75 -0
- package/dist/core/engine/lifecycle/recovery-revision-groups.js +64 -0
- package/dist/core/engine/lifecycle/resume-generation-guard.d.ts +58 -0
- package/dist/core/engine/lifecycle/resume-generation-guard.js +11 -0
- package/dist/core/engine/lifecycle/resume.js +18 -9
- package/dist/core/engine/lifecycle/shared.d.ts +43 -0
- package/dist/core/engine/lifecycle/standalone-claim-acquire.d.ts +12 -0
- package/dist/core/engine/lifecycle/standalone-claim-acquire.js +6 -0
- package/dist/core/engine/lifecycle/start-commit-errors.d.ts +12 -0
- package/dist/core/engine/lifecycle/start-commit-errors.js +6 -0
- package/dist/core/engine/lifecycle/start-commit.d.ts +22 -7
- package/dist/core/engine/lifecycle/start-commit.js +49 -15
- package/dist/core/engine/lifecycle/start-exec.d.ts +18 -2
- package/dist/core/engine/lifecycle/start-exec.js +7 -5
- package/dist/core/engine/lifecycle/start-or-signal-create.js +1 -3
- package/dist/core/engine/lifecycle/start-or-signal.js +1 -1
- package/dist/core/engine/lifecycle/start-precondition-attribution.d.ts +49 -0
- package/dist/core/engine/lifecycle/start-precondition-attribution.js +13 -0
- package/dist/core/engine/lifecycle/start-revision-resolution.d.ts +34 -0
- package/dist/core/engine/lifecycle/start-revision-resolution.js +11 -0
- package/dist/core/engine/lifecycle/start-state.d.ts +16 -0
- package/dist/core/engine/lifecycle/start-state.js +60 -0
- package/dist/core/engine/lifecycle/start-terminal-conflict-purge.d.ts +66 -3
- package/dist/core/engine/lifecycle/start-terminal-conflict-purge.js +13 -3
- package/dist/core/engine/lifecycle/start.d.ts +16 -6
- package/dist/core/engine/lifecycle/start.js +39 -66
- package/dist/core/engine/lifecycle/transition.d.ts +3 -4
- package/dist/core/engine/lifecycle/transition.js +30 -80
- package/dist/core/engine/lifecycle.d.ts +4 -2
- package/dist/core/engine/lifecycle.js +6 -6
- package/dist/core/engine/listing.js +15 -1
- package/dist/core/engine/memo-durable-activity.js +4 -1
- package/dist/core/engine/nonterminal-revision-count.d.ts +25 -0
- package/dist/core/engine/nonterminal-revision-count.js +16 -0
- package/dist/core/engine/operations-time.d.ts +5 -23
- package/dist/core/engine/operations-time.js +13 -11
- package/dist/core/engine/ownership-bootstrap.d.ts +1 -1
- package/dist/core/engine/pinned-schedule-revision-count.d.ts +26 -0
- package/dist/core/engine/pinned-schedule-revision-count.js +19 -0
- package/dist/core/engine/pinned-schedule-revision.d.ts +76 -0
- package/dist/core/engine/pinned-schedule-revision.js +53 -0
- package/dist/core/engine/registration.d.ts +106 -0
- package/dist/core/engine/registration.js +28 -7
- package/dist/core/engine/retention.js +9 -2
- package/dist/core/engine/revision-errors.d.ts +71 -0
- package/dist/core/engine/revision-errors.js +13 -0
- package/dist/core/engine/schedule-revision-fire.d.ts +17 -0
- package/dist/core/engine/schedule-revision-fire.js +3 -0
- package/dist/core/engine/schedule-run.js +2 -1
- package/dist/core/engine/schedules.d.ts +2 -1
- package/dist/core/engine/schedules.js +28 -9
- package/dist/core/engine/source-diagnostics.d.ts +76 -0
- package/dist/core/engine/source-diagnostics.js +105 -0
- package/dist/core/engine/source-registration.d.ts +42 -0
- package/dist/core/engine/source-registration.js +38 -0
- package/dist/core/engine/source-resolution.d.ts +101 -0
- package/dist/core/engine/source-resolution.js +154 -0
- package/dist/core/engine/source-runtime-state.d.ts +92 -0
- package/dist/core/engine/source-runtime-state.js +36 -0
- package/dist/core/engine/storage-io.d.ts +19 -2
- package/dist/core/engine/storage-io.js +21 -2
- package/dist/core/engine/termination/finalizer-registration.d.ts +18 -0
- package/dist/core/engine/termination/finalizer-registration.js +21 -0
- package/dist/core/engine/termination/finalizer.d.ts +16 -19
- package/dist/core/engine/termination/finalizer.js +3 -3
- package/dist/core/engine/time-operation-callbacks.d.ts +32 -0
- package/dist/core/engine/time-operation-callbacks.js +0 -0
- package/dist/core/engine/validation/schedule-cadence.d.ts +16 -0
- package/dist/core/engine/validation/schedule-cadence.js +20 -0
- package/dist/core/engine/validation/schedule-options.d.ts +4 -1
- package/dist/core/engine/validation/schedule-options.js +14 -0
- package/dist/core/engine/validation/schedule-revision.d.ts +24 -0
- package/dist/core/engine/validation/schedule-revision.js +21 -0
- package/dist/core/engine/validation/schedule-warnings.d.ts +11 -0
- package/dist/core/engine/validation/schedule-warnings.js +5 -0
- package/dist/core/engine/validation/schedule.d.ts +4 -3
- package/dist/core/engine/validation/schedule.js +15 -26
- package/dist/core/engine/validation.js +5 -2
- package/dist/core/engine/workflow-claim-reclaim-target.d.ts +1 -1
- package/dist/core/engine/workflow-claim-reclaim-target.js +16 -2
- package/dist/core/engine/workflow-retention-deadline.d.ts +16 -0
- package/dist/core/engine/workflow-retention-deadline.js +33 -0
- package/dist/core/events/catalog-events.d.ts +117 -0
- package/dist/core/events/catalog-events.js +62 -0
- package/dist/core/events/event-map.d.ts +11 -0
- package/dist/core/events/index.d.ts +2 -0
- package/dist/core/events/index.js +2 -0
- package/dist/core/events/workflow-source-events.d.ts +102 -0
- package/dist/core/events/workflow-source-events.js +57 -0
- package/dist/core/execution-strategy.d.ts +7 -0
- package/dist/core/inline-execution-strategy.context-options.d.ts +1 -1
- package/dist/core/inline-execution-strategy.js +1 -1
- package/dist/core/mailbox-admission.d.ts +26 -0
- package/dist/core/mailbox-admission.js +130 -0
- package/dist/core/mailbox-claims.d.ts +251 -0
- package/dist/core/mailbox-claims.js +0 -0
- package/dist/core/mailbox-codec.d.ts +32 -0
- package/dist/core/mailbox-codec.js +221 -0
- package/dist/core/mailbox-contract.d.ts +290 -0
- package/dist/core/mailbox-contract.js +0 -0
- package/dist/core/mailbox-delivery.d.ts +37 -0
- package/dist/core/mailbox-delivery.js +193 -0
- package/dist/core/mailbox-guards.d.ts +78 -0
- package/dist/core/mailbox-guards.js +58 -0
- package/dist/core/mailbox-index-codec.d.ts +41 -0
- package/dist/core/mailbox-index-codec.js +71 -0
- package/dist/core/mailbox-internals.d.ts +156 -0
- package/dist/core/mailbox-internals.js +154 -0
- package/dist/core/mailbox-maintenance.d.ts +29 -0
- package/dist/core/mailbox-maintenance.js +196 -0
- package/dist/core/mailbox-settlement.d.ts +69 -0
- package/dist/core/mailbox-settlement.js +206 -0
- package/dist/core/mailbox-storage.d.ts +117 -0
- package/dist/core/mailbox-storage.js +115 -0
- package/dist/core/mailbox-transition-helpers.d.ts +70 -0
- package/dist/core/mailbox-transition-helpers.js +43 -0
- package/dist/core/mailbox-transitions-recovery.d.ts +28 -0
- package/dist/core/mailbox-transitions-recovery.js +64 -0
- package/dist/core/mailbox-transitions.d.ts +138 -0
- package/dist/core/mailbox-transitions.js +180 -0
- package/dist/core/mailbox-types.d.ts +406 -0
- package/dist/core/mailbox-types.js +16 -0
- package/dist/core/mailbox-validation.d.ts +65 -0
- package/dist/core/mailbox-validation.js +73 -0
- package/dist/core/mailbox-waits.d.ts +50 -0
- package/dist/core/mailbox-waits.js +97 -0
- package/dist/core/mailbox.d.ts +163 -0
- package/dist/core/mailbox.js +211 -0
- package/dist/core/outbox-claims.d.ts +307 -0
- package/dist/core/outbox-claims.js +0 -0
- package/dist/core/outbox-codec.d.ts +24 -0
- package/dist/core/outbox-codec.js +257 -0
- package/dist/core/outbox-contract.d.ts +346 -0
- package/dist/core/outbox-contract.js +0 -0
- package/dist/core/outbox-delivery.d.ts +34 -0
- package/dist/core/outbox-delivery.js +149 -0
- package/dist/core/outbox-drain.d.ts +29 -0
- package/dist/core/outbox-drain.js +158 -0
- package/dist/core/outbox-enqueue.d.ts +24 -0
- package/dist/core/outbox-enqueue.js +115 -0
- package/dist/core/outbox-guards.d.ts +76 -0
- package/dist/core/outbox-guards.js +60 -0
- package/dist/core/outbox-index-codec.d.ts +33 -0
- package/dist/core/outbox-index-codec.js +71 -0
- package/dist/core/outbox-internals.d.ts +99 -0
- package/dist/core/outbox-internals.js +159 -0
- package/dist/core/outbox-listing.d.ts +12 -0
- package/dist/core/outbox-listing.js +38 -0
- package/dist/core/outbox-maintenance.d.ts +28 -0
- package/dist/core/outbox-maintenance.js +191 -0
- package/dist/core/outbox-operations.d.ts +18 -0
- package/dist/core/outbox-operations.js +46 -0
- package/dist/core/outbox-runner.d.ts +30 -0
- package/dist/core/outbox-runner.js +217 -0
- package/dist/core/outbox-settlement.d.ts +57 -0
- package/dist/core/outbox-settlement.js +183 -0
- package/dist/core/outbox-storage.d.ts +107 -0
- package/dist/core/outbox-storage.js +127 -0
- package/dist/core/outbox-transition-helpers.d.ts +83 -0
- package/dist/core/outbox-transition-helpers.js +63 -0
- package/dist/core/outbox-transitions-recovery.d.ts +30 -0
- package/dist/core/outbox-transitions-recovery.js +42 -0
- package/dist/core/outbox-transitions.d.ts +145 -0
- package/dist/core/outbox-transitions.js +236 -0
- package/dist/core/outbox-types.d.ts +400 -0
- package/dist/core/outbox-types.js +20 -0
- package/dist/core/outbox-validation.d.ts +101 -0
- package/dist/core/outbox-validation.js +127 -0
- package/dist/core/outbox-waits.d.ts +39 -0
- package/dist/core/outbox-waits.js +88 -0
- package/dist/core/outbox.d.ts +156 -0
- package/dist/core/outbox.js +234 -0
- package/dist/core/registry-limits.d.ts +49 -0
- package/dist/core/registry-limits.js +10 -0
- package/dist/core/registry-schema-conversion.d.ts +20 -0
- package/dist/core/registry-schema-conversion.js +22 -0
- package/dist/core/registry-snapshot.d.ts +91 -42
- package/dist/core/registry-snapshot.js +39 -77
- package/dist/core/registry-workflow-contract-draft.d.ts +32 -0
- package/dist/core/registry-workflow-contract-draft.js +28 -0
- package/dist/core/registry-workflow-manifest.d.ts +122 -0
- package/dist/core/registry-workflow-manifest.js +92 -0
- package/dist/core/source/errors.d.ts +55 -0
- package/dist/core/source/errors.js +13 -0
- package/dist/core/source/index.d.ts +19 -0
- package/dist/core/source/index.js +4 -0
- package/dist/core/source/resolvers.d.ts +45 -0
- package/dist/core/source/resolvers.js +9 -0
- package/dist/core/source/types.d.ts +211 -0
- package/dist/core/source/types.js +0 -0
- package/dist/core/source/validate.d.ts +91 -0
- package/dist/core/source/validate.js +62 -0
- package/dist/core/source/workflow-source.d.ts +43 -0
- package/dist/core/source/workflow-source.js +16 -0
- package/dist/core/types/checkpoint.d.ts +14 -0
- package/dist/core/types/schedules.d.ts +52 -1
- package/dist/core/types/state.d.ts +16 -0
- package/dist/core/types/workflow-registries.d.ts +7 -0
- package/dist/core/types/workflow-registry.d.ts +12 -0
- package/dist/core/versioning.js +2 -0
- package/dist/core/weft-error.d.ts +1 -1
- package/dist/core/weft-error.js +12 -1
- package/dist/core/worker-execution-strategy.d.ts +1 -0
- package/dist/core/worker-execution-strategy.js +13 -1
- package/dist/core/worker-fault-handling.d.ts +10 -0
- package/dist/core/worker-fault-handling.js +1 -0
- package/dist/core/worker-inbound-message.d.ts +1 -0
- package/dist/core/worker-inbound-message.js +1 -0
- package/dist/core/worker-protocol-guard.js +4 -0
- package/dist/core/worker-protocol.d.ts +7 -0
- package/dist/core/worker-protocol.js +2 -1
- package/dist/core/worker-turn-watchdog.d.ts +8 -1
- package/dist/core/worker-turn-watchdog.js +3 -2
- package/dist/diagnostics/format.js +5 -0
- package/dist/diagnostics/types.d.ts +24 -0
- package/dist/diagnostics/version-check.js +8 -2
- package/dist/http.js +2 -2
- package/dist/index.d.ts +25 -4
- package/dist/index.js +57 -0
- package/dist/indexeddb.js +1 -1
- package/dist/json-schema.js +3 -3
- package/dist/mcp/resources.js +2 -0
- package/dist/runtime/portable.d.ts +9 -1
- package/dist/server/fault-to-json-rpc.js +4 -1
- package/dist/server/handler/auth-context-principal.js +4 -0
- package/dist/server/index.d.ts +11 -0
- package/dist/server/json-rpc-http.js +2 -0
- package/dist/server/json-rpc-websocket.js +6 -4
- package/dist/server/operation-catalog/index.d.ts +1 -1
- package/dist/server/operation-catalog/pipeline-helpers.js +2 -0
- package/dist/server/operation-catalog/types.d.ts +17 -0
- package/dist/server/operation-fault.d.ts +5 -0
- package/dist/server/operation-fault.js +4 -1
- package/dist/server/operations/activate-workflow-revision.d.ts +30 -0
- package/dist/server/operations/activate-workflow-revision.js +69 -0
- package/dist/server/operations/create-schedule.d.ts +2 -0
- package/dist/server/operations/create-schedule.js +10 -6
- package/dist/server/operations/fleet-events-subscription.js +1 -1
- package/dist/server/operations/fork-workflow.js +1 -1
- package/dist/server/operations/get-active-workflow-revision.d.ts +32 -0
- package/dist/server/operations/get-active-workflow-revision.js +46 -0
- package/dist/server/operations/get-catalog-diagnostics.d.ts +113 -0
- package/dist/server/operations/get-catalog-diagnostics.js +65 -0
- package/dist/server/operations/get-registry.js +17 -4
- package/dist/server/operations/get-task-detail-schema.d.ts +8 -0
- package/dist/server/operations/get-task-detail-schema.js +1 -0
- package/dist/server/operations/get-task-detail.d.ts +8 -0
- package/dist/server/operations/get-task-detail.js +1 -0
- package/dist/server/operations/get-task-diagnostics.d.ts +4 -4
- package/dist/server/operations/get-workflow-revision.d.ts +25 -0
- package/dist/server/operations/get-workflow-revision.js +50 -0
- package/dist/server/operations/install-workflow-revision.d.ts +27 -0
- package/dist/server/operations/install-workflow-revision.js +46 -0
- package/dist/server/operations/list-workflow-revisions.d.ts +20 -0
- package/dist/server/operations/list-workflow-revisions.js +40 -0
- package/dist/server/operations/preload-workflow-revision.d.ts +28 -0
- package/dist/server/operations/preload-workflow-revision.js +60 -0
- package/dist/server/operations/schedule-faults.d.ts +1 -0
- package/dist/server/operations/schedule-faults.js +32 -4
- package/dist/server/operations/schedule-rest-body.d.ts +1 -0
- package/dist/server/operations/schedule-rest-body.js +2 -1
- package/dist/server/operations/start-or-signal-workflow.js +1 -1
- package/dist/server/operations/start-workflow.js +1 -1
- package/dist/server/operations/static-registrations.js +42 -0
- package/dist/server/operations/storage.js +2 -2
- package/dist/server/operations/submit-review-decision.js +1 -1
- package/dist/server/operations/update-schedule.d.ts +2 -0
- package/dist/server/operations/update-schedule.js +4 -2
- package/dist/server/operations/workflow-catalog-operation-helpers.d.ts +59 -0
- package/dist/server/operations/workflow-catalog-operation-helpers.js +162 -0
- package/dist/server/runtime/task-dispatch-envelope.d.ts +14 -0
- package/dist/server/runtime/task-dispatch-envelope.js +32 -0
- package/dist/server/runtime/task-dispatch-revision.d.ts +29 -0
- package/dist/server/runtime/task-dispatch-revision.js +13 -0
- package/dist/server/runtime/task-dispatch.js +12 -37
- package/dist/server/runtime/task-ledger-recovery.js +1 -1
- package/dist/server/runtime/task-ledger-runtime.d.ts +14 -2
- package/dist/server/runtime/task-ledger-runtime.js +5 -3
- package/dist/server/runtime/task-polling.js +9 -3
- package/dist/server/runtime/task-reconciliation.js +2 -1
- package/dist/server/runtime/websocket-upgrade.js +2 -0
- package/dist/server/runtime/websocket-worker.js +9 -0
- package/dist/server/task-ledger-codec.d.ts +9 -0
- package/dist/server/task-ledger-codec.js +4 -1
- package/dist/server/task-ledger-transition-helpers.js +1 -0
- package/dist/server/task-ledger-types.d.ts +9 -0
- package/dist/server/task-ledger.d.ts +1 -1
- package/dist/server/task-ledger.js +2 -1
- package/dist/server/task-queue-types.d.ts +2 -0
- package/dist/storage/bun-sql.js +51 -6
- package/dist/storage/catalog-keys.d.ts +64 -0
- package/dist/storage/catalog-keys.js +8 -0
- package/dist/storage/compressed-storage.js +1 -1
- package/dist/storage/derived-operations.js +1 -1
- package/dist/storage/index.d.ts +28 -1
- package/dist/storage/interface.d.ts +28 -1
- package/dist/storage/interface.js +1 -1
- package/dist/storage/key-encoding.d.ts +15 -0
- package/dist/storage/key-encoding.js +3 -0
- package/dist/storage/key-prefixes.d.ts +1 -1
- package/dist/storage/key-prefixes.js +16 -0
- package/dist/storage/lmdb.d.ts +54 -1
- package/dist/storage/lmdb.js +1 -1
- package/dist/storage/mailbox-keys.d.ts +80 -0
- package/dist/storage/mailbox-keys.js +14 -0
- package/dist/storage/memory.js +1 -1
- package/dist/storage/neon.js +2 -2
- package/dist/storage/node-sqlite-loader.d.ts +1 -1
- package/dist/storage/node-sqlite.js +51 -6
- package/dist/storage/outbox-keys.d.ts +77 -0
- package/dist/storage/outbox-keys.js +14 -0
- package/dist/storage/postgres.js +2 -2
- package/dist/storage/resolve.js +1 -1
- package/dist/storage/scoped-storage.js +1 -1
- package/dist/storage/storage-configuration.d.ts +6 -0
- package/dist/storage/testing.js +1 -1
- package/dist/storage/turso.js +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/web-extension.js +1 -1
- package/dist/worker/manifest/registry-contract-builder.d.ts +21 -3
- package/dist/worker/manifest/registry-contract-builder.js +40 -31
- package/dist/worker/protocol-messages.d.ts +17 -0
- package/dist/worker/protocol-schemas.d.ts +32 -0
- package/dist/worker/protocol-schemas.js +7 -3
- package/dist/worker/protocol-task-result.js +35 -3
- package/dist/worker/protocol.js +1 -1
- package/dist/worker/registry/types.d.ts +2 -0
- package/dist/worker/registry.d.ts +1 -1
- package/dist/worker/registry.js +3 -1
- package/dist/workers/workflow-runner.d.ts +9 -0
- package/dist/workers/workflow-runner.js +9 -2
- package/dist/workers/workflow-worker-entry.js +9 -4
- package/package.json +2 -2
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encoding and fail-closed decoding for the application mailbox's index
|
|
3
|
+
* records (WFT-84): the per-mailbox header, idempotency bindings, and the
|
|
4
|
+
* entries of the delivery, listing, and terminal-retention indexes.
|
|
5
|
+
*
|
|
6
|
+
* Kept apart from the command-record codec so each stays under the repository's
|
|
7
|
+
* file ceiling while carrying its full rationale.
|
|
8
|
+
*
|
|
9
|
+
* @module core/mailbox-index-codec
|
|
10
|
+
*/
|
|
11
|
+
import type { ApplicationCommandIdempotencyRecord, MailboxRecord } from './mailbox-types.ts';
|
|
12
|
+
/**
|
|
13
|
+
* Decode the per-mailbox header holding the FIFO allocator and backlog counter.
|
|
14
|
+
*
|
|
15
|
+
* @throws {PersistedDataCorruptError} When the stored bytes are malformed.
|
|
16
|
+
*/
|
|
17
|
+
export declare function decodeMailboxRecord(bytes: Uint8Array, key: string): MailboxRecord;
|
|
18
|
+
/**
|
|
19
|
+
* Encode the per-mailbox header.
|
|
20
|
+
*/
|
|
21
|
+
export declare function encodeMailboxRecord(record: MailboxRecord): Uint8Array;
|
|
22
|
+
/**
|
|
23
|
+
* Decode an idempotency index record.
|
|
24
|
+
*
|
|
25
|
+
* @throws {PersistedDataCorruptError} When the stored bytes are malformed.
|
|
26
|
+
*/
|
|
27
|
+
export declare function decodeApplicationCommandIdempotencyRecord(bytes: Uint8Array, key: string): ApplicationCommandIdempotencyRecord;
|
|
28
|
+
/**
|
|
29
|
+
* Encode an idempotency index record.
|
|
30
|
+
*/
|
|
31
|
+
export declare function encodeApplicationCommandIdempotencyRecord(record: ApplicationCommandIdempotencyRecord): Uint8Array;
|
|
32
|
+
/**
|
|
33
|
+
* Decode the command id stored in a FIFO delivery-index entry.
|
|
34
|
+
*
|
|
35
|
+
* @throws {PersistedDataCorruptError} When the entry is not a non-empty string.
|
|
36
|
+
*/
|
|
37
|
+
export declare function decodeApplicationReadyEntry(bytes: Uint8Array, key: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Encode a FIFO delivery-index entry.
|
|
40
|
+
*/
|
|
41
|
+
export declare function encodeApplicationReadyEntry(commandId: string): Uint8Array;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { KEYS } from "../storage/interface.js";
|
|
2
|
+
import {
|
|
3
|
+
fail,
|
|
4
|
+
isRecordObject,
|
|
5
|
+
ownKey,
|
|
6
|
+
readIdentifier,
|
|
7
|
+
readInteger,
|
|
8
|
+
readString,
|
|
9
|
+
readVersion
|
|
10
|
+
} from "./application-primitive-codec.js";
|
|
11
|
+
import { decode, encode } from "./codec.js";
|
|
12
|
+
import { MAILBOX_RECORD_VERSION } from "./mailbox-types.js";
|
|
13
|
+
export function decodeMailboxRecord(bytes, key) {
|
|
14
|
+
let decoded;
|
|
15
|
+
try {
|
|
16
|
+
decoded = decode(bytes);
|
|
17
|
+
} catch {
|
|
18
|
+
fail(key);
|
|
19
|
+
}
|
|
20
|
+
if (!isRecordObject(decoded))
|
|
21
|
+
fail(key);
|
|
22
|
+
readVersion(decoded, key);
|
|
23
|
+
const namespace = readString(decoded, "namespace", key), resourceId = readString(decoded, "resourceId", key);
|
|
24
|
+
if (ownKey(() => KEYS.applicationMailbox(namespace, resourceId), key) !== key)
|
|
25
|
+
fail(key);
|
|
26
|
+
const nextSequence = readInteger(decoded, "nextSequence", key), openCount = readInteger(decoded, "openCount", key), admittedCount = readInteger(decoded, "admittedCount", key);
|
|
27
|
+
if (nextSequence !== admittedCount || openCount > admittedCount)
|
|
28
|
+
fail(key);
|
|
29
|
+
return {
|
|
30
|
+
recordVersion: MAILBOX_RECORD_VERSION,
|
|
31
|
+
namespace,
|
|
32
|
+
resourceId,
|
|
33
|
+
nextSequence,
|
|
34
|
+
openCount,
|
|
35
|
+
admittedCount
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function encodeMailboxRecord(record) {
|
|
39
|
+
return encode(record);
|
|
40
|
+
}
|
|
41
|
+
export function decodeApplicationCommandIdempotencyRecord(bytes, key) {
|
|
42
|
+
let decoded;
|
|
43
|
+
try {
|
|
44
|
+
decoded = decode(bytes);
|
|
45
|
+
} catch {
|
|
46
|
+
fail(key);
|
|
47
|
+
}
|
|
48
|
+
if (!isRecordObject(decoded))
|
|
49
|
+
fail(key);
|
|
50
|
+
readVersion(decoded, key);
|
|
51
|
+
return {
|
|
52
|
+
recordVersion: MAILBOX_RECORD_VERSION,
|
|
53
|
+
commandId: readIdentifier(decoded.commandId, key),
|
|
54
|
+
identityDigest: readString(decoded, "identityDigest", key)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function encodeApplicationCommandIdempotencyRecord(record) {
|
|
58
|
+
return encode(record);
|
|
59
|
+
}
|
|
60
|
+
export function decodeApplicationReadyEntry(bytes, key) {
|
|
61
|
+
let decoded;
|
|
62
|
+
try {
|
|
63
|
+
decoded = decode(bytes);
|
|
64
|
+
} catch {
|
|
65
|
+
fail(key);
|
|
66
|
+
}
|
|
67
|
+
return readIdentifier(decoded, key);
|
|
68
|
+
}
|
|
69
|
+
export function encodeApplicationReadyEntry(commandId) {
|
|
70
|
+
return encode(commandId);
|
|
71
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared runtime context and projections for the application command mailbox
|
|
3
|
+
* (WFT-84).
|
|
4
|
+
*
|
|
5
|
+
* The public class and the maintenance pass both need the same bound storage
|
|
6
|
+
* keys, resolved policy, clock, commit path, and attempt-controller registry.
|
|
7
|
+
* Passing that context explicitly — rather than reaching into the class — keeps
|
|
8
|
+
* `mailbox-maintenance.ts` a set of ordinary functions that tests
|
|
9
|
+
* can drive directly.
|
|
10
|
+
*
|
|
11
|
+
* @module core/mailbox-internals
|
|
12
|
+
*/
|
|
13
|
+
import type { BatchOperation, ConditionalBatchCondition, Storage } from '../storage/interface.ts';
|
|
14
|
+
import { AttemptRegistry } from './application-primitive-attempt-registry.ts';
|
|
15
|
+
import type { ApplicationCommandReceipt, MailboxEventSink } from './mailbox-contract.ts';
|
|
16
|
+
import { type MailboxKeys } from './mailbox-storage.ts';
|
|
17
|
+
import type { ApplicationCommandRecord, ApplicationCommandTerminalRecord } from './mailbox-types.ts';
|
|
18
|
+
import type { ResolvedMailboxPolicy } from './mailbox-validation.ts';
|
|
19
|
+
import { WeftError } from './weft-error.ts';
|
|
20
|
+
/**
|
|
21
|
+
* How many times a transition re-reads durable state and retries after losing a
|
|
22
|
+
* compare-and-swap.
|
|
23
|
+
*
|
|
24
|
+
* The mailbox header is deliberately a per-mailbox hot key — admission and every
|
|
25
|
+
* terminal transition both touch it, which is what makes backlog accounting
|
|
26
|
+
* exact — so a busy mailbox does see contention. The ceiling exists so a
|
|
27
|
+
* pathological loop surfaces as an error instead of spinning forever.
|
|
28
|
+
*/
|
|
29
|
+
export declare const MAX_MAILBOX_TRANSITION_ATTEMPTS = 25;
|
|
30
|
+
/**
|
|
31
|
+
* How many scan pages one maintenance pass may walk.
|
|
32
|
+
*
|
|
33
|
+
* The pass reaches the whole keyspace by paging, but stops after this many pages
|
|
34
|
+
* so a very large mailbox cannot make one call run unboundedly. The next pass
|
|
35
|
+
* starts again from the beginning and picks up whatever is still due.
|
|
36
|
+
*/
|
|
37
|
+
export declare const MAILBOX_MAINTENANCE_MAX_PAGES = 200;
|
|
38
|
+
/** Everything a mailbox operation needs that is fixed at construction. */
|
|
39
|
+
export type MailboxRuntime = {
|
|
40
|
+
readonly storage: Storage;
|
|
41
|
+
readonly events: MailboxEventSink | undefined;
|
|
42
|
+
readonly policy: ResolvedMailboxPolicy;
|
|
43
|
+
readonly keys: MailboxKeys;
|
|
44
|
+
readonly now: () => number;
|
|
45
|
+
readonly generateId: () => string;
|
|
46
|
+
/**
|
|
47
|
+
* Abort controllers for attempts claimed in this process, keyed by attempt
|
|
48
|
+
* token — shared across every handle onto the same mailbox, so a cancellation
|
|
49
|
+
* raised through one handle reaches a claimant holding another.
|
|
50
|
+
*/
|
|
51
|
+
readonly attemptControllers: AttemptRegistry;
|
|
52
|
+
/**
|
|
53
|
+
* Record an attempt this handle now owns, or report that disposal already won.
|
|
54
|
+
*
|
|
55
|
+
* Registering ownership in the caller's `await` continuation would race
|
|
56
|
+
* disposal: `dispose()` could run between the claim resolving and the token
|
|
57
|
+
* being recorded, see nothing to abort, and leave a live claim from a disposed
|
|
58
|
+
* mailbox. Doing both under one synchronous call closes that window.
|
|
59
|
+
*/
|
|
60
|
+
readonly adoptAttempt: (attemptToken: string) => (() => void) | null;
|
|
61
|
+
/**
|
|
62
|
+
* Where the previous maintenance pass stopped, when its page cap cut it short.
|
|
63
|
+
*
|
|
64
|
+
* Process-local rather than durable: it is an optimisation for walking a very
|
|
65
|
+
* large keyspace across successive calls, and losing it on restart only means
|
|
66
|
+
* the next pass starts from the beginning, which is always correct.
|
|
67
|
+
*/
|
|
68
|
+
readonly readMaintenanceCursor: () => string | undefined;
|
|
69
|
+
readonly writeMaintenanceCursor: (cursor: string | undefined) => void;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Attempt controllers, shared per `(storage, namespace, resourceId)` within one
|
|
73
|
+
* process.
|
|
74
|
+
*
|
|
75
|
+
* Two `Mailbox` handles onto the same durable mailbox are the same
|
|
76
|
+
* mailbox. Giving each its own registry would make the documented in-process
|
|
77
|
+
* cancellation channel silently fail whenever the claimant and the canceller
|
|
78
|
+
* held different handles — the claimant's signal would never fire and it would
|
|
79
|
+
* learn about cancellation only through renewal, which is supposed to be the
|
|
80
|
+
* *cross-process* fallback. Keyed by `Storage` identity in a `WeakMap` so a
|
|
81
|
+
* discarded backend takes its registries with it.
|
|
82
|
+
*/
|
|
83
|
+
/** The registry-scope tag for the mailbox, so it never shares a registry with another primitive. */
|
|
84
|
+
export declare const MAILBOX_PRIMITIVE = "mailbox";
|
|
85
|
+
/**
|
|
86
|
+
* Thrown when a transition keeps losing its compare-and-swap.
|
|
87
|
+
*
|
|
88
|
+
* Surfacing this beats looping forever: it means durable contention on this
|
|
89
|
+
* mailbox is real, and the caller — not a hidden retry loop — decides whether to
|
|
90
|
+
* back off, shed load, or shard the resource.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* import { MailboxContentionError } from '@lostgradient/weft';
|
|
95
|
+
*
|
|
96
|
+
* const error = new MailboxContentionError('admit', null);
|
|
97
|
+
* console.log(error.code); // 'MailboxContentionError'
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export declare class MailboxContentionError extends WeftError<'MailboxContentionError'> {
|
|
101
|
+
/** The mailbox operation that could not commit. */
|
|
102
|
+
readonly operation: string;
|
|
103
|
+
/** The command the operation targeted, or `null` for mailbox-wide operations. */
|
|
104
|
+
readonly commandId: string | null;
|
|
105
|
+
constructor(operation: string, commandId: string | null);
|
|
106
|
+
}
|
|
107
|
+
export declare function toApplicationCommandReceipt(record: ApplicationCommandRecord): ApplicationCommandReceipt;
|
|
108
|
+
/**
|
|
109
|
+
* The durable fleet event that describes a transition.
|
|
110
|
+
*
|
|
111
|
+
* A retry is distinguished from an initial admission even though both land in
|
|
112
|
+
* `accepted`, so a consumer reading the feed can tell redelivery from first
|
|
113
|
+
* delivery without diffing receipts. The payload is deliberately bounded: no
|
|
114
|
+
* command payload, no failure details, nothing unbounded.
|
|
115
|
+
*/
|
|
116
|
+
export declare function describeCommandTransition(previous: ApplicationCommandRecord | null, next: ApplicationCommandRecord): {
|
|
117
|
+
readonly kind: string;
|
|
118
|
+
readonly payload: unknown;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Abort and forget the process-local controller for one attempt.
|
|
122
|
+
*
|
|
123
|
+
* Releasing a lease must never leave a live controller behind: the signal is
|
|
124
|
+
* attempt-scoped, so a later attempt on the same command gets a fresh one.
|
|
125
|
+
*/
|
|
126
|
+
export declare function releaseAttemptController(runtime: MailboxRuntime, attemptToken: string, reason: string, commandId?: string): void;
|
|
127
|
+
/**
|
|
128
|
+
* Release every attempt this process holds for one command.
|
|
129
|
+
*
|
|
130
|
+
* For an observer that finds the command gone — retired by retention in
|
|
131
|
+
* another process, whose maintenance cannot reach this registry — every local
|
|
132
|
+
* attempt on it is over, whatever token it held.
|
|
133
|
+
*/
|
|
134
|
+
export declare function releaseAttemptsForCommand(runtime: MailboxRuntime, commandId: string, reason: string, currentToken?: string, observedAt?: number): void;
|
|
135
|
+
/**
|
|
136
|
+
* Commit one command transition together with the index maintenance and
|
|
137
|
+
* backlog accounting it implies.
|
|
138
|
+
*
|
|
139
|
+
* Terminalizing a command decrements the mailbox's open count in the same
|
|
140
|
+
* conditional batch, so `capacity()` can never drift from the records it
|
|
141
|
+
* describes. Admission is the one transition that builds its own header
|
|
142
|
+
* operation, because it also allocates the FIFO sequence.
|
|
143
|
+
*
|
|
144
|
+
* Returns `false` when a compare-and-swap was lost; the caller re-reads and
|
|
145
|
+
* re-decides rather than retrying blindly with stale bytes.
|
|
146
|
+
*/
|
|
147
|
+
export declare function commitCommandTransition(runtime: MailboxRuntime, options: {
|
|
148
|
+
readonly previous: ApplicationCommandRecord | null;
|
|
149
|
+
readonly expectedBytes: Uint8Array | null;
|
|
150
|
+
readonly next: ApplicationCommandRecord;
|
|
151
|
+
readonly now: number;
|
|
152
|
+
readonly extraConditions?: readonly ConditionalBatchCondition[] | undefined;
|
|
153
|
+
readonly extraOperations?: readonly BatchOperation[] | undefined;
|
|
154
|
+
}): Promise<boolean>;
|
|
155
|
+
/** Whether a record occupies one of the four terminal dispositions. */
|
|
156
|
+
export declare function isTerminalRecord(record: ApplicationCommandRecord): record is ApplicationCommandTerminalRecord;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { AttemptRegistry } from "./application-primitive-attempt-registry.js";
|
|
2
|
+
import {
|
|
3
|
+
commitMailboxTransition,
|
|
4
|
+
headerOperation,
|
|
5
|
+
loadMailboxHeader,
|
|
6
|
+
planCommandTransition
|
|
7
|
+
} from "./mailbox-storage.js";
|
|
8
|
+
import { isApplicationCommandLeased } from "./mailbox-types.js";
|
|
9
|
+
import { WeftError } from "./weft-error.js";
|
|
10
|
+
export const MAX_MAILBOX_TRANSITION_ATTEMPTS = 25, MAILBOX_MAINTENANCE_MAX_PAGES = 200, MAILBOX_PRIMITIVE = "mailbox";
|
|
11
|
+
|
|
12
|
+
export class MailboxContentionError extends WeftError {
|
|
13
|
+
operation;
|
|
14
|
+
commandId;
|
|
15
|
+
constructor(operation, commandId) {
|
|
16
|
+
super("MailboxContentionError", `Application mailbox ${operation}${commandId === null ? "" : ` for command "${commandId}"`} lost its storage precondition after ${MAX_MAILBOX_TRANSITION_ATTEMPTS} attempts.`);
|
|
17
|
+
this.operation = operation;
|
|
18
|
+
this.commandId = commandId;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function receiptLeaseFields(record) {
|
|
22
|
+
if (!isApplicationCommandLeased(record))
|
|
23
|
+
return {};
|
|
24
|
+
return {
|
|
25
|
+
claimedAt: record.claimedAt,
|
|
26
|
+
visibilityExpiresAt: record.visibilityExpiresAt,
|
|
27
|
+
lastActivityAt: record.lastActivityAt,
|
|
28
|
+
progress: record.progress
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function receiptTerminalFields(record) {
|
|
32
|
+
if (!isTerminalRecord(record))
|
|
33
|
+
return record.state === "cancellation-requested" ? {
|
|
34
|
+
cancellationRequestedAt: record.cancellationRequestedAt,
|
|
35
|
+
cancellationReason: record.cancellationReason
|
|
36
|
+
} : {};
|
|
37
|
+
return {
|
|
38
|
+
cancellationRequestedAt: record.cancellationRequestedAt,
|
|
39
|
+
cancellationReason: record.cancellationReason,
|
|
40
|
+
terminalAt: record.terminalAt,
|
|
41
|
+
outcome: record.outcome,
|
|
42
|
+
failure: record.failure,
|
|
43
|
+
cleanupPending: record.cleanupPending
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function deepFreeze(value) {
|
|
47
|
+
if (typeof value !== "object" || value === null || Object.isFrozen(value))
|
|
48
|
+
return value;
|
|
49
|
+
Object.freeze(value);
|
|
50
|
+
for (const nested of Object.values(value))
|
|
51
|
+
deepFreeze(nested);
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
export function toApplicationCommandReceipt(record) {
|
|
55
|
+
return deepFreeze({
|
|
56
|
+
commandId: record.commandId,
|
|
57
|
+
namespace: record.namespace,
|
|
58
|
+
resourceId: record.resourceId,
|
|
59
|
+
sequence: record.sequence,
|
|
60
|
+
state: record.state,
|
|
61
|
+
caller: record.caller,
|
|
62
|
+
target: record.target,
|
|
63
|
+
kind: record.kind,
|
|
64
|
+
payloadDigest: record.payloadDigest,
|
|
65
|
+
payloadForm: record.payload.form,
|
|
66
|
+
payloadMediaType: record.payloadMediaType,
|
|
67
|
+
payloadSchema: record.payloadSchema,
|
|
68
|
+
idempotencyKey: record.idempotencyKey,
|
|
69
|
+
causation: record.causation,
|
|
70
|
+
acceptedAt: record.acceptedAt,
|
|
71
|
+
availableAt: record.availableAt,
|
|
72
|
+
absoluteDeadlineAt: record.absoluteDeadlineAt,
|
|
73
|
+
attempt: record.attempt,
|
|
74
|
+
retryCount: record.retryCount,
|
|
75
|
+
maxAttempts: record.maxAttempts,
|
|
76
|
+
generation: record.generation,
|
|
77
|
+
...receiptLeaseFields(record),
|
|
78
|
+
...receiptTerminalFields(record)
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const STATE_EVENT_KINDS = {
|
|
82
|
+
accepted: "mailbox:command-accepted",
|
|
83
|
+
available: "mailbox:command-available",
|
|
84
|
+
claimed: "mailbox:command-claimed",
|
|
85
|
+
"cancellation-requested": "mailbox:command-cancellation-requested",
|
|
86
|
+
applied: "mailbox:command-applied",
|
|
87
|
+
rejected: "mailbox:command-rejected",
|
|
88
|
+
cancelled: "mailbox:command-cancelled",
|
|
89
|
+
"dead-lettered": "mailbox:command-dead-lettered"
|
|
90
|
+
};
|
|
91
|
+
export function describeCommandTransition(previous, next) {
|
|
92
|
+
return {
|
|
93
|
+
kind: previous !== null && next.state === "accepted" && next.retryCount > previous.retryCount ? "mailbox:command-retry-scheduled" : STATE_EVENT_KINDS[next.state],
|
|
94
|
+
payload: {
|
|
95
|
+
namespace: next.namespace,
|
|
96
|
+
resourceId: next.resourceId,
|
|
97
|
+
commandId: next.commandId,
|
|
98
|
+
sequence: next.sequence,
|
|
99
|
+
state: next.state,
|
|
100
|
+
commandKind: next.kind,
|
|
101
|
+
target: next.target,
|
|
102
|
+
attempt: next.attempt,
|
|
103
|
+
retryCount: next.retryCount,
|
|
104
|
+
generation: next.generation,
|
|
105
|
+
previousState: previous === null ? null : previous.state
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export function releaseAttemptController(runtime, attemptToken, reason, commandId) {
|
|
110
|
+
const registration = runtime.attemptControllers.get(attemptToken);
|
|
111
|
+
if (registration === void 0)
|
|
112
|
+
return;
|
|
113
|
+
if (commandId !== void 0 && registration.subjectId !== commandId)
|
|
114
|
+
return;
|
|
115
|
+
runtime.attemptControllers.delete(attemptToken);
|
|
116
|
+
registration.release();
|
|
117
|
+
if (!registration.controller.signal.aborted)
|
|
118
|
+
registration.controller.abort(Error(reason));
|
|
119
|
+
}
|
|
120
|
+
export function releaseAttemptsForCommand(runtime, commandId, reason, currentToken, observedAt) {
|
|
121
|
+
for (const attemptToken of runtime.attemptControllers.tokensFor(commandId)) {
|
|
122
|
+
const registration = runtime.attemptControllers.get(attemptToken);
|
|
123
|
+
if (registration === void 0 || attemptToken === currentToken)
|
|
124
|
+
continue;
|
|
125
|
+
if (registration.committedSerial === null)
|
|
126
|
+
continue;
|
|
127
|
+
if (observedAt !== void 0 && registration.committedSerial > observedAt)
|
|
128
|
+
continue;
|
|
129
|
+
releaseAttemptController(runtime, attemptToken, reason);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
export async function commitCommandTransition(runtime, options) {
|
|
133
|
+
const closing = options.previous !== null && !isTerminalRecord(options.previous) && isTerminalRecord(options.next), extraConditions = [...options.extraConditions ?? []], extraOperations = [...options.extraOperations ?? []];
|
|
134
|
+
if (closing) {
|
|
135
|
+
const header = await loadMailboxHeader(runtime.storage, runtime.keys, runtime.policy.namespace, runtime.policy.resourceId);
|
|
136
|
+
extraConditions.push({ key: runtime.keys.header, expectedValue: header.bytes });
|
|
137
|
+
extraOperations.push(headerOperation(runtime.keys, {
|
|
138
|
+
...header.record,
|
|
139
|
+
openCount: Math.max(0, header.record.openCount - 1)
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
return commitMailboxTransition(runtime.storage, runtime.events, planCommandTransition(runtime.keys, {
|
|
143
|
+
previous: options.previous,
|
|
144
|
+
expectedBytes: options.expectedBytes,
|
|
145
|
+
next: options.next,
|
|
146
|
+
event: describeCommandTransition(options.previous, options.next),
|
|
147
|
+
now: options.now,
|
|
148
|
+
extraConditions,
|
|
149
|
+
extraOperations
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
export function isTerminalRecord(record) {
|
|
153
|
+
return record.state === "applied" || record.state === "rejected" || record.state === "cancelled" || record.state === "dead-lettered";
|
|
154
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bounded maintenance pass for the application command mailbox (WFT-84).
|
|
3
|
+
*
|
|
4
|
+
* Maintenance is the only thing that advances time-driven work: releasing a
|
|
5
|
+
* delayed command, reclaiming an expired lease at its original FIFO position,
|
|
6
|
+
* dead-lettering a command past its absolute deadline, and retiring terminal
|
|
7
|
+
* receipts past their retention window. Nothing here runs on a hidden timer — a
|
|
8
|
+
* host with `backgroundTasks: 'manual'` calls `runMaintenance()` and gets
|
|
9
|
+
* exactly one deterministic pass.
|
|
10
|
+
*
|
|
11
|
+
* Every pass is bounded. It walks the command keyspace in pages of
|
|
12
|
+
* `maintenanceBatchSize` records, up to {@link MAILBOX_MAINTENANCE_MAX_PAGES}
|
|
13
|
+
* pages per call, carrying its cursor to the next call when the cap cuts it
|
|
14
|
+
* short; and it retires at most `maintenanceBatchSize` terminal receipts. A large
|
|
15
|
+
* mailbox drains across several calls instead of one unbounded sweep.
|
|
16
|
+
*
|
|
17
|
+
* @module core/mailbox-maintenance
|
|
18
|
+
*/
|
|
19
|
+
import type { MailboxMaintenanceReport } from './mailbox-contract.ts';
|
|
20
|
+
import { type MailboxRuntime } from './mailbox-internals.ts';
|
|
21
|
+
/**
|
|
22
|
+
* Run one bounded maintenance pass.
|
|
23
|
+
*
|
|
24
|
+
* The scan decodes every command record it visits, so a single corrupt record
|
|
25
|
+
* raises `PersistedDataCorruptError` and halts the pass. That is deliberate: a
|
|
26
|
+
* mailbox whose durable state is untrustworthy must not keep reclaiming leases
|
|
27
|
+
* and dead-lettering commands around the damage.
|
|
28
|
+
*/
|
|
29
|
+
export declare function runMailboxMaintenance(runtime: MailboxRuntime, now: number): Promise<MailboxMaintenanceReport>;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { formatSortableStorageTimestamp, storageConditionalBatch } from "../storage/interface.js";
|
|
2
|
+
import { leaseCommitSerial } from "./application-primitive-attempt-registry.js";
|
|
3
|
+
import { decodeApplicationCommandRecord } from "./mailbox-codec.js";
|
|
4
|
+
import {
|
|
5
|
+
decodeApplicationCommandIdempotencyRecord,
|
|
6
|
+
decodeApplicationReadyEntry
|
|
7
|
+
} from "./mailbox-index-codec.js";
|
|
8
|
+
import {
|
|
9
|
+
commitCommandTransition,
|
|
10
|
+
isTerminalRecord,
|
|
11
|
+
MAILBOX_MAINTENANCE_MAX_PAGES,
|
|
12
|
+
MailboxContentionError,
|
|
13
|
+
MAX_MAILBOX_TRANSITION_ATTEMPTS,
|
|
14
|
+
releaseAttemptController,
|
|
15
|
+
releaseAttemptsForCommand
|
|
16
|
+
} from "./mailbox-internals.js";
|
|
17
|
+
import { loadCommand } from "./mailbox-storage.js";
|
|
18
|
+
import { recoverExpiredCommand } from "./mailbox-transitions-recovery.js";
|
|
19
|
+
import { isCommandPastDeadline, releaseWaitingCommand } from "./mailbox-transitions.js";
|
|
20
|
+
import { isApplicationCommandLeased } from "./mailbox-types.js";
|
|
21
|
+
function classify(record, now) {
|
|
22
|
+
if (isTerminalRecord(record))
|
|
23
|
+
return null;
|
|
24
|
+
if (isCommandPastDeadline(record, now))
|
|
25
|
+
return "recover";
|
|
26
|
+
if (record.state === "accepted" && now >= record.availableAt)
|
|
27
|
+
return "release";
|
|
28
|
+
if (isApplicationCommandLeased(record) && now >= record.visibilityExpiresAt)
|
|
29
|
+
return "recover";
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
function countTransition(counters, previous, next) {
|
|
33
|
+
if (next.state === "available" && previous.state === "accepted")
|
|
34
|
+
counters.released += 1;
|
|
35
|
+
else if (next.state === "accepted")
|
|
36
|
+
counters.reclaimed += 1;
|
|
37
|
+
else if (next.state === "dead-lettered")
|
|
38
|
+
counters.deadLettered += 1;
|
|
39
|
+
else if (next.state === "cancelled")
|
|
40
|
+
counters.cancelled += 1;
|
|
41
|
+
}
|
|
42
|
+
function reconcileLocalAttempts(runtime, commandId, record, observedAt) {
|
|
43
|
+
releaseAttemptsForCommand(runtime, commandId, "This attempt is no longer the current lease on its command.", record !== void 0 && isApplicationCommandLeased(record) ? record.attemptToken : void 0, observedAt);
|
|
44
|
+
}
|
|
45
|
+
async function advanceCommand(runtime, commandId, now, counters) {
|
|
46
|
+
for (let attempt = 1;attempt <= MAX_MAILBOX_TRANSITION_ATTEMPTS; attempt += 1) {
|
|
47
|
+
const observedAt = leaseCommitSerial(), loaded = await loadCommand(runtime.storage, runtime.keys, commandId);
|
|
48
|
+
if (loaded === null) {
|
|
49
|
+
reconcileLocalAttempts(runtime, commandId, void 0, observedAt);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const due = classify(loaded.record, now);
|
|
53
|
+
if (due === null) {
|
|
54
|
+
reconcileLocalAttempts(runtime, commandId, loaded.record, observedAt);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const transition = due === "release" ? releaseWaitingCommand(loaded.record, now) : recoverExpiredCommand(loaded.record, {
|
|
58
|
+
now,
|
|
59
|
+
retryBackoffMs: runtime.policy.retryBackoffMs,
|
|
60
|
+
maxRetryBackoffMs: runtime.policy.maxRetryBackoffMs
|
|
61
|
+
});
|
|
62
|
+
if (!transition.ok)
|
|
63
|
+
return;
|
|
64
|
+
if (!await commitCommandTransition(runtime, {
|
|
65
|
+
previous: loaded.record,
|
|
66
|
+
expectedBytes: loaded.bytes,
|
|
67
|
+
next: transition.next,
|
|
68
|
+
now
|
|
69
|
+
}))
|
|
70
|
+
continue;
|
|
71
|
+
if (isApplicationCommandLeased(loaded.record))
|
|
72
|
+
releaseAttemptController(runtime, loaded.record.attemptToken, "The application mailbox reclaimed this attempt after its lease expired.");
|
|
73
|
+
countTransition(counters, loaded.record, transition.next);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
throw new MailboxContentionError("maintenance", commandId);
|
|
77
|
+
}
|
|
78
|
+
async function retireOneReceipt(runtime, indexKey, indexBytes) {
|
|
79
|
+
const commandId = commandIdFromTerminalKey(indexKey), observedAt = leaseCommitSerial(), loaded = commandId === null ? null : await loadCommand(runtime.storage, runtime.keys, commandId);
|
|
80
|
+
if (commandId === null || loaded !== null && !ownsTerminalEntry(runtime, indexKey, loaded)) {
|
|
81
|
+
await discardTerminalEntry(runtime, indexKey, indexBytes);
|
|
82
|
+
return !1;
|
|
83
|
+
}
|
|
84
|
+
const operations = [{ type: "delete", key: indexKey }];
|
|
85
|
+
if (loaded === null)
|
|
86
|
+
return storageConditionalBatch(runtime.storage, [{ key: indexKey, expectedValue: indexBytes }], operations);
|
|
87
|
+
operations.push({ type: "delete", key: runtime.keys.command(commandId) });
|
|
88
|
+
const auxiliary = await ownedAuxiliaryEntries(runtime, loaded);
|
|
89
|
+
operations.push(...auxiliary.operations);
|
|
90
|
+
const retired = await storageConditionalBatch(runtime.storage, [
|
|
91
|
+
{ key: runtime.keys.command(commandId), expectedValue: loaded.bytes },
|
|
92
|
+
...auxiliary.conditions
|
|
93
|
+
], operations);
|
|
94
|
+
if (retired)
|
|
95
|
+
releaseAttemptsForCommand(runtime, commandId, "This command was retired; its attempt is over.", void 0, observedAt);
|
|
96
|
+
return retired;
|
|
97
|
+
}
|
|
98
|
+
async function ownedAuxiliaryEntries(runtime, loaded) {
|
|
99
|
+
const conditions = [], operations = [], sequenceKey = runtime.keys.bySequence(loaded.record.sequence), sequenceBytes = await runtime.storage.get(sequenceKey);
|
|
100
|
+
if (sequenceBytes !== null && decodeApplicationReadyEntry(sequenceBytes, sequenceKey) === loaded.record.commandId) {
|
|
101
|
+
conditions.push({ key: sequenceKey, expectedValue: sequenceBytes });
|
|
102
|
+
operations.push({ type: "delete", key: sequenceKey });
|
|
103
|
+
}
|
|
104
|
+
if (loaded.record.idempotencyKey === void 0)
|
|
105
|
+
return { conditions, operations };
|
|
106
|
+
const bindingKey = runtime.keys.idempotency(loaded.record.idempotencyKey), bindingBytes = await runtime.storage.get(bindingKey);
|
|
107
|
+
if (bindingBytes !== null && decodeApplicationCommandIdempotencyRecord(bindingBytes, bindingKey).commandId === loaded.record.commandId) {
|
|
108
|
+
conditions.push({ key: bindingKey, expectedValue: bindingBytes });
|
|
109
|
+
operations.push({ type: "delete", key: bindingKey });
|
|
110
|
+
}
|
|
111
|
+
return { conditions, operations };
|
|
112
|
+
}
|
|
113
|
+
function ownsTerminalEntry(runtime, indexKey, loaded) {
|
|
114
|
+
return isTerminalRecord(loaded.record) && runtime.keys.terminal(loaded.record.terminalAt, loaded.record.commandId) === indexKey;
|
|
115
|
+
}
|
|
116
|
+
async function discardTerminalEntry(runtime, indexKey, indexBytes) {
|
|
117
|
+
await storageConditionalBatch(runtime.storage, [{ key: indexKey, expectedValue: indexBytes }], [{ type: "delete", key: indexKey }]);
|
|
118
|
+
}
|
|
119
|
+
async function retireTerminalReceipts(runtime, now, counters) {
|
|
120
|
+
const horizon = now - runtime.policy.terminalRetentionMs;
|
|
121
|
+
if (horizon < 0)
|
|
122
|
+
return;
|
|
123
|
+
const expired = [], malformed = [];
|
|
124
|
+
for await (const [key, value] of runtime.storage.scan(runtime.keys.terminalPrefix, {
|
|
125
|
+
limit: runtime.policy.maintenanceBatchSize
|
|
126
|
+
})) {
|
|
127
|
+
const terminalAt = parseTerminalAt(key, runtime.keys.terminalPrefix);
|
|
128
|
+
if (terminalAt === null) {
|
|
129
|
+
malformed.push([key, value]);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (terminalAt >= horizon)
|
|
133
|
+
break;
|
|
134
|
+
expired.push([key, value]);
|
|
135
|
+
}
|
|
136
|
+
for (const [indexKey, bytes] of malformed)
|
|
137
|
+
await discardTerminalEntry(runtime, indexKey, bytes);
|
|
138
|
+
for (const [indexKey, bytes] of expired)
|
|
139
|
+
if (await retireOneReceipt(runtime, indexKey, bytes))
|
|
140
|
+
counters.retired += 1;
|
|
141
|
+
}
|
|
142
|
+
function parseTerminalAt(key, prefix) {
|
|
143
|
+
const suffix = key.slice(prefix.length), separator = suffix.indexOf(":");
|
|
144
|
+
if (separator === -1)
|
|
145
|
+
return null;
|
|
146
|
+
const segment = suffix.slice(0, separator), parsed = Number(segment);
|
|
147
|
+
return Number.isSafeInteger(parsed) && formatSortableStorageTimestamp(parsed) === segment ? parsed : null;
|
|
148
|
+
}
|
|
149
|
+
function commandIdFromTerminalKey(key) {
|
|
150
|
+
const separator = key.lastIndexOf(":");
|
|
151
|
+
if (separator === -1)
|
|
152
|
+
return null;
|
|
153
|
+
try {
|
|
154
|
+
return decodeURIComponent(key.slice(separator + 1));
|
|
155
|
+
} catch {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async function collectDueCommands(runtime, now, startAfter) {
|
|
160
|
+
const batchSize = runtime.policy.maintenanceBatchSize, due = [];
|
|
161
|
+
let cursor = startAfter;
|
|
162
|
+
for (let page = 0;page < MAILBOX_MAINTENANCE_MAX_PAGES; page += 1) {
|
|
163
|
+
let seen = 0;
|
|
164
|
+
const options = cursor === void 0 ? { limit: batchSize } : { limit: batchSize, gt: cursor }, observedAt = leaseCommitSerial();
|
|
165
|
+
for await (const [key, value] of runtime.storage.scan(runtime.keys.commandPrefix, options)) {
|
|
166
|
+
seen += 1;
|
|
167
|
+
cursor = key;
|
|
168
|
+
const record = decodeApplicationCommandRecord(value, key);
|
|
169
|
+
reconcileLocalAttempts(runtime, record.commandId, record, observedAt);
|
|
170
|
+
if (classify(record, now) !== null)
|
|
171
|
+
due.push(record.commandId);
|
|
172
|
+
}
|
|
173
|
+
if (seen < batchSize)
|
|
174
|
+
return { due, nextCursor: void 0 };
|
|
175
|
+
}
|
|
176
|
+
return { due, nextCursor: cursor };
|
|
177
|
+
}
|
|
178
|
+
export async function runMailboxMaintenance(runtime, now) {
|
|
179
|
+
const counters = {
|
|
180
|
+
released: 0,
|
|
181
|
+
reclaimed: 0,
|
|
182
|
+
deadLettered: 0,
|
|
183
|
+
cancelled: 0,
|
|
184
|
+
retired: 0
|
|
185
|
+
}, previousCursor = runtime.readMaintenanceCursor(), scan = await collectDueCommands(runtime, now, previousCursor);
|
|
186
|
+
try {
|
|
187
|
+
for (const commandId of scan.due)
|
|
188
|
+
await advanceCommand(runtime, commandId, now, counters);
|
|
189
|
+
} catch (error) {
|
|
190
|
+
runtime.writeMaintenanceCursor(previousCursor);
|
|
191
|
+
throw error;
|
|
192
|
+
}
|
|
193
|
+
runtime.writeMaintenanceCursor(scan.nextCursor);
|
|
194
|
+
await retireTerminalReceipts(runtime, now, counters);
|
|
195
|
+
return Object.freeze({ ...counters });
|
|
196
|
+
}
|