@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
|
@@ -1,23 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
KEYS,
|
|
3
|
+
requireStorageCapability,
|
|
4
|
+
storageValuesEqual
|
|
5
|
+
} from "../../../storage/interface.js";
|
|
2
6
|
import { AtomicStateConflictError } from "../../atomic-state.js";
|
|
7
|
+
import { WorkflowAlreadyExistsError } from "../errors.js";
|
|
3
8
|
import {
|
|
4
9
|
commitFencedEngineWrite,
|
|
5
10
|
commitFencedEngineWriteAllowingPreconditionFailure
|
|
6
11
|
} from "../fenced-write.js";
|
|
12
|
+
import { WorkflowRevisionUnavailableError } from "../revision-errors.js";
|
|
7
13
|
import {
|
|
8
14
|
commitWithWorkflowClaimFold,
|
|
9
15
|
prepareWorkflowClaimFold,
|
|
10
16
|
throwWorkflowClaimUnavailable
|
|
11
17
|
} from "../workflow-claim-fold.js";
|
|
12
18
|
import { buildStartBatchOperations } from "./start-batch.js";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
constructor() {
|
|
16
|
-
super("start idempotency compare-and-swap lost to a concurrent caller");
|
|
17
|
-
this.name = "StartIdempotencyRaceLostError";
|
|
18
|
-
}
|
|
19
|
-
}
|
|
19
|
+
import { StartIdempotencyRaceLostError } from "./start-commit-errors.js";
|
|
20
|
+
import { attributeLostStartPreconditionOrRetry } from "./start-precondition-attribution.js";
|
|
20
21
|
const WORKFLOW_CONCURRENCY_ADMISSION_MAX_ATTEMPTS = 5;
|
|
22
|
+
function needsCatalogEntryStartPrecondition(internals, state) {
|
|
23
|
+
return internals.options.ownershipMode !== "none" && state.revision !== void 0;
|
|
24
|
+
}
|
|
25
|
+
async function buildCatalogEntryStartPrecondition(internals, state) {
|
|
26
|
+
return {
|
|
27
|
+
source: "catalog-entry",
|
|
28
|
+
condition: await buildCatalogEntryRevisionCondition(internals, state.type, state.revision)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export async function buildCatalogEntryRevisionCondition(internals, type, revision) {
|
|
32
|
+
const key = KEYS.catalogEntry(type, revision), entryBytes = await internals.storage.get(key);
|
|
33
|
+
if (entryBytes === null)
|
|
34
|
+
throw new WorkflowRevisionUnavailableError(type, revision, "not-installed");
|
|
35
|
+
return { key, expectedValue: entryBytes };
|
|
36
|
+
}
|
|
21
37
|
async function persistStartBatch(internals, workflowId, startOperations, conditions, claimFold, isDelayedStart) {
|
|
22
38
|
if (claimFold) {
|
|
23
39
|
const result = await commitWithWorkflowClaimFold(internals, claimFold, startOperations, conditions.map((entry) => entry.condition), "workflow claim acquisition");
|
|
@@ -33,9 +49,19 @@ async function persistStartBatch(internals, workflowId, startOperations, conditi
|
|
|
33
49
|
requireStorageCapability(internals.storage, "conditionalBatch", "start preconditions");
|
|
34
50
|
return await commitFencedEngineWriteAllowingPreconditionFailure(internals, fenceWorkflowId, startOperations, conditions.map((entry) => entry.condition)) ? "committed" : "precondition-lost";
|
|
35
51
|
}
|
|
36
|
-
async function
|
|
52
|
+
async function hasStartConditionConflict(internals, conditions, source) {
|
|
53
|
+
for (const entry of conditions) {
|
|
54
|
+
if (entry.source !== source)
|
|
55
|
+
continue;
|
|
56
|
+
const currentValue = await internals.storage.get(entry.condition.key);
|
|
57
|
+
if (!storageValuesEqual(currentValue, entry.condition.expectedValue))
|
|
58
|
+
return !0;
|
|
59
|
+
}
|
|
60
|
+
return !1;
|
|
61
|
+
}
|
|
62
|
+
async function hasCatalogEntryConflict(internals, conditions) {
|
|
37
63
|
for (const entry of conditions) {
|
|
38
|
-
if (entry.source !== "
|
|
64
|
+
if (entry.source !== "catalog-entry")
|
|
39
65
|
continue;
|
|
40
66
|
const currentValue = await internals.storage.get(entry.condition.key);
|
|
41
67
|
if (!storageValuesEqual(currentValue, entry.condition.expectedValue))
|
|
@@ -49,6 +75,9 @@ function tagStartPreconditions(conditions) {
|
|
|
49
75
|
condition
|
|
50
76
|
}));
|
|
51
77
|
}
|
|
78
|
+
function tagDuplicateIdCondition(condition) {
|
|
79
|
+
return condition === void 0 ? [] : [{ source: "duplicate-id", condition }];
|
|
80
|
+
}
|
|
52
81
|
function tagWorkflowConcurrencyConditions(conditions) {
|
|
53
82
|
return conditions.map((condition) => ({
|
|
54
83
|
source: "workflow-concurrency",
|
|
@@ -66,18 +95,23 @@ export async function buildAndCommitStartBatch(context, buildIdempotentStartOper
|
|
|
66
95
|
for (let attempt = 0;attempt < maxAttempts; attempt += 1) {
|
|
67
96
|
const idempotent = buildIdempotentStartOperations?.(workflowId), workflowConcurrency = await context.buildWorkflowConcurrencyStartOperations?.();
|
|
68
97
|
lastWorkflowConcurrencyStateKey = workflowConcurrency?.stateKey ?? lastWorkflowConcurrencyStateKey;
|
|
69
|
-
const startOperations = buildStartBatchOperations(internals, workflowId, state, checkpoint, registration, options, state.executionDeadline, context.delayedStartTimer, context.persistedWorkflowStartHeaders, mergeAdditionalStartOperations(context.additionalStartOperations, mergeAdditionalStartOperations(idempotent?.operations, workflowConcurrency?.operations)), context.callbacks, context.purgeDeleteOperations), conditions = [
|
|
98
|
+
const catalogEntryPrecondition = needsCatalogEntryStartPrecondition(internals, state) ? await buildCatalogEntryStartPrecondition(internals, state) : void 0, startOperations = buildStartBatchOperations(internals, workflowId, state, checkpoint, registration, options, state.executionDeadline, context.delayedStartTimer, context.persistedWorkflowStartHeaders, mergeAdditionalStartOperations(context.additionalStartOperations, mergeAdditionalStartOperations(idempotent?.operations, workflowConcurrency?.operations)), context.callbacks, context.purgeDeleteOperations), conditions = [
|
|
70
99
|
...tagStartPreconditions(idempotent?.conditions),
|
|
71
|
-
...
|
|
100
|
+
...tagDuplicateIdCondition(context.duplicateIdCondition),
|
|
101
|
+
...tagWorkflowConcurrencyConditions(workflowConcurrency?.conditions ?? []),
|
|
102
|
+
...catalogEntryPrecondition === void 0 ? [] : [catalogEntryPrecondition]
|
|
72
103
|
], isDelayedStart = context.delayedStartTimer !== void 0, claimFold = isDelayedStart ? void 0 : await prepareWorkflowClaimFold(internals, workflowId), outcome = await persistStartBatch(internals, workflowId, startOperations, conditions, claimFold, isDelayedStart);
|
|
73
104
|
if (outcome === "committed")
|
|
74
105
|
return;
|
|
75
|
-
if (await
|
|
106
|
+
if (outcome !== "claim-lost" && await hasStartConditionConflict(internals, conditions, "duplicate-id"))
|
|
107
|
+
throw new WorkflowAlreadyExistsError(workflowId);
|
|
108
|
+
if (await hasStartConditionConflict(internals, conditions, "start-precondition"))
|
|
76
109
|
throw new StartIdempotencyRaceLostError;
|
|
110
|
+
if (await hasCatalogEntryConflict(internals, conditions))
|
|
111
|
+
throw new WorkflowRevisionUnavailableError(state.type, state.revision, "not-installed");
|
|
77
112
|
if (outcome === "claim-lost")
|
|
78
113
|
return throwWorkflowClaimUnavailable(internals, workflowId);
|
|
79
|
-
|
|
80
|
-
throw new StartIdempotencyRaceLostError;
|
|
114
|
+
await attributeLostStartPreconditionOrRetry(workflowId, context.duplicateIdCondition !== void 0, workflowConcurrency !== void 0, () => hasStartConditionConflict(internals, conditions, "workflow-concurrency"));
|
|
81
115
|
}
|
|
82
116
|
throw new AtomicStateConflictError(lastWorkflowConcurrencyStateKey ?? "workflow concurrency admission", WORKFLOW_CONCURRENCY_ADMISSION_MAX_ATTEMPTS);
|
|
83
117
|
}
|
|
@@ -2,8 +2,24 @@ import type { Checkpoint, StartOptions, WorkflowState } from '../../types.ts';
|
|
|
2
2
|
import type { EngineInternals } from '../internals.ts';
|
|
3
3
|
import { type LifecycleCallbacks, type RegistrationEntry } from './shared.ts';
|
|
4
4
|
export declare function runWorkflowStartInterceptor(_internals: EngineInternals, workflowId: string, workflowType: string, input: unknown, parentHeaders: Map<string, string> | undefined, callbacks: LifecycleCallbacks): Map<string, string> | undefined;
|
|
5
|
-
export declare function startWorkflowExecution(internals: EngineInternals, workflowId: string, workflowExecutionToken: string | undefined, workflowType: string,
|
|
6
|
-
|
|
5
|
+
export declare function startWorkflowExecution(internals: EngineInternals, workflowId: string, workflowExecutionToken: string | undefined, workflowType: string,
|
|
6
|
+
/**
|
|
7
|
+
* The starting run's persisted `WorkflowState.revision` (WFT-19/WFT-20):
|
|
8
|
+
* populates the per-instance identity cache readers use for revision-scoped
|
|
9
|
+
* routing, and is echoed to the strategy's `startWorkflow` for worker-protocol
|
|
10
|
+
* revision validation.
|
|
11
|
+
*/
|
|
12
|
+
revision: string | undefined, input: unknown, checkpoint: Checkpoint, nestingDepth: number, executionDeadline: number | undefined, executionStateOwnerId: string, _callbacks?: LifecycleCallbacks): void;
|
|
13
|
+
export declare function beginWorkflowExecution(internals: EngineInternals, workflowId: string, workflowExecutionToken: string | undefined, workflowType: string,
|
|
14
|
+
/**
|
|
15
|
+
* The starting run's persisted `WorkflowState.revision` (WFT-19/WFT-20).
|
|
16
|
+
* Threaded into both the queued-inline path below (which also re-reads
|
|
17
|
+
* `revision` off the RELOADED `WorkflowState` at flush time —
|
|
18
|
+
* `inline-launch-queue.ts` — exactly like it already does for
|
|
19
|
+
* `workflowExecutionToken`) and the non-inline `startWorkflowExecution`
|
|
20
|
+
* branch, which populates the per-instance identity cache with it directly.
|
|
21
|
+
*/
|
|
22
|
+
revision: string | undefined, input: unknown, checkpoint: Checkpoint, executionDeadline: number | undefined, executionStateOwnerId: string, _registration: RegistrationEntry, callbacks: LifecycleCallbacks, onStarted?: () => void): void;
|
|
7
23
|
/**
|
|
8
24
|
* `defer: false` is an inline-only liveness gate: it awaits the moment the
|
|
9
25
|
* generator is first driven. A worker-mode start queues to the Worker transport
|
|
@@ -20,13 +20,14 @@ export function runWorkflowStartInterceptor(_internals, workflowId, workflowType
|
|
|
20
20
|
});
|
|
21
21
|
return capturedHeaders;
|
|
22
22
|
}
|
|
23
|
-
export function startWorkflowExecution(internals, workflowId, workflowExecutionToken, workflowType, input, checkpoint, nestingDepth, executionDeadline, executionStateOwnerId, _callbacks) {
|
|
23
|
+
export function startWorkflowExecution(internals, workflowId, workflowExecutionToken, workflowType, revision, input, checkpoint, nestingDepth, executionDeadline, executionStateOwnerId, _callbacks) {
|
|
24
24
|
if (nestingDepth !== 0)
|
|
25
25
|
internals.workflowNestingDepths.set(workflowId, nestingDepth);
|
|
26
|
-
internals.workflowTypeByWorkflowId.set(workflowId, workflowType);
|
|
26
|
+
internals.workflowTypeByWorkflowId.set(workflowId, { type: workflowType, revision });
|
|
27
27
|
internals.strategy.startWorkflow({
|
|
28
28
|
workflowId,
|
|
29
29
|
...workflowExecutionToken !== void 0 && { workflowExecutionToken },
|
|
30
|
+
...revision !== void 0 && { revision },
|
|
30
31
|
workflowType,
|
|
31
32
|
input,
|
|
32
33
|
checkpoint: serializeCheckpoint(checkpoint),
|
|
@@ -40,7 +41,7 @@ export function startWorkflowExecution(internals, workflowId, workflowExecutionT
|
|
|
40
41
|
}
|
|
41
42
|
});
|
|
42
43
|
}
|
|
43
|
-
export function beginWorkflowExecution(internals, workflowId, workflowExecutionToken, workflowType, input, checkpoint, executionDeadline, executionStateOwnerId, _registration, callbacks, onStarted) {
|
|
44
|
+
export function beginWorkflowExecution(internals, workflowId, workflowExecutionToken, workflowType, revision, input, checkpoint, executionDeadline, executionStateOwnerId, _registration, callbacks, onStarted) {
|
|
44
45
|
const nestingDepth = internals.pendingNestingDepth ?? 0;
|
|
45
46
|
internals.pendingNestingDepth = void 0;
|
|
46
47
|
if (internals.inlineStrategy !== null) {
|
|
@@ -48,6 +49,7 @@ export function beginWorkflowExecution(internals, workflowId, workflowExecutionT
|
|
|
48
49
|
workflowId,
|
|
49
50
|
...workflowExecutionToken !== void 0 && { workflowExecutionToken },
|
|
50
51
|
workflowType,
|
|
52
|
+
revision,
|
|
51
53
|
input,
|
|
52
54
|
checkpoint,
|
|
53
55
|
nestingDepth,
|
|
@@ -58,7 +60,7 @@ export function beginWorkflowExecution(internals, workflowId, workflowExecutionT
|
|
|
58
60
|
return;
|
|
59
61
|
}
|
|
60
62
|
callbacks.dispatchEvent(new WorkflowStartedEvent(workflowId, workflowType, input));
|
|
61
|
-
startWorkflowExecution(internals, workflowId, workflowExecutionToken, workflowType, input, checkpoint, nestingDepth, executionDeadline, executionStateOwnerId, callbacks);
|
|
63
|
+
startWorkflowExecution(internals, workflowId, workflowExecutionToken, workflowType, revision, input, checkpoint, nestingDepth, executionDeadline, executionStateOwnerId, callbacks);
|
|
62
64
|
onStarted?.();
|
|
63
65
|
}
|
|
64
66
|
export function assertDeferSupported(internals, options, isDelayedStart) {
|
|
@@ -73,7 +75,7 @@ export async function beginExecutionAwaitingLiveness(internals, params, workflow
|
|
|
73
75
|
if (params.isDelayed)
|
|
74
76
|
return;
|
|
75
77
|
const liveness = params.options?.defer === !1 ? Promise.withResolvers() : void 0;
|
|
76
|
-
beginWorkflowExecution(internals, workflowId, params.state.workflowExecutionToken, params.type, params.input, params.checkpoint, params.state.executionDeadline, params.state.executionStateOwnerId ?? workflowId, params.registration, callbacks, liveness ? () => liveness.resolve() : void 0);
|
|
78
|
+
beginWorkflowExecution(internals, workflowId, params.state.workflowExecutionToken, params.type, params.state.revision, params.input, params.checkpoint, params.state.executionDeadline, params.state.executionStateOwnerId ?? workflowId, params.registration, callbacks, liveness ? () => liveness.resolve() : void 0);
|
|
77
79
|
if (liveness)
|
|
78
80
|
await liveness.promise;
|
|
79
81
|
}
|
|
@@ -2,9 +2,7 @@ import { KEYS } from "../../../storage/interface.js";
|
|
|
2
2
|
import { encode } from "../../codec.js";
|
|
3
3
|
import { WorkflowAlreadyExistsError } from "../errors.js";
|
|
4
4
|
import { buildCreateBatchSignalOperations } from "../signals.js";
|
|
5
|
-
import {
|
|
6
|
-
StartIdempotencyRaceLostError
|
|
7
|
-
} from "./start-commit.js";
|
|
5
|
+
import { StartIdempotencyRaceLostError } from "./start-commit-errors.js";
|
|
8
6
|
import {
|
|
9
7
|
requireWinnerId,
|
|
10
8
|
resolveCallerIdWinnerOrRetry,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
StartWorkflowValidationError
|
|
7
7
|
} from "../../start-workflow-validation.js";
|
|
8
8
|
import { IdempotencyKeyPurgedError, StartOrSignalConflictError } from "../errors.js";
|
|
9
|
-
import { StartIdempotencyRaceLostError } from "./start-commit.js";
|
|
9
|
+
import { StartIdempotencyRaceLostError } from "./start-commit-errors.js";
|
|
10
10
|
import {
|
|
11
11
|
createWithSignalOrFallback,
|
|
12
12
|
idempotentStartOperationsFor
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decide what a lost start compare-and-swap MEANS, once every positively-detectable
|
|
3
|
+
* cause has already been ruled out by the caller — an idempotency precondition, a
|
|
4
|
+
* catalog-entry removal, and a lost workflow claim are each re-checked and raise
|
|
5
|
+
* their own error before this runs.
|
|
6
|
+
*
|
|
7
|
+
* Throws when the loss is terminal; returns normally when the caller should retry
|
|
8
|
+
* workflow-concurrency admission.
|
|
9
|
+
*
|
|
10
|
+
* WFT-152: a caller-supplied id that lost its duplicate-id condition means another
|
|
11
|
+
* engine sharing this store committed a create for the same id after this start's
|
|
12
|
+
* duplicate-id read. It raises the SAME {@link WorkflowAlreadyExistsError} the
|
|
13
|
+
* in-engine `pendingStarts` guard raises for the identical collision, so a
|
|
14
|
+
* cross-engine duplicate id is indistinguishable from an in-engine one at the call
|
|
15
|
+
* site.
|
|
16
|
+
*
|
|
17
|
+
* Attribution is by ELIMINATION, never by re-reading the duplicate-id key. A
|
|
18
|
+
* re-read is unsound: the winning run can complete and be purged (or swept by
|
|
19
|
+
* retention) between the failed compare-and-swap and the check, restoring the
|
|
20
|
+
* workflow record to the very value the condition expected, so the conflict reads
|
|
21
|
+
* back as "no conflict". With no workflow-concurrency conditions in the batch,
|
|
22
|
+
* nothing else is left for the lost outcome to mean.
|
|
23
|
+
*
|
|
24
|
+
* When concurrency conditions ARE present, retry only on positive evidence that the
|
|
25
|
+
* retryable one is what missed — hence `hasWorkflowConcurrencyConflict`, invoked
|
|
26
|
+
* lazily so the extra storage read happens only on that path.
|
|
27
|
+
*
|
|
28
|
+
* That evidence is NOT proof the duplicate id was fine, and deliberately is not
|
|
29
|
+
* treated as such. The concurrency precondition is a monotonic atomic-state VERSION
|
|
30
|
+
* key (`buildWorkflowConcurrencyStartOperations` conditions on `snapshot.version`
|
|
31
|
+
* and writes `version + 1`; releasing the slot increments again rather than
|
|
32
|
+
* restoring), so once a same-id winner acquires and releases, that condition stays
|
|
33
|
+
* mismatched forever and reports a conflict regardless of what else missed. What
|
|
34
|
+
* makes retrying safe here is the caller's own earlier check: it re-reads the
|
|
35
|
+
* duplicate-id key positively and raises `WorkflowAlreadyExistsError` before this
|
|
36
|
+
* runs, so reaching this point means the workflow record currently matches the
|
|
37
|
+
* expectation — the id is free right now, and a retry re-conditions on that same
|
|
38
|
+
* still-matching value.
|
|
39
|
+
*
|
|
40
|
+
* The residual is the pre-compare-and-swap purge ABA (WFT-153): a winner purged
|
|
41
|
+
* between the read and the commit makes an absent record look never-used, which no
|
|
42
|
+
* value-comparing condition can detect. That is tracked separately and is the reason
|
|
43
|
+
* this function does not claim exclusive attribution.
|
|
44
|
+
*
|
|
45
|
+
* With no concurrency evidence at all, fail closed: something missed, nothing
|
|
46
|
+
* retryable explains it, and a spurious `WorkflowAlreadyExistsError` is public,
|
|
47
|
+
* non-destructive, and retryable by the caller.
|
|
48
|
+
*/
|
|
49
|
+
export declare function attributeLostStartPreconditionOrRetry(workflowId: string, hasDuplicateIdCondition: boolean, hasWorkflowConcurrency: boolean, hasWorkflowConcurrencyConflict: () => Promise<boolean>): Promise<void>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { WorkflowAlreadyExistsError } from "../errors.js";
|
|
2
|
+
import { StartIdempotencyRaceLostError } from "./start-commit-errors.js";
|
|
3
|
+
export async function attributeLostStartPreconditionOrRetry(workflowId, hasDuplicateIdCondition, hasWorkflowConcurrency, hasWorkflowConcurrencyConflict) {
|
|
4
|
+
if (!hasDuplicateIdCondition) {
|
|
5
|
+
if (!hasWorkflowConcurrency)
|
|
6
|
+
throw new StartIdempotencyRaceLostError;
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
if (!hasWorkflowConcurrency)
|
|
10
|
+
throw new WorkflowAlreadyExistsError(workflowId);
|
|
11
|
+
if (!await hasWorkflowConcurrencyConflict())
|
|
12
|
+
throw new WorkflowAlreadyExistsError(workflowId);
|
|
13
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the exact revision a fresh `startWorkflow()` call — or a schedule's
|
|
3
|
+
* pin capture (WFT-20, `pinned-schedule-revision.ts`) — is about to commit
|
|
4
|
+
* to. Extracted out of `lifecycle/start.ts` so both that module and the
|
|
5
|
+
* schedule pin-capture path import the SAME logic instead of duplicating the
|
|
6
|
+
* `registeredCatalogRevisions`-fallback + `ensureWorkflowCatalogReady()`
|
|
7
|
+
* recheck rule; `start.ts` imports these back unchanged.
|
|
8
|
+
*
|
|
9
|
+
* @module core/engine/lifecycle/start-revision-resolution
|
|
10
|
+
*/
|
|
11
|
+
import type { EngineInternals } from '../internals.ts';
|
|
12
|
+
/**
|
|
13
|
+
* The exact executable artifact a start is about to run: the resolved
|
|
14
|
+
* dynamic-source candidate revision, or — for an eager registration, which
|
|
15
|
+
* never populates `resolvedRevision` — this process's own
|
|
16
|
+
* `registeredCatalogRevisions` entry for `type` (the revision of the code
|
|
17
|
+
* actually loaded here, NOT `inFlightRevision`, which for an eager type
|
|
18
|
+
* falls back to the catalog's cached ACTIVE pointer and can name a revision
|
|
19
|
+
* this process never loaded under a multi-engine deployment). Synchronous,
|
|
20
|
+
* on purpose: every top-level engine.* method already awaits
|
|
21
|
+
* `ensureWorkflowCatalogReady()` before reaching a call site that needs this,
|
|
22
|
+
* so this map is populated by the time the overwhelmingly common case gets
|
|
23
|
+
* here. `undefined` means "genuinely not cached yet"; the caller falls back
|
|
24
|
+
* to {@link resolveStartRevisionUncached}.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveCachedStartRevision(internals: EngineInternals, type: string, resolvedRevision: string | undefined): string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* The rare fallback {@link resolveCachedStartRevision} defers to: a fired
|
|
29
|
+
* schedule occurrence or a delayed-start timer calls `startWorkflow`
|
|
30
|
+
* directly from background scheduler code, with no top-level
|
|
31
|
+
* `ensureWorkflowCatalogReady()` gate already awaited. Re-checks catalog
|
|
32
|
+
* readiness once, then re-reads the cache.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveStartRevisionUncached(internals: EngineInternals, type: string): Promise<string>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ensureWorkflowCatalogReady } from "../catalog-readiness.js";
|
|
2
|
+
export function resolveCachedStartRevision(internals, type, resolvedRevision) {
|
|
3
|
+
return resolvedRevision ?? internals.registeredCatalogRevisions.get(type);
|
|
4
|
+
}
|
|
5
|
+
export async function resolveStartRevisionUncached(internals, type) {
|
|
6
|
+
await ensureWorkflowCatalogReady(internals.engine);
|
|
7
|
+
const afterReadiness = internals.registeredCatalogRevisions.get(type);
|
|
8
|
+
if (afterReadiness !== void 0)
|
|
9
|
+
return afterReadiness;
|
|
10
|
+
throw Error(`Cannot start workflow "${type}": no catalog revision is registered for this eagerly-registered type, even after re-checking catalog readiness. This should be unreachable.`);
|
|
11
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the initial in-memory {@link WorkflowState} and {@link Checkpoint}
|
|
3
|
+
* for a fresh `start()` — split out of `start.ts`, which has no headroom
|
|
4
|
+
* under the repository's 500-line implementation-file ceiling for this
|
|
5
|
+
* logic inline.
|
|
6
|
+
*
|
|
7
|
+
* @module core/engine/lifecycle/start-state
|
|
8
|
+
*/
|
|
9
|
+
import type { Checkpoint, Duration, StartOptions, TimerEntry, WorkflowState } from '../../types.ts';
|
|
10
|
+
import { type WorkflowVersionTuple } from '../../workflow-version-tuple.ts';
|
|
11
|
+
import type { EngineInternals } from '../internals.ts';
|
|
12
|
+
import { type LifecycleCallbacks } from './shared.ts';
|
|
13
|
+
export declare function parseStartOptionDuration(_internals: EngineInternals, duration: Duration, fieldName: 'options.executionTimeout' | 'options.startAfter', _callbacks: LifecycleCallbacks): number;
|
|
14
|
+
export declare function createInitialWorkflowState(internals: EngineInternals, workflowId: string, type: string, input: unknown, versionTuple: WorkflowVersionTuple, revision: string, options: StartOptions | undefined, tags: string[] | undefined, executionStateOwnerId: string | undefined, parentWorkflowId: string | undefined, parentWorkflowExecutionToken: string | undefined, delayedStartTimer: TimerEntry | undefined, callbacks: LifecycleCallbacks): WorkflowState;
|
|
15
|
+
export declare function createInitialCheckpoint(internals: EngineInternals, workflowId: string, workflowVersion: string, options: StartOptions | undefined, _callbacks: LifecycleCallbacks): Checkpoint;
|
|
16
|
+
export declare function applyRestartLineage(state: WorkflowState, displacedState: WorkflowState | null): void;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { createCheckpoint } from "../../checkpoint.js";
|
|
2
|
+
import { normalizeStorageTimestamp } from "../../scheduler.js";
|
|
3
|
+
import {
|
|
4
|
+
StartWorkflowValidationError,
|
|
5
|
+
parseStartWorkflowDuration
|
|
6
|
+
} from "../../start-workflow-validation.js";
|
|
7
|
+
export function parseStartOptionDuration(_internals, duration, fieldName, _callbacks) {
|
|
8
|
+
return parseStartWorkflowDuration(duration, fieldName);
|
|
9
|
+
}
|
|
10
|
+
function buildInitialIdentitySlice(workflowId, type, input, versionTuple, revision, executionStateOwnerId, parentWorkflowId, parentWorkflowExecutionToken, delayedStartTimer, now, tags) {
|
|
11
|
+
return {
|
|
12
|
+
id: workflowId,
|
|
13
|
+
type,
|
|
14
|
+
status: delayedStartTimer ? "pending" : "running",
|
|
15
|
+
input,
|
|
16
|
+
versionTuple,
|
|
17
|
+
revision,
|
|
18
|
+
workflowExecutionToken: crypto.randomUUID(),
|
|
19
|
+
...executionStateOwnerId !== void 0 && { executionStateOwnerId },
|
|
20
|
+
...parentWorkflowId !== void 0 && { parentWorkflowId },
|
|
21
|
+
...parentWorkflowExecutionToken !== void 0 && { parentWorkflowExecutionToken },
|
|
22
|
+
createdAt: now,
|
|
23
|
+
...!delayedStartTimer && { startedAt: now },
|
|
24
|
+
updatedAt: now,
|
|
25
|
+
...tags !== void 0 && { tags }
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function resolveInitialExecutionDeadline(internals, options, delayedStartTimer, now, callbacks) {
|
|
29
|
+
if (options?.executionTimeout === void 0 || delayedStartTimer)
|
|
30
|
+
return;
|
|
31
|
+
const executionTimeoutMilliseconds = parseStartOptionDuration(internals, options.executionTimeout, "options.executionTimeout", callbacks);
|
|
32
|
+
try {
|
|
33
|
+
return normalizeStorageTimestamp(now + executionTimeoutMilliseconds, "options.executionTimeout");
|
|
34
|
+
} catch {
|
|
35
|
+
throw new StartWorkflowValidationError("options.executionTimeout must resolve to a finite, non-negative deadline");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function createInitialWorkflowState(internals, workflowId, type, input, versionTuple, revision, options, tags, executionStateOwnerId, parentWorkflowId, parentWorkflowExecutionToken, delayedStartTimer, callbacks) {
|
|
39
|
+
const now = internals.options.getNow(), state = buildInitialIdentitySlice(workflowId, type, input, versionTuple, revision, executionStateOwnerId, parentWorkflowId, parentWorkflowExecutionToken, delayedStartTimer, now, tags), executionDeadline = resolveInitialExecutionDeadline(internals, options, delayedStartTimer, now, callbacks);
|
|
40
|
+
if (executionDeadline !== void 0)
|
|
41
|
+
state.executionDeadline = executionDeadline;
|
|
42
|
+
return state;
|
|
43
|
+
}
|
|
44
|
+
export function createInitialCheckpoint(internals, workflowId, workflowVersion, options, _callbacks) {
|
|
45
|
+
const checkpoint = createCheckpoint(workflowId, workflowVersion, internals.options.getNow());
|
|
46
|
+
if (options?.searchAttributes)
|
|
47
|
+
checkpoint.searchAttributes = { ...options.searchAttributes };
|
|
48
|
+
return checkpoint;
|
|
49
|
+
}
|
|
50
|
+
export function applyRestartLineage(state, displacedState) {
|
|
51
|
+
if (displacedState === null)
|
|
52
|
+
return;
|
|
53
|
+
state.restartedFrom = {
|
|
54
|
+
workflowId: displacedState.id,
|
|
55
|
+
...displacedState.workflowExecutionToken !== void 0 && {
|
|
56
|
+
workflowExecutionToken: displacedState.workflowExecutionToken
|
|
57
|
+
},
|
|
58
|
+
replacedAt: state.createdAt
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -1,7 +1,65 @@
|
|
|
1
|
-
import type { BatchOperation } from '../../../storage/interface.ts';
|
|
1
|
+
import type { BatchOperation, ConditionalBatchCondition } from '../../../storage/interface.ts';
|
|
2
2
|
import type { StartWorkflowOptions, WorkflowState } from '../../types.ts';
|
|
3
3
|
import type { EngineInternals } from '../internals.ts';
|
|
4
4
|
import { type LifecycleCallbacks } from './shared.ts';
|
|
5
|
+
/**
|
|
6
|
+
* What the duplicate-id read decided about a caller-supplied workflow id.
|
|
7
|
+
*/
|
|
8
|
+
export type StartDuplicateIdDecision = {
|
|
9
|
+
/** A prior terminal run this start will displace, or `null` for a fresh id. */
|
|
10
|
+
terminalRunToPurge: WorkflowState | null;
|
|
11
|
+
/**
|
|
12
|
+
* Compare-and-swap precondition that makes the duplicate-id check atomic with
|
|
13
|
+
* the create commit (WFT-152).
|
|
14
|
+
*
|
|
15
|
+
* The check below reads `KEYS.workflow(workflowId)` and decides whether the id
|
|
16
|
+
* is free, but that read and the create batch are separated by every build step
|
|
17
|
+
* in between. Within ONE engine `pendingStarts` holds the id across that window;
|
|
18
|
+
* two engines sharing a store share no such memory, so both previously committed
|
|
19
|
+
* blind and the second silently overwrote the first — leaving the loser with a
|
|
20
|
+
* run whose terminal transition happens on the other engine and therefore never
|
|
21
|
+
* settles its `result()` waiter. Conditioning the batch on the exact value seen
|
|
22
|
+
* here collapses that window: the loser's batch does not commit, and
|
|
23
|
+
* `buildAndCommitStartBatch` surfaces {@link WorkflowAlreadyExistsError} — the
|
|
24
|
+
* same error the in-engine `pendingStarts` guard already throws for the same
|
|
25
|
+
* collision.
|
|
26
|
+
*
|
|
27
|
+
* `expectedValue` is the RAW observed bytes, deliberately not a re-encoding of
|
|
28
|
+
* the decoded state: re-encoding is not guaranteed to round-trip byte-identically,
|
|
29
|
+
* and a condition built from one would fail against a record nothing had touched.
|
|
30
|
+
* `null` (id absent) and a prior terminal run's bytes (an
|
|
31
|
+
* `onTerminalConflict: 'start-new'` restart) are both valid expected values, so a
|
|
32
|
+
* restart is equally protected against a concurrent engine displacing the same
|
|
33
|
+
* terminal run.
|
|
34
|
+
*
|
|
35
|
+
* No `conditionalBatch` capability gate is needed. Reaching this code means a
|
|
36
|
+
* workflow is registered, and registration drains through
|
|
37
|
+
* `WorkflowCatalog#activateRegistered`, which already hard-requires that
|
|
38
|
+
* capability at `Engine.create()`. A store that cannot honour this condition
|
|
39
|
+
* cannot host an engine that could start a workflow in the first place.
|
|
40
|
+
*
|
|
41
|
+
* KNOWN LIMITATION (PR #959 review). The condition compares a VALUE, so it cannot
|
|
42
|
+
* distinguish "this id was never used" from "a run existed here and was purged".
|
|
43
|
+
* If a racing winner commits, completes, and is purged or swept by retention
|
|
44
|
+
* before this batch commits, `wf:<id>` is absent again, `expectedValue: null`
|
|
45
|
+
* matches, and both starts execute. Closing that needs durable per-id generation
|
|
46
|
+
* or tombstone state a purge cannot restore — a new persisted mechanism, out of
|
|
47
|
+
* scope here. The window is narrow (a winner's whole lifecycle plus a purge inside
|
|
48
|
+
* one loser's read-to-commit gap) and the outcome is a duplicate run rather than
|
|
49
|
+
* the indefinite hang this fence exists to remove.
|
|
50
|
+
*/
|
|
51
|
+
duplicateIdCondition: ConditionalBatchCondition;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* The decision for a start whose id was GENERATED rather than caller-supplied. A
|
|
55
|
+
* v4 UUID is effectively unique, so the duplicate-id read is skipped entirely and
|
|
56
|
+
* there is no observed value to condition on — the start keeps the unconditioned
|
|
57
|
+
* single-write hot path.
|
|
58
|
+
*/
|
|
59
|
+
export declare const GENERATED_ID_START_DECISION: {
|
|
60
|
+
readonly terminalRunToPurge: null;
|
|
61
|
+
readonly duplicateIdCondition: undefined;
|
|
62
|
+
};
|
|
5
63
|
/**
|
|
6
64
|
* Decide what a caller-supplied workflow id that already has a persisted record
|
|
7
65
|
* means, WITHOUT performing any destructive action. Only invoked when the caller
|
|
@@ -23,9 +81,14 @@ import { type LifecycleCallbacks } from './shared.ts';
|
|
|
23
81
|
* delete the finalizer payload before the resource is torn down, leaking it, so
|
|
24
82
|
* the restart is refused until teardown settles (which clears the marker).
|
|
25
83
|
*
|
|
26
|
-
*
|
|
84
|
+
* This read is only a point-in-time observation: another engine sharing the store
|
|
85
|
+
* can commit a create for the same id in the window between it and the create
|
|
86
|
+
* batch. The returned {@link StartDuplicateIdDecision} therefore carries a
|
|
87
|
+
* `duplicateIdCondition` holding the exact bytes seen here, so that batch can be
|
|
88
|
+
* conditioned on them — turning that window into a lost compare-and-swap rather
|
|
89
|
+
* than a blind overwrite (WFT-152).
|
|
27
90
|
*/
|
|
28
|
-
export declare function resolveTerminalConflictForRestart(internals: EngineInternals, workflowId: string, options: StartWorkflowOptions | undefined): Promise<
|
|
91
|
+
export declare function resolveTerminalConflictForRestart(internals: EngineInternals, workflowId: string, options: StartWorkflowOptions | undefined): Promise<StartDuplicateIdDecision>;
|
|
29
92
|
/**
|
|
30
93
|
* Prepare a prior terminal run for displacement by a `'start-new'` restart WITHOUT
|
|
31
94
|
* committing the destructive delete. Returns the storage delete operations (for the
|
|
@@ -6,10 +6,17 @@ import {
|
|
|
6
6
|
import { WorkflowAlreadyExistsError, WorkflowTeardownPendingError } from "../errors.js";
|
|
7
7
|
import { cleanupWaiters } from "../termination/cleanup.js";
|
|
8
8
|
import { decodeWorkflowState, isTerminalWorkflowStatus } from "../validation.js";
|
|
9
|
+
export const GENERATED_ID_START_DECISION = {
|
|
10
|
+
terminalRunToPurge: null,
|
|
11
|
+
duplicateIdCondition: void 0
|
|
12
|
+
};
|
|
9
13
|
export async function resolveTerminalConflictForRestart(internals, workflowId, options) {
|
|
10
|
-
const existingBytes = await internals.storage.get(
|
|
14
|
+
const key = KEYS.workflow(workflowId), existingBytes = await internals.storage.get(key);
|
|
11
15
|
if (existingBytes === null)
|
|
12
|
-
return
|
|
16
|
+
return {
|
|
17
|
+
terminalRunToPurge: null,
|
|
18
|
+
duplicateIdCondition: { key, expectedValue: null }
|
|
19
|
+
};
|
|
13
20
|
if (options?.onTerminalConflict !== "start-new")
|
|
14
21
|
throw new WorkflowAlreadyExistsError(workflowId);
|
|
15
22
|
const existingState = decodeWorkflowState(existingBytes);
|
|
@@ -17,7 +24,10 @@ export async function resolveTerminalConflictForRestart(internals, workflowId, o
|
|
|
17
24
|
throw new WorkflowAlreadyExistsError(workflowId);
|
|
18
25
|
if (await storageHas(internals.storage, KEYS.teardownOwed(workflowId)))
|
|
19
26
|
throw new WorkflowTeardownPendingError(workflowId);
|
|
20
|
-
return
|
|
27
|
+
return {
|
|
28
|
+
terminalRunToPurge: existingState,
|
|
29
|
+
duplicateIdCondition: { key, expectedValue: existingBytes }
|
|
30
|
+
};
|
|
21
31
|
}
|
|
22
32
|
export async function prepareTerminalRunPurge(internals, state, callbacks) {
|
|
23
33
|
const cleanupWaitersForStart = (id) => cleanupWaiters(internals, id, {
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
import type { BatchOperation } from '../../../storage/interface.ts';
|
|
2
|
-
import type {
|
|
3
|
-
import { type WorkflowVersionTuple } from '../../workflow-version-tuple.ts';
|
|
2
|
+
import type { StartOptions, StartWorkflowOptions } from '../../types.ts';
|
|
4
3
|
import { type WorkflowHandle } from '../handles.ts';
|
|
5
4
|
import type { EngineInternals } from '../internals.ts';
|
|
6
5
|
import { type LifecycleCallbacks } from './shared.ts';
|
|
7
6
|
import { type BuildIdempotentStartOperations } from './start-commit.ts';
|
|
8
7
|
export declare function start(internals: EngineInternals, type: string, input: unknown, options: StartWorkflowOptions | undefined, callbacks: LifecycleCallbacks): Promise<WorkflowHandle>;
|
|
9
|
-
export declare function startWorkflow(internals: EngineInternals, type: string, input: unknown, options: StartWorkflowOptions | undefined, additionalStartOperations: BatchOperation[] | undefined, callbacks: LifecycleCallbacks, buildIdempotentStartOperations?: BuildIdempotentStartOperations
|
|
8
|
+
export declare function startWorkflow(internals: EngineInternals, type: string, input: unknown, options: StartWorkflowOptions | undefined, additionalStartOperations: BatchOperation[] | undefined, callbacks: LifecycleCallbacks, buildIdempotentStartOperations?: BuildIdempotentStartOperations,
|
|
9
|
+
/**
|
|
10
|
+
* A pinned schedule's forward-looking revision commitment (WFT-20),
|
|
11
|
+
* threaded from `ScheduleCallbacks.startWorkflow`'s own `revisionOverride`
|
|
12
|
+
* parameter. When supplied, this call resolves and reserves EXACTLY this
|
|
13
|
+
* revision (via {@link resolveAndReservePinnedExecutableRegistration},
|
|
14
|
+
* which enforces an exact-match check for an eager type rather than
|
|
15
|
+
* silently falling back to whatever is active) instead of the ordinary
|
|
16
|
+
* "whatever `resolveExecutableRegistration` currently resolves"
|
|
17
|
+
* admission path, and the persisted `WorkflowState.revision` is this
|
|
18
|
+
* value verbatim — never re-derived from `registeredCatalogRevisions`.
|
|
19
|
+
* `undefined` (the default, every non-schedule start) is byte-for-byte
|
|
20
|
+
* the pre-WFT-20 admission path.
|
|
21
|
+
*/
|
|
22
|
+
revisionOverride?: string): Promise<WorkflowHandle>;
|
|
10
23
|
export declare function resolveScheduledStartAt(internals: EngineInternals, options: StartOptions | undefined, submissionTime: number, callbacks: LifecycleCallbacks): number | undefined;
|
|
11
|
-
export declare function parseStartOptionDuration(_internals: EngineInternals, duration: Duration, fieldName: 'options.executionTimeout' | 'options.startAfter', _callbacks: LifecycleCallbacks): number;
|
|
12
|
-
export declare function createInitialWorkflowState(internals: EngineInternals, workflowId: string, type: string, input: unknown, versionTuple: WorkflowVersionTuple, options: StartOptions | undefined, tags: string[] | undefined, executionStateOwnerId: string | undefined, parentWorkflowId: string | undefined, parentWorkflowExecutionToken: string | undefined, delayedStartTimer: TimerEntry | undefined, callbacks: LifecycleCallbacks): WorkflowState;
|
|
13
|
-
export declare function createInitialCheckpoint(internals: EngineInternals, workflowId: string, workflowVersion: string, options: StartOptions | undefined, _callbacks: LifecycleCallbacks): Checkpoint;
|