@lostgradient/weft 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -24
- package/dist/cli/conformance.js +7 -1
- package/dist/cli/generated/operation-client.generated.d.ts +28 -13
- package/dist/cli/generated/operation-client.generated.js +1 -0
- package/dist/cli/help-text.d.ts +1 -1
- package/dist/cli/help-text.js +1 -0
- package/dist/cli/operation-catalog-snapshot.d.ts +8 -0
- package/dist/cli/operation-catalog-snapshot.js +11 -0
- package/dist/cli/parse-schedule-arguments.js +3 -1
- package/dist/cli/schedule.js +2 -1
- package/dist/cli/types.d.ts +1 -0
- package/dist/cli-main.js +79 -78
- package/dist/client/handle-delegation.d.ts +3 -2
- package/dist/client/handle-delegation.js +3 -1
- package/dist/client/http-client-requests.d.ts +2 -1
- package/dist/client/http-client-requests.js +7 -0
- package/dist/client/http-client.d.ts +9 -6
- package/dist/client/http-client.js +10 -1
- package/dist/client/http-request.d.ts +11 -1
- package/dist/client/http-request.js +17 -7
- package/dist/client/index.d.ts +1 -1
- package/dist/client/interface.d.ts +46 -5
- package/dist/client/local.d.ts +4 -1
- package/dist/client/local.js +12 -4
- package/dist/client/start-body.d.ts +4 -3
- package/dist/core/byte-arrays.d.ts +5 -0
- package/dist/core/byte-arrays.js +5 -0
- package/dist/core/checkpoint/lifecycle.d.ts +3 -0
- package/dist/core/checkpoint/lifecycle.js +9 -4
- package/dist/core/checkpoint/serialization.js +12 -5
- package/dist/core/codec/extension-codec.d.ts +16 -0
- package/dist/core/codec/extension-codec.js +100 -6
- package/dist/core/context/activity-retry-state.d.ts +38 -0
- package/dist/core/context/activity-retry-state.js +89 -0
- package/dist/core/context/activity-schedule-to-close.d.ts +159 -0
- package/dist/core/context/activity-schedule-to-close.js +69 -0
- package/dist/core/context/child-workflow-pipe.d.ts +5 -5
- package/dist/core/context/child-workflow-pipe.js +15 -2
- package/dist/core/context/context-presence.d.ts +19 -0
- package/dist/core/context/context-presence.js +13 -0
- package/dist/core/context/durable-operations.d.ts +25 -0
- package/dist/core/context/durable-operations.js +64 -0
- package/dist/core/context/index.d.ts +9 -5
- package/dist/core/context/index.js +17 -12
- package/dist/core/context/internals.d.ts +20 -0
- package/dist/core/context/internals.js +3 -1
- package/dist/core/context/operation-request.d.ts +35 -0
- package/dist/core/context/parallel-cache-entry.d.ts +14 -5
- package/dist/core/context/run-operation.d.ts +6 -1
- package/dist/core/context/run-operation.js +41 -87
- package/dist/core/context/session-state.js +7 -8
- package/dist/core/context/speculative-child.js +2 -0
- package/dist/core/context/types.d.ts +15 -0
- package/dist/core/context/version-patching.d.ts +8 -0
- package/dist/core/context/version-patching.js +40 -0
- package/dist/core/context/workflow-logger.d.ts +98 -0
- package/dist/core/context/workflow-logger.js +68 -0
- package/dist/core/engine/activity-heartbeat-tracking.d.ts +64 -0
- package/dist/core/engine/activity-heartbeat-tracking.js +42 -0
- package/dist/core/engine/activity-per-attempt-timeout.d.ts +41 -0
- package/dist/core/engine/activity-per-attempt-timeout.js +36 -0
- package/dist/core/engine/activity-reconciliation.d.ts +1 -0
- package/dist/core/engine/activity-reconciliation.js +12 -1
- package/dist/core/engine/activity-resolution.d.ts +23 -0
- package/dist/core/engine/activity-resolution.js +37 -0
- package/dist/core/engine/aggregate.js +66 -30
- package/dist/core/engine/async-activity-completion.d.ts +23 -6
- package/dist/core/engine/async-activity-completion.js +53 -12
- package/dist/core/engine/attributes-tags.js +9 -4
- package/dist/core/engine/bulk-operations-purge.d.ts +26 -0
- package/dist/core/engine/bulk-operations-purge.js +38 -3
- package/dist/core/engine/bulk-operations-shared.d.ts +2 -0
- package/dist/core/engine/bulk-operations-shared.js +8 -2
- package/dist/core/engine/bulk-operations.d.ts +4 -1
- package/dist/core/engine/bulk-operations.js +235 -24
- package/dist/core/engine/callback-creators-bundles.d.ts +9 -0
- package/dist/core/engine/callback-creators-bundles.js +30 -2
- package/dist/core/engine/callback-creators-core.js +6 -4
- package/dist/core/engine/callback-creators-router.js +10 -0
- package/dist/core/engine/callback-creators-schedule.d.ts +2 -2
- package/dist/core/engine/callback-creators-schedule.js +12 -8
- package/dist/core/engine/candidate-read-batching.d.ts +6 -0
- package/dist/core/engine/candidate-read-batching.js +1 -0
- package/dist/core/engine/checkpoint-io.js +61 -25
- package/dist/core/engine/checkpoint-reads.js +3 -2
- package/dist/core/engine/checkpoint-replay.d.ts +20 -0
- package/dist/core/engine/checkpoint-replay.js +242 -0
- package/dist/core/engine/checkpoint-side-effects.d.ts +23 -0
- package/dist/core/engine/checkpoint-side-effects.js +35 -0
- package/dist/core/engine/child-workflow-cancellation.d.ts +10 -0
- package/dist/core/engine/child-workflow-cancellation.js +40 -0
- package/dist/core/engine/child-workflow.js +27 -4
- package/dist/core/engine/condition-waiters.d.ts +14 -0
- package/dist/core/engine/condition-waiters.js +5 -0
- package/dist/core/engine/construction.d.ts +3 -0
- package/dist/core/engine/construction.js +21 -8
- package/dist/core/engine/coordination-branch-executors.d.ts +70 -0
- package/dist/core/engine/coordination-branch-executors.js +107 -0
- package/dist/core/engine/deferred-consume-envelope.d.ts +59 -0
- package/dist/core/engine/deferred-consume-envelope.js +14 -0
- package/dist/core/engine/disposal.js +13 -0
- package/dist/core/engine/engine-create-types.d.ts +14 -4
- package/dist/core/engine/engine-internal-types.d.ts +26 -3
- package/dist/core/engine/errors.d.ts +47 -0
- package/dist/core/engine/errors.js +20 -0
- package/dist/core/engine/event-log-compaction.d.ts +8 -0
- package/dist/core/engine/event-log-compaction.js +20 -2
- package/dist/core/engine/fenced-write.d.ts +65 -0
- package/dist/core/engine/fenced-write.js +71 -0
- package/dist/core/engine/finalizer-state.d.ts +28 -0
- package/dist/core/engine/finalizer-state.js +16 -0
- package/dist/core/engine/handles.d.ts +8 -0
- package/dist/core/engine/index.d.ts +34 -16
- package/dist/core/engine/index.js +133 -8
- package/dist/core/engine/inline-parking.js +3 -2
- package/dist/core/engine/internals.d.ts +107 -3
- package/dist/core/engine/lease-codec.d.ts +35 -0
- package/dist/core/engine/lease-codec.js +32 -0
- package/dist/core/engine/lease-deposition.d.ts +66 -0
- package/dist/core/engine/lease-deposition.js +10 -0
- package/dist/core/engine/lease-errors.d.ts +99 -0
- package/dist/core/engine/lease-errors.js +30 -0
- package/dist/core/engine/lease-manager.d.ts +114 -0
- package/dist/core/engine/lease-manager.js +135 -0
- package/dist/core/engine/lifecycle/persist.js +10 -32
- package/dist/core/engine/lifecycle/recovered-services.d.ts +8 -6
- package/dist/core/engine/lifecycle/recovered-services.js +18 -3
- package/dist/core/engine/lifecycle/resume.js +16 -9
- package/dist/core/engine/lifecycle/shared.d.ts +8 -1
- package/dist/core/engine/lifecycle/start-batch.d.ts +1 -1
- package/dist/core/engine/lifecycle/start-batch.js +2 -1
- package/dist/core/engine/lifecycle/start-commit.d.ts +9 -0
- package/dist/core/engine/lifecycle/start-commit.js +50 -8
- package/dist/core/engine/lifecycle/start-or-signal-create.d.ts +58 -0
- package/dist/core/engine/lifecycle/start-or-signal-create.js +95 -0
- package/dist/core/engine/lifecycle/start-or-signal.d.ts +3 -2
- package/dist/core/engine/lifecycle/start-or-signal.js +11 -81
- package/dist/core/engine/lifecycle/start-terminal-conflict-purge.d.ts +39 -0
- package/dist/core/engine/lifecycle/start-terminal-conflict-purge.js +28 -0
- package/dist/core/engine/lifecycle/start.d.ts +4 -4
- package/dist/core/engine/lifecycle/start.js +17 -9
- package/dist/core/engine/lifecycle/transition.js +17 -5
- package/dist/core/engine/lifecycle.d.ts +1 -1
- package/dist/core/engine/list-candidate-resolution.js +2 -2
- package/dist/core/engine/listing.js +9 -5
- package/dist/core/engine/operations-activity.d.ts +4 -11
- package/dist/core/engine/operations-activity.js +22 -49
- package/dist/core/engine/operations-coordination.d.ts +10 -0
- package/dist/core/engine/operations-coordination.js +43 -8
- package/dist/core/engine/operations-router.d.ts +6 -0
- package/dist/core/engine/operations-router.js +2 -0
- package/dist/core/engine/operations-speculate.js +6 -2
- package/dist/core/engine/operations-stream.js +12 -2
- package/dist/core/engine/operations-time.d.ts +4 -2
- package/dist/core/engine/operations-time.js +14 -2
- package/dist/core/engine/operations-wait-condition.d.ts +23 -0
- package/dist/core/engine/operations-wait-condition.js +62 -0
- package/dist/core/engine/ownership-options.d.ts +23 -0
- package/dist/core/engine/ownership-options.js +26 -0
- package/dist/core/engine/payload-size-policy.d.ts +3 -0
- package/dist/core/engine/payload-size-policy.js +4 -0
- package/dist/core/engine/pending-updates.d.ts +17 -0
- package/dist/core/engine/pending-updates.js +33 -11
- package/dist/core/engine/queries.js +1 -1
- package/dist/core/engine/registration.d.ts +1 -0
- package/dist/core/engine/registration.js +18 -3
- package/dist/core/engine/schedule-jitter.d.ts +5 -0
- package/dist/core/engine/schedule-jitter.js +10 -0
- package/dist/core/engine/schedule-run.d.ts +17 -0
- package/dist/core/engine/schedule-run.js +38 -0
- package/dist/core/engine/schedule-timer.js +81 -17
- package/dist/core/engine/schedules.d.ts +7 -4
- package/dist/core/engine/schedules.js +37 -16
- package/dist/core/engine/search-attribute-records.d.ts +4 -0
- package/dist/core/engine/search-attribute-records.js +17 -0
- package/dist/core/engine/signals.d.ts +18 -2
- package/dist/core/engine/signals.js +42 -21
- package/dist/core/engine/state-utilities.d.ts +49 -2
- package/dist/core/engine/state-utilities.js +114 -3
- package/dist/core/engine/storage-io.d.ts +16 -2
- package/dist/core/engine/storage-io.js +26 -5
- package/dist/core/engine/stream-chunk-loading.d.ts +2 -0
- package/dist/core/engine/stream-chunk-loading.js +24 -1
- package/dist/core/engine/sub-operation.js +22 -3
- package/dist/core/engine/termination/cleanup.d.ts +3 -1
- package/dist/core/engine/termination/cleanup.js +25 -1
- package/dist/core/engine/termination/complete.js +44 -6
- package/dist/core/engine/termination/finalizer-activity.d.ts +53 -0
- package/dist/core/engine/termination/finalizer-activity.js +30 -0
- package/dist/core/engine/termination/finalizer-claim.d.ts +110 -0
- package/dist/core/engine/termination/finalizer-claim.js +80 -0
- package/dist/core/engine/termination/finalizer.d.ts +48 -0
- package/dist/core/engine/termination/finalizer.js +159 -0
- package/dist/core/engine/termination/suspend.d.ts +3 -1
- package/dist/core/engine/termination.d.ts +1 -0
- package/dist/core/engine/termination.js +4 -0
- package/dist/core/engine/updates.js +5 -25
- package/dist/core/engine/validation/schedule.d.ts +2 -1
- package/dist/core/engine/validation/schedule.js +51 -2
- package/dist/core/engine/validation.js +32 -5
- package/dist/core/engine/waiting-update-response.d.ts +8 -0
- package/dist/core/engine/waiting-update-response.js +22 -0
- package/dist/core/engine/workflow-concurrency.d.ts +10 -0
- package/dist/core/engine/workflow-concurrency.js +136 -0
- package/dist/core/engine/workflow-feed.d.ts +4 -5
- package/dist/core/engine/workflow-feed.js +4 -1
- package/dist/core/engine/workflow-indexes.d.ts +9 -0
- package/dist/core/engine/workflow-indexes.js +10 -0
- package/dist/core/engine/workflow-state-stream.js +33 -13
- package/dist/core/engine/workflow-visibility-queries.d.ts +2 -2
- package/dist/core/engine-helpers.js +1 -0
- package/dist/core/events/activity-events.d.ts +43 -12
- package/dist/core/events/activity-events.js +28 -0
- package/dist/core/events/attribute-events.d.ts +2 -3
- package/dist/core/events/event-map.d.ts +18 -12
- package/dist/core/events/index.d.ts +1 -0
- package/dist/core/events/index.js +1 -0
- package/dist/core/events/schedule-events.d.ts +71 -0
- package/dist/core/events/schedule-events.js +29 -0
- package/dist/core/events/signal-events.d.ts +4 -6
- package/dist/core/events/system-events.d.ts +29 -19
- package/dist/core/events/system-events.js +26 -0
- package/dist/core/events/update-events.d.ts +6 -8
- package/dist/core/events/workflow-events.d.ts +88 -23
- package/dist/core/events/workflow-events.js +26 -0
- package/dist/core/failure-categories.js +5 -17
- package/dist/core/fault-code.d.ts +9 -3
- package/dist/core/fault-code.js +1 -0
- package/dist/core/inline-execution-strategy.context-options.d.ts +69 -0
- package/dist/core/inline-execution-strategy.context-options.js +56 -0
- package/dist/core/inline-execution-strategy.d.ts +31 -36
- package/dist/core/inline-execution-strategy.js +16 -27
- package/dist/core/list-filter-validation.d.ts +1 -1
- package/dist/core/payload-size.d.ts +1 -1
- package/dist/core/persisted-data-incompatible-error.d.ts +9 -7
- package/dist/core/review/events.d.ts +6 -8
- package/dist/core/scheduler/scheduler-class.d.ts +10 -1
- package/dist/core/scheduler/scheduler-class.js +38 -11
- package/dist/core/scheduler/timer-batch.js +9 -1
- package/dist/core/scheduler/timer-sources.d.ts +1 -0
- package/dist/core/scheduler/timer-sources.js +11 -5
- package/dist/core/session-state.d.ts +0 -1
- package/dist/core/session-state.js +2 -2
- package/dist/core/start-workflow-validation.d.ts +34 -0
- package/dist/core/start-workflow-validation.js +19 -0
- package/dist/core/types/activity-verification.d.ts +67 -0
- package/dist/core/types/activity-verification.js +0 -0
- package/dist/core/types/activity.d.ts +80 -61
- package/dist/core/types/bulk.d.ts +50 -9
- package/dist/core/types/checkpoint.d.ts +44 -8
- package/dist/core/types/list-options.d.ts +146 -0
- package/dist/core/types/list-options.js +0 -0
- package/dist/core/types/options.d.ts +121 -165
- package/dist/core/types/schedules.d.ts +39 -3
- package/dist/core/types/services-resolution.d.ts +2 -2
- package/dist/core/types/workflow-builder-runtime.d.ts +12 -3
- package/dist/core/types/workflow-builder-runtime.js +11 -2
- package/dist/core/types/workflow-builder.d.ts +42 -11
- package/dist/core/types/workflow-concurrency.d.ts +39 -0
- package/dist/core/types/workflow-concurrency.js +0 -0
- package/dist/core/types/workflow-context.d.ts +157 -14
- package/dist/core/types/workflow-definition.d.ts +53 -10
- package/dist/core/types/workflow-function.d.ts +84 -9
- package/dist/core/types/workflow-log.d.ts +98 -0
- package/dist/core/types/workflow-log.js +0 -0
- package/dist/core/types/workflow-registries.d.ts +14 -3
- package/dist/core/types/workflow-registry.d.ts +3 -0
- package/dist/core/types.d.ts +4 -0
- package/dist/core/types.js +4 -0
- package/dist/core/versioning.d.ts +9 -39
- package/dist/core/versioning.js +2 -12
- package/dist/core/weft-error.d.ts +44 -1
- package/dist/core/weft-error.js +16 -0
- package/dist/core/worker-execution-strategy-options.d.ts +26 -0
- package/dist/core/worker-execution-strategy.js +40 -36
- package/dist/core/worker-inbound-message.d.ts +38 -0
- package/dist/core/worker-inbound-message.js +37 -0
- package/dist/core/worker-log-abuse-counter.d.ts +175 -0
- package/dist/core/worker-log-abuse-counter.js +107 -0
- package/dist/core/worker-message-helpers.d.ts +47 -0
- package/dist/core/worker-message-helpers.js +26 -0
- package/dist/core/worker-protocol-guard.js +1 -1
- package/dist/core/worker-protocol-log.d.ts +56 -0
- package/dist/core/worker-protocol-log.js +18 -0
- package/dist/core/worker-protocol.d.ts +23 -0
- package/dist/core/worker-protocol.js +12 -2
- package/dist/core/workflow-version-tuple.d.ts +3 -9
- package/dist/core/workflow-version-tuple.js +2 -3
- package/dist/diagnostics/format.js +1 -6
- package/dist/diagnostics/types.d.ts +4 -5
- package/dist/diagnostics/validate.d.ts +0 -1
- package/dist/diagnostics/version-check.d.ts +1 -1
- package/dist/diagnostics/version-check.js +4 -9
- package/dist/index.d.ts +12 -10
- package/dist/index.js +29 -3
- package/dist/json-schema.js +3 -3
- package/dist/mcp/cli.js +27 -27
- package/dist/mcp/dispatcher.js +1 -1
- package/dist/mcp/http.js +16 -6
- package/dist/mcp/protocol.d.ts +2 -0
- package/dist/mcp/protocol.js +1 -1
- package/dist/mcp/session.d.ts +16 -0
- package/dist/mcp/session.js +59 -6
- package/dist/mcp/tools.js +73 -6
- package/dist/observability/index.js +2 -2
- package/dist/server/asyncapi-channels.js +5 -2
- package/dist/server/attribute-filters.d.ts +8 -3
- package/dist/server/attribute-filters.js +6 -1
- package/dist/server/authentication/api-key.d.ts +2 -1
- package/dist/server/authentication/api-key.js +5 -2
- package/dist/server/authentication/constant-time-api-key.d.ts +15 -0
- package/dist/server/authentication/constant-time-api-key.js +24 -0
- package/dist/server/authentication/index.d.ts +1 -1
- package/dist/server/authentication/index.js +7 -4
- package/dist/server/authentication/rotating-api-key-store.d.ts +5 -3
- package/dist/server/authentication/rotating-api-key-store.js +19 -11
- package/dist/server/engine-event-feed-backend.d.ts +4 -5
- package/dist/server/fault-to-http.js +8 -1
- package/dist/server/fault-to-json-rpc.js +1 -0
- package/dist/server/fleet-event-feed.d.ts +31 -0
- package/dist/server/fleet-event-feed.js +157 -0
- package/dist/server/handler/index.js +1 -1
- package/dist/server/handler/response-helpers.js +2 -1
- package/dist/server/handler/route-dispatch.d.ts +6 -3
- package/dist/server/handler/route-dispatch.js +2 -2
- package/dist/server/handler.js +23 -23
- package/dist/server/index.d.ts +32 -2
- package/dist/server/index.js +26 -26
- package/dist/server/json-rpc-transport-helpers.d.ts +1 -0
- package/dist/server/json-rpc-transport-helpers.js +2 -1
- package/dist/server/json-rpc-websocket-runtime.d.ts +11 -2
- package/dist/server/json-rpc-websocket-runtime.js +1 -0
- package/dist/server/json-rpc-websocket-subscriptions.d.ts +10 -0
- package/dist/server/json-rpc-websocket-subscriptions.js +29 -0
- package/dist/server/json-rpc-websocket-types.d.ts +22 -0
- package/dist/server/json-rpc-websocket-types.js +0 -0
- package/dist/server/json-rpc-websocket-validation.js +20 -4
- package/dist/server/json-rpc-websocket.d.ts +2 -43
- package/dist/server/json-rpc-websocket.js +57 -74
- package/dist/server/openapi.d.ts +2 -1
- package/dist/server/openapi.js +1 -3
- package/dist/server/openrpc-document-schema.d.ts +78 -0
- package/dist/server/openrpc-document-schema.js +27 -1
- package/dist/server/openrpc-errors.d.ts +3 -0
- package/dist/server/openrpc-errors.js +2 -1
- package/dist/server/openrpc.js +32 -0
- package/dist/server/operation-catalog/index.d.ts +1 -1
- package/dist/server/operation-catalog/pipeline-helpers.js +1 -0
- package/dist/server/operation-catalog/pipeline-stages.js +26 -12
- package/dist/server/operation-catalog/pipeline.js +8 -1
- package/dist/server/operation-catalog/registry.js +13 -0
- package/dist/server/operation-catalog/types.d.ts +20 -9
- package/dist/server/operation-fault.d.ts +10 -0
- package/dist/server/operation-fault.js +2 -0
- package/dist/server/operation-registry.d.ts +12 -11
- package/dist/server/operation-registry.js +12 -3
- package/dist/server/operations/aggregate-workflows.d.ts +15 -15
- package/dist/server/operations/async-activity.d.ts +2 -2
- package/dist/server/operations/async-activity.js +12 -7
- package/dist/server/operations/bulk-cancel-workflows.d.ts +1 -1
- package/dist/server/operations/bulk-cancel-workflows.js +2 -2
- package/dist/server/operations/bulk-delete-workflows.d.ts +1 -1
- package/dist/server/operations/bulk-delete-workflows.js +2 -2
- package/dist/server/operations/bulk-filter-helpers.d.ts +8 -6
- package/dist/server/operations/bulk-filter-helpers.js +54 -24
- package/dist/server/operations/bulk-mutate-workflow-tags.d.ts +11 -9
- package/dist/server/operations/bulk-mutate-workflow-tags.js +2 -2
- package/dist/server/operations/bulk-operation-helpers.d.ts +2 -2
- package/dist/server/operations/bulk-retry-failed-workflows.d.ts +7 -0
- package/dist/server/operations/bulk-retry-failed-workflows.js +64 -0
- package/dist/server/operations/bulk-signal-workflows.d.ts +11 -9
- package/dist/server/operations/bulk-signal-workflows.js +2 -2
- package/dist/server/operations/cancel-schedule.d.ts +1 -1
- package/dist/server/operations/create-schedule.d.ts +3 -1
- package/dist/server/operations/create-schedule.js +29 -11
- package/dist/server/operations/fleet-events-subscription.d.ts +54 -0
- package/dist/server/operations/fleet-events-subscription.js +99 -0
- package/dist/server/operations/fork-workflow.d.ts +1 -1
- package/dist/server/operations/fork-workflow.js +3 -2
- package/dist/server/operations/get-checkpoint-at.d.ts +1 -1
- package/dist/server/operations/get-registry.d.ts +1 -1
- package/dist/server/operations/get-retention-overview.d.ts +1 -1
- package/dist/server/operations/get-review.d.ts +1 -1
- package/dist/server/operations/get-schedule.d.ts +1 -1
- package/dist/server/operations/get-stream-chunks.d.ts +1 -1
- package/dist/server/operations/get-system-metrics.d.ts +2 -2
- package/dist/server/operations/get-task-diagnostics.d.ts +45 -6
- package/dist/server/operations/get-task-diagnostics.js +101 -7
- package/dist/server/operations/get-update-result.d.ts +1 -1
- package/dist/server/operations/get-workflow-attributes.d.ts +1 -1
- package/dist/server/operations/get-workflow-events.d.ts +1 -1
- package/dist/server/operations/get-workflow-result.d.ts +1 -1
- package/dist/server/operations/get-workflow-timeline.d.ts +1 -1
- package/dist/server/operations/get-workflow.d.ts +1 -1
- package/dist/server/operations/list-checkpoints.d.ts +1 -1
- package/dist/server/operations/list-reviews.d.ts +1 -1
- package/dist/server/operations/list-schedules.d.ts +1 -1
- package/dist/server/operations/list-task-queues.d.ts +2 -2
- package/dist/server/operations/list-workers.d.ts +2 -2
- package/dist/server/operations/list-workflows.d.ts +1 -1
- package/dist/server/operations/operation-helpers.d.ts +16 -1
- package/dist/server/operations/operation-helpers.js +15 -3
- package/dist/server/operations/pause-schedule.d.ts +1 -1
- package/dist/server/operations/purge-workflows.d.ts +5 -5
- package/dist/server/operations/purge-workflows.js +2 -2
- package/dist/server/operations/query-workflow.d.ts +1 -1
- package/dist/server/operations/query-workflow.js +3 -2
- package/dist/server/operations/recover-all.d.ts +1 -1
- package/dist/server/operations/replay-workflow.d.ts +1 -1
- package/dist/server/operations/resume-schedule.d.ts +1 -1
- package/dist/server/operations/set-workflow-attributes.d.ts +1 -1
- package/dist/server/operations/set-workflow-attributes.js +6 -3
- package/dist/server/operations/signal-workflow.js +8 -3
- package/dist/server/operations/single-workflow-tag-mutation.js +13 -3
- package/dist/server/operations/start-or-signal-workflow.d.ts +6 -1
- package/dist/server/operations/start-or-signal-workflow.js +14 -24
- package/dist/server/operations/start-workflow-options.d.ts +6 -0
- package/dist/server/operations/start-workflow-rest-input.d.ts +14 -0
- package/dist/server/operations/start-workflow-rest-input.js +31 -0
- package/dist/server/operations/start-workflow.d.ts +1 -1
- package/dist/server/operations/start-workflow.js +8 -23
- package/dist/server/operations/storage.d.ts +6 -6
- package/dist/server/operations/storage.js +24 -14
- package/dist/server/operations/stream-workflow-sse.d.ts +1 -1
- package/dist/server/operations/submit-review-decision.d.ts +1 -1
- package/dist/server/operations/submit-review-decision.js +6 -3
- package/dist/server/operations/update-schedule.d.ts +1 -1
- package/dist/server/operations/update-schedule.js +7 -4
- package/dist/server/operations/update-workflow.d.ts +1 -1
- package/dist/server/operations/update-workflow.js +7 -3
- package/dist/server/operations/worker-drain.d.ts +8 -8
- package/dist/server/operations/worker-drain.js +11 -8
- package/dist/server/operations/workflow-events-subscription.d.ts +7 -8
- package/dist/server/operations/workflow-events-subscription.js +43 -8
- package/dist/server/rest-binding.d.ts +3 -1
- package/dist/server/rest-bindings.js +12 -0
- package/dist/server/rest-body.d.ts +10 -0
- package/dist/server/rest-body.js +80 -0
- package/dist/server/runtime/authentication-bridge.d.ts +1 -0
- package/dist/server/runtime/authentication-bridge.js +33 -6
- package/dist/server/runtime/client-visible-events.d.ts +3 -0
- package/dist/server/runtime/client-visible-events.js +65 -0
- package/dist/server/runtime/context.d.ts +7 -0
- package/dist/server/runtime/event-broadcasting.d.ts +6 -0
- package/dist/server/runtime/event-broadcasting.js +72 -38
- package/dist/server/runtime/retry.d.ts +1 -0
- package/dist/server/runtime/retry.js +14 -0
- package/dist/server/runtime/shutdown.d.ts +7 -6
- package/dist/server/runtime/shutdown.js +6 -1
- package/dist/server/runtime/task-polling.js +61 -20
- package/dist/server/runtime/task-reconciliation.js +9 -1
- package/dist/server/runtime/task-result-resolution.d.ts +15 -0
- package/dist/server/runtime/task-result-resolution.js +81 -0
- package/dist/server/runtime/websocket-stream.d.ts +20 -1
- package/dist/server/runtime/websocket-stream.js +173 -7
- package/dist/server/runtime/websocket-upgrade.js +22 -3
- package/dist/server/runtime/websocket-worker.d.ts +1 -1
- package/dist/server/runtime/websocket-worker.js +38 -30
- package/dist/server/serve-internals.d.ts +3 -1
- package/dist/server/serve-internals.js +18 -2
- package/dist/server/task-state.d.ts +30 -0
- package/dist/server/task-state.js +28 -0
- package/dist/server/workflow-event-feed.d.ts +36 -115
- package/dist/server/workflow-event-feed.js +92 -27
- package/dist/service-worker/index.js +30 -30
- package/dist/storage/auto.js +1 -1
- package/dist/storage/bun-sql.js +24 -0
- package/dist/storage/compressed-storage.js +1 -1
- package/dist/storage/default-scope.d.ts +7 -4
- package/dist/storage/http.js +2 -2
- package/dist/storage/index.d.ts +76 -4
- package/dist/storage/indexeddb.js +1 -1
- package/dist/storage/interface.d.ts +163 -0
- package/dist/storage/interface.js +1 -1
- package/dist/storage/key-prefixes.d.ts +1 -1
- package/dist/storage/key-prefixes.js +10 -0
- package/dist/storage/lmdb.d.ts +4 -3
- package/dist/storage/lmdb.js +1 -1
- package/dist/storage/memory.js +1 -1
- package/dist/storage/neon-batch.d.ts +30 -0
- package/dist/storage/neon-batch.js +30 -0
- package/dist/storage/neon-value-mapping.d.ts +30 -3
- package/dist/storage/neon-value-mapping.js +12 -0
- package/dist/storage/neon.d.ts +16 -0
- package/dist/storage/neon.js +9 -4
- package/dist/storage/node-sqlite.js +23 -0
- package/dist/storage/postgres-key-value-queries.d.ts +69 -44
- package/dist/storage/postgres-key-value-queries.js +62 -24
- package/dist/storage/resolve.js +1 -1
- package/dist/storage/scoped-storage.js +1 -1
- package/dist/storage/testing.d.ts +168 -0
- package/dist/storage/testing.js +2 -0
- package/dist/storage/turso.d.ts +8 -0
- package/dist/storage/turso.js +2 -2
- package/dist/storage/typed-storage.js +1 -1
- package/dist/storage/web-extension.js +1 -1
- package/dist/testing/index.js +28 -28
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker/protocol.js +1 -1
- package/dist/workers/worker-replay-state.d.ts +32 -0
- package/dist/workers/worker-replay-state.js +35 -0
- package/dist/workers/worker-state-namespace.d.ts +21 -0
- package/dist/workers/worker-state-namespace.js +13 -0
- package/dist/workers/workflow-runner.d.ts +19 -15
- package/dist/workers/workflow-runner.js +72 -60
- package/dist/workers/workflow-worker-entry.js +5 -2
- package/package.json +5 -1
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
KEYS,
|
|
3
|
+
requireStorageCapability
|
|
4
|
+
} from "../../storage/interface.js";
|
|
2
5
|
import {
|
|
3
6
|
ActivityRegistry
|
|
4
7
|
} from "../activity-registry.js";
|
|
@@ -24,6 +27,7 @@ import {
|
|
|
24
27
|
cancelAll as cancelAllWorkflows,
|
|
25
28
|
deleteAll as deleteAllWorkflows,
|
|
26
29
|
purge as purgeWorkflows,
|
|
30
|
+
retryFailedAll as retryFailedAllWorkflows,
|
|
27
31
|
signalAll as signalAllWorkflows,
|
|
28
32
|
tagAll as tagAllWorkflows,
|
|
29
33
|
untagAll as untagAllWorkflows
|
|
@@ -68,7 +72,9 @@ import {
|
|
|
68
72
|
drainQueuedInlineWorkflowStartsForEngine,
|
|
69
73
|
isActivityDefinition
|
|
70
74
|
} from "./engine-runtime-helpers.js";
|
|
71
|
-
import { EngineCreateNameMismatchError } from "./errors.js";
|
|
75
|
+
import { EngineCreateNameMismatchError, EngineDisposedError } from "./errors.js";
|
|
76
|
+
import { assertLeaseHeldForEngineWork, commitFencedEngineWrite } from "./fenced-write.js";
|
|
77
|
+
import { recordFinalizerState } from "./finalizer-state.js";
|
|
72
78
|
import {
|
|
73
79
|
createWorkflowHandleWithResultPromise as createWorkflowHandleWithResultPromiseFromInternals,
|
|
74
80
|
getWorkflowResultPromise as getWorkflowResultPromiseFromInternals
|
|
@@ -80,6 +86,8 @@ import {
|
|
|
80
86
|
resumeParkedInlineWorkflow as resumeParkedInlineWorkflowFromInternals
|
|
81
87
|
} from "./inline-parking.js";
|
|
82
88
|
import { getInternals, initializeInternals } from "./internals.js";
|
|
89
|
+
import { ENGINE_LEASE_LOST_WARNING_NAME, handleDeposition } from "./lease-deposition.js";
|
|
90
|
+
import { createLeaseManager } from "./lease-manager.js";
|
|
83
91
|
import {
|
|
84
92
|
fork as forkFromLifecycle,
|
|
85
93
|
recoverAll as recoverAllFromLifecycle,
|
|
@@ -124,6 +132,7 @@ import {
|
|
|
124
132
|
cancelSchedule as cancelScheduleFromInternals,
|
|
125
133
|
listSchedules as listSchedulesFromInternals,
|
|
126
134
|
pauseSchedule as pauseScheduleFromInternals,
|
|
135
|
+
recoverOrphanedScheduleTimers,
|
|
127
136
|
resumeSchedule as resumeScheduleFromInternals,
|
|
128
137
|
schedule as scheduleFromInternals,
|
|
129
138
|
toScheduleSummary,
|
|
@@ -174,12 +183,19 @@ export {
|
|
|
174
183
|
PersistedDataIncompatibleError,
|
|
175
184
|
StartOrSignalConflictError,
|
|
176
185
|
WorkflowAlreadyExistsError,
|
|
186
|
+
WorkflowConcurrencyLimitExceededError,
|
|
177
187
|
WorkflowNotFoundError,
|
|
178
188
|
WorkflowNotRegisteredError,
|
|
179
189
|
WorkflowSuspendNotSupportedError,
|
|
190
|
+
WorkflowTeardownPendingError,
|
|
180
191
|
WorkflowTypeNotRegisteredForRecoveryError
|
|
181
192
|
} from "./errors.js";
|
|
182
193
|
export { HANDLE_RESULT_PROMISE, WorkflowHandle } from "./handles.js";
|
|
194
|
+
export {
|
|
195
|
+
EngineLeaseAcquisitionTimeoutError,
|
|
196
|
+
EngineLeaseCorruptedError,
|
|
197
|
+
EngineLeaseNotHeldError
|
|
198
|
+
} from "./lease-errors.js";
|
|
183
199
|
export { ScheduleHandle } from "./schedule-handle.js";
|
|
184
200
|
export {
|
|
185
201
|
clearEngineLeakWarningTokenForTesting,
|
|
@@ -192,6 +208,8 @@ export {
|
|
|
192
208
|
export { assertCompatiblePersistedDataVersion };
|
|
193
209
|
export const ENGINE_PARKED_WORKFLOW_COUNT_FOR_TESTING = Symbol("engineParkedWorkflowCountForTesting"), ENGINE_SIGNAL_WAITER_COUNT_FOR_TESTING = Symbol("engineSignalWaiterCountForTesting"), ENGINE_SLEEP_RESOLVER_COUNT_FOR_TESTING = Symbol("engineSleepResolverCountForTesting");
|
|
194
210
|
|
|
211
|
+
export { ENGINE_LEASE_LOST_WARNING_NAME };
|
|
212
|
+
|
|
195
213
|
export class Engine extends EventTarget {
|
|
196
214
|
static async create(options) {
|
|
197
215
|
const engine = new Engine(options);
|
|
@@ -207,6 +225,7 @@ export class Engine extends EventTarget {
|
|
|
207
225
|
throw new EngineCreateNameMismatchError("workflow", name, definition.name);
|
|
208
226
|
engine.register(definition);
|
|
209
227
|
}
|
|
228
|
+
await engine.#acquireLeaseIfConfigured();
|
|
210
229
|
if (options.recover !== !1)
|
|
211
230
|
await engine.recoverAll(options.acknowledgeUnknownWorkflowTypes !== void 0 ? { acknowledgeUnknownWorkflowTypes: options.acknowledgeUnknownWorkflowTypes } : {});
|
|
212
231
|
} catch (error) {
|
|
@@ -230,7 +249,9 @@ export class Engine extends EventTarget {
|
|
|
230
249
|
getComposedWorkflowInterceptor: () => getComposedWorkflowInterceptor(getInternals(this)),
|
|
231
250
|
resolveWorkflowType: this.#resolveWorkflowTypeTarget.bind(this),
|
|
232
251
|
registerCancelHandler: (workflowId, handler) => registerCancelHandler(getInternals(this), workflowId, handler),
|
|
233
|
-
|
|
252
|
+
recordFinalizerState: (workflowId, value) => recordFinalizerState(getInternals(this), workflowId, value),
|
|
253
|
+
getWorkflowServices: (workflowId) => getInternals(this).workflowServices.get(workflowId),
|
|
254
|
+
getLogSink: () => resolvedOptions.onLog ?? void 0
|
|
234
255
|
});
|
|
235
256
|
getInternals(this).storage = storage;
|
|
236
257
|
getInternals(this).abortController = new AbortController;
|
|
@@ -239,6 +260,7 @@ export class Engine extends EventTarget {
|
|
|
239
260
|
getInternals(this).resultResolvers = new Map;
|
|
240
261
|
getInternals(this).signalWaiters = new Map;
|
|
241
262
|
getInternals(this).signalWaitersByWorkflow = new Map;
|
|
263
|
+
getInternals(this).conditionWaiters = new Map;
|
|
242
264
|
getInternals(this).updateWaiters = new Map;
|
|
243
265
|
getInternals(this).updateWaitersByWorkflow = new Map;
|
|
244
266
|
getInternals(this).sleepResolvers = new Map;
|
|
@@ -265,7 +287,8 @@ export class Engine extends EventTarget {
|
|
|
265
287
|
getInternals(this).scheduler = new Scheduler({
|
|
266
288
|
storage,
|
|
267
289
|
onTimerFired: (entry) => handleTimerFiredFromInternals(getInternals(this), entry, this.#createTimeOperationCallbacks()),
|
|
268
|
-
getNow
|
|
290
|
+
getNow,
|
|
291
|
+
commitTimerCleanup: (operations) => commitFencedEngineWrite(getInternals(this), operations, [], () => Error("Timer cleanup lost its fenced CAS race"))
|
|
269
292
|
});
|
|
270
293
|
getInternals(this).strategy = strategyBundle.strategy;
|
|
271
294
|
getInternals(this).inlineStrategy = strategyBundle.inlineStrategy;
|
|
@@ -278,8 +301,10 @@ export class Engine extends EventTarget {
|
|
|
278
301
|
if (queuedInlineWorkflowStartChannel !== null)
|
|
279
302
|
queuedInlineWorkflowStartChannel.port1.onmessage = createQueuedInlineWorkflowStartHandler(weakEngine, queuedInlineWorkflowStartChannel);
|
|
280
303
|
getInternals(this).heartbeatDetails = new Map;
|
|
304
|
+
getInternals(this).lastHeartbeatDetailsByStep = new Map;
|
|
281
305
|
getInternals(this).workflowServices = new Map;
|
|
282
306
|
getInternals(this).pendingAsyncActivities = new Map;
|
|
307
|
+
getInternals(this).pendingAsyncActivityResolutions = new Map;
|
|
283
308
|
getInternals(this).pendingStarts = new Set;
|
|
284
309
|
getInternals(this).pendingScheduleCreations = new Set;
|
|
285
310
|
getInternals(this).workflowsNeedingTerminalCleanup = new Set;
|
|
@@ -290,10 +315,12 @@ export class Engine extends EventTarget {
|
|
|
290
315
|
getInternals(this).workflowReviewIds = new Map;
|
|
291
316
|
getInternals(this).parkedInlineWorkflows = new Set;
|
|
292
317
|
getInternals(this).terminalizingWorkflows = new Set;
|
|
318
|
+
getInternals(this).deliveredPendingUpdateIds = new Map;
|
|
293
319
|
getInternals(this).cancelHandlersByWorkflow = new Map;
|
|
294
320
|
getInternals(this).reviewTimerIds = new Map;
|
|
295
321
|
getInternals(this).pendingWebhooks = new Set;
|
|
296
322
|
getInternals(this).pendingTimelineEntries = new Map;
|
|
323
|
+
getInternals(this).pendingAtomicWorkflowCommitSideEffects = new Map;
|
|
297
324
|
getInternals(this).cleanupIntervalDisposalTracker = null;
|
|
298
325
|
const cleanupIntervalDisposalTracker = {
|
|
299
326
|
disposed: !1,
|
|
@@ -310,15 +337,90 @@ export class Engine extends EventTarget {
|
|
|
310
337
|
getInternals(this).nextRetentionSweepAt = null;
|
|
311
338
|
getInternals(this).secondInstanceDetectionInterval = null;
|
|
312
339
|
getInternals(this).secondInstanceDetector = null;
|
|
340
|
+
getInternals(this).leaseManager = null;
|
|
341
|
+
getInternals(this).inFlightLeaseAcquire = null;
|
|
342
|
+
getInternals(this).deposed = !1;
|
|
343
|
+
getInternals(this).tearDownAfterDeposition = () => {
|
|
344
|
+
this.#disposeAfterDeposition();
|
|
345
|
+
};
|
|
313
346
|
getInternals(this).eventLogHeads = new Map;
|
|
314
347
|
getInternals(this).workflowFeedListeners = new Map;
|
|
315
348
|
getInternals(this).workflowVersionTuples = new Map;
|
|
349
|
+
getInternals(this).workflowVisibilityWatermark = void 0;
|
|
350
|
+
getInternals(this).workflowVisibilityWatermarkExpiresAt = void 0;
|
|
316
351
|
getInternals(this).activityWorkerDispatcher = createActivityWorkerDispatcher(options?.activityExecution);
|
|
317
352
|
getInternals(this).strategy.onMessage(this.#handleStrategyMessage.bind(this));
|
|
318
353
|
getInternals(this).alertManager = createAlertManagerForEngine(this, options?.alerts, getNow);
|
|
319
354
|
this.#ensureRetentionSweepInterval();
|
|
320
355
|
this.#startSecondInstanceDetection();
|
|
321
356
|
}
|
|
357
|
+
addEventListener(type, listener, options) {
|
|
358
|
+
super.addEventListener(type, listener, options);
|
|
359
|
+
}
|
|
360
|
+
removeEventListener(type, listener, options) {
|
|
361
|
+
super.removeEventListener(type, listener, options);
|
|
362
|
+
}
|
|
363
|
+
async#acquireLeaseIfConfigured() {
|
|
364
|
+
const internals = getInternals(this);
|
|
365
|
+
if (internals.options.ownershipMode !== "lease")
|
|
366
|
+
return;
|
|
367
|
+
if (internals.disposed)
|
|
368
|
+
throw new EngineDisposedError;
|
|
369
|
+
if (internals.inFlightLeaseAcquire !== null) {
|
|
370
|
+
await internals.inFlightLeaseAcquire;
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (internals.leaseManager !== null)
|
|
374
|
+
return;
|
|
375
|
+
requireStorageCapability(internals.storage, "conditionalBatch", "ownership: 'lease'");
|
|
376
|
+
const manager = createLeaseManager({
|
|
377
|
+
storage: internals.storage,
|
|
378
|
+
holderId: crypto.randomUUID(),
|
|
379
|
+
getNow: internals.options.getNow,
|
|
380
|
+
ttlMs: internals.options.leaseTtlMs,
|
|
381
|
+
renewIntervalMs: internals.options.leaseRenewIntervalMs,
|
|
382
|
+
waitTimeoutMs: internals.options.leaseWaitTimeoutMs,
|
|
383
|
+
onLeaseLost: (reason) => {
|
|
384
|
+
if (reason === "deposed") {
|
|
385
|
+
handleDeposition(internals);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
process.emitWarning("engine ownership lease renewal could not be confirmed (transient storage error); another instance may take over if this persists. Weft supports one engine process " + "per store \u2014 keep infrastructure-level single-instance enforcement in place.", ENGINE_LEASE_LOST_WARNING_NAME);
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
internals.leaseManager = manager;
|
|
392
|
+
const acquisition = (async () => {
|
|
393
|
+
try {
|
|
394
|
+
await manager.acquire();
|
|
395
|
+
} catch (error) {
|
|
396
|
+
if (internals.leaseManager === manager)
|
|
397
|
+
internals.leaseManager = null;
|
|
398
|
+
throw error;
|
|
399
|
+
}
|
|
400
|
+
if (internals.disposed) {
|
|
401
|
+
await manager.release();
|
|
402
|
+
if (internals.leaseManager === manager)
|
|
403
|
+
internals.leaseManager = null;
|
|
404
|
+
throw new EngineDisposedError;
|
|
405
|
+
}
|
|
406
|
+
manager.startRenewal();
|
|
407
|
+
})();
|
|
408
|
+
internals.inFlightLeaseAcquire = acquisition;
|
|
409
|
+
try {
|
|
410
|
+
await acquisition;
|
|
411
|
+
} finally {
|
|
412
|
+
if (internals.inFlightLeaseAcquire === acquisition)
|
|
413
|
+
internals.inFlightLeaseAcquire = null;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
#disposeAfterDeposition() {
|
|
417
|
+
const internals = getInternals(this);
|
|
418
|
+
if (internals.disposed)
|
|
419
|
+
return;
|
|
420
|
+
const leaseManager = internals.leaseManager;
|
|
421
|
+
disposeEngine(internals);
|
|
422
|
+
leaseManager?.release().catch(() => {});
|
|
423
|
+
}
|
|
322
424
|
#startSecondInstanceDetection() {
|
|
323
425
|
const internals = getInternals(this);
|
|
324
426
|
if (!internals.options.secondInstanceDetectionEnabled)
|
|
@@ -437,11 +539,13 @@ export class Engine extends EventTarget {
|
|
|
437
539
|
return getInternals(this).activityRegistry.listDefinitions();
|
|
438
540
|
}
|
|
439
541
|
async start(type, input, options) {
|
|
542
|
+
assertLeaseHeldForEngineWork(getInternals(this));
|
|
440
543
|
if (options?.idempotencyKey !== void 0)
|
|
441
544
|
return startWithIdempotencyFromLifecycle(getInternals(this), type, input, options, this.#createLifecycleCallbacks());
|
|
442
545
|
return startWorkflowFromLifecycle(getInternals(this), type, input, options, void 0, this.#createLifecycleCallbacks());
|
|
443
546
|
}
|
|
444
547
|
async startOrSignal(type, input, signal, options) {
|
|
548
|
+
assertLeaseHeldForEngineWork(getInternals(this));
|
|
445
549
|
return startOrSignalFromLifecycle(getInternals(this), type, input, signal, options, this.#createStartOrSignalCallbacks());
|
|
446
550
|
}
|
|
447
551
|
#createStartOrSignalCallbacks() {
|
|
@@ -474,6 +578,9 @@ export class Engine extends EventTarget {
|
|
|
474
578
|
async cancelAll(filter, options) {
|
|
475
579
|
return cancelAllWorkflows(getInternals(this), filter, options);
|
|
476
580
|
}
|
|
581
|
+
async retryFailedAll(filter, options) {
|
|
582
|
+
return retryFailedAllWorkflows(getInternals(this), filter, options);
|
|
583
|
+
}
|
|
477
584
|
async signalAll(filter, name, payloadOrOptions, options) {
|
|
478
585
|
if (options === void 0)
|
|
479
586
|
return signalAllWorkflows(getInternals(this), filter, name, payloadOrOptions);
|
|
@@ -494,7 +601,8 @@ export class Engine extends EventTarget {
|
|
|
494
601
|
return scheduleFromInternals(getInternals(this), workflowType, definition.input, definitionSpec, {
|
|
495
602
|
...definition.id !== void 0 && { id: definition.id },
|
|
496
603
|
...definition.overlapPolicy !== void 0 && { overlap: definition.overlapPolicy },
|
|
497
|
-
...definition.backfill !== void 0 && { backfill: definition.backfill }
|
|
604
|
+
...definition.backfill !== void 0 && { backfill: definition.backfill },
|
|
605
|
+
...definition.jitter !== void 0 && { jitter: definition.jitter }
|
|
498
606
|
});
|
|
499
607
|
}
|
|
500
608
|
if (spec === void 0)
|
|
@@ -554,20 +662,32 @@ export class Engine extends EventTarget {
|
|
|
554
662
|
return getOffloadFromInternals(getInternals(this), workflowId, key);
|
|
555
663
|
}
|
|
556
664
|
async fork(sourceWorkflowId, options) {
|
|
665
|
+
assertLeaseHeldForEngineWork(getInternals(this));
|
|
557
666
|
return forkFromLifecycle(getInternals(this), sourceWorkflowId, options, this.#createLifecycleCallbacks());
|
|
558
667
|
}
|
|
559
668
|
async resume(workflowId) {
|
|
669
|
+
assertLeaseHeldForEngineWork(getInternals(this));
|
|
560
670
|
return resumeFromLifecycle(getInternals(this), workflowId, this.#createLifecycleCallbacks());
|
|
561
671
|
}
|
|
562
672
|
async recoverAll(options) {
|
|
673
|
+
await this.#acquireLeaseIfConfigured();
|
|
674
|
+
if (getInternals(this).disposed)
|
|
675
|
+
throw new EngineDisposedError;
|
|
563
676
|
await recoverPendingAsyncActivities(getInternals(this));
|
|
677
|
+
await recoverOrphanedScheduleTimers(getInternals(this));
|
|
564
678
|
return recoverAllFromLifecycle(getInternals(this), this.#createLifecycleCallbacks(), options);
|
|
565
679
|
}
|
|
566
680
|
async completeAsyncActivity(token, result) {
|
|
567
|
-
await completeAsyncActivityFromInternals(getInternals(this), token, result,
|
|
681
|
+
await completeAsyncActivityFromInternals(getInternals(this), token, result, {
|
|
682
|
+
feedOperationResult: (workflowId, outcome) => feedOperationResult(getInternals(this), workflowId, outcome),
|
|
683
|
+
finalizeTimeline: (workflowId, status, output) => finalizePendingTimelineEntry(getInternals(this), workflowId, status, output)
|
|
684
|
+
});
|
|
568
685
|
}
|
|
569
686
|
async failAsyncActivity(token, error) {
|
|
570
|
-
await failAsyncActivityFromInternals(getInternals(this), token, error,
|
|
687
|
+
await failAsyncActivityFromInternals(getInternals(this), token, error, {
|
|
688
|
+
feedOperationResult: (workflowId, outcome, originalReason) => feedOperationResult(getInternals(this), workflowId, outcome, originalReason),
|
|
689
|
+
finalizeTimeline: (workflowId, status, output) => finalizePendingTimelineEntry(getInternals(this), workflowId, status, output)
|
|
690
|
+
});
|
|
571
691
|
}
|
|
572
692
|
async cancel(workflowId) {
|
|
573
693
|
await cancelWorkflowFromTermination(getInternals(this), workflowId, this.#createTerminationCallbacks());
|
|
@@ -647,14 +767,19 @@ export class Engine extends EventTarget {
|
|
|
647
767
|
return submitCoordinatedUpdateFromInternals(getInternals(this), workflowId, name, payload, options, this.#createUpdateCallbacks());
|
|
648
768
|
}
|
|
649
769
|
[Symbol.dispose]() {
|
|
770
|
+
const leaseManager = getInternals(this).leaseManager;
|
|
650
771
|
disposeEngine(getInternals(this));
|
|
772
|
+
leaseManager?.release();
|
|
651
773
|
}
|
|
652
774
|
async[Symbol.asyncDispose]() {
|
|
653
775
|
if (!getInternals(this).disposed) {
|
|
776
|
+
const leaseManager = getInternals(this).leaseManager;
|
|
654
777
|
try {
|
|
655
778
|
await drainQueuedInlineWorkflowStartsForEngine(this);
|
|
656
779
|
} finally {
|
|
657
|
-
this
|
|
780
|
+
disposeEngine(getInternals(this));
|
|
781
|
+
await getInternals(this).inFlightLeaseAcquire?.catch(() => {});
|
|
782
|
+
await leaseManager?.release();
|
|
658
783
|
}
|
|
659
784
|
return;
|
|
660
785
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hasExposedAccessors, hasUpdateHandlers } from "../context/context-presence.js";
|
|
1
2
|
import {
|
|
2
3
|
resumeWorkflowFromStorage as resumeWorkflowFromStorageFromLifecycle
|
|
3
4
|
} from "./lifecycle.js";
|
|
@@ -9,7 +10,7 @@ export async function parkInlineWorkflowAfterCheckpoint(internals, workflowId, o
|
|
|
9
10
|
if (operation.type !== "wait-signal" || internals.inlineStrategy === null)
|
|
10
11
|
return !1;
|
|
11
12
|
const inlineStrategy = internals.inlineStrategy, context = inlineStrategy.getContext(workflowId);
|
|
12
|
-
if (context
|
|
13
|
+
if (context && (hasUpdateHandlers(context) || hasExposedAccessors(context)))
|
|
13
14
|
return !1;
|
|
14
15
|
if (await callbacks.hasBufferedSignal(workflowId, operation.signalName))
|
|
15
16
|
return !1;
|
|
@@ -17,7 +18,7 @@ export async function parkInlineWorkflowAfterCheckpoint(internals, workflowId, o
|
|
|
17
18
|
const latestState = await callbacks.loadWorkflowState(workflowId);
|
|
18
19
|
if (internals.terminalizingWorkflows.has(workflowId) || !latestState || latestState.status !== "running")
|
|
19
20
|
return !1;
|
|
20
|
-
inlineStrategy.parkWorkflow(workflowId);
|
|
21
|
+
inlineStrategy.parkWorkflow(workflowId, { retainContext: !0 });
|
|
21
22
|
internals.parkedInlineWorkflows.add(workflowId);
|
|
22
23
|
return !0;
|
|
23
24
|
}))
|
|
@@ -28,16 +28,19 @@ import type { InlineExecutionStrategy } from '../inline-execution-strategy.ts';
|
|
|
28
28
|
import type { ComposedActivityInterceptor, ComposedWorkflowInterceptor, Interceptor } from '../interceptor.ts';
|
|
29
29
|
import type { HumanReviewResult, ReviewCoordinator } from '../review/index.ts';
|
|
30
30
|
import type { Scheduler } from '../scheduler.ts';
|
|
31
|
-
import type { Checkpoint } from '../types.ts';
|
|
31
|
+
import type { Checkpoint, StartWorkflowOptions } from '../types.ts';
|
|
32
32
|
import type { UpdateCoordinator } from '../updates.ts';
|
|
33
33
|
import type { WorkflowVersionTuple } from '../workflow-version-tuple.ts';
|
|
34
34
|
import type { PendingTimelineEntry, QueuedInlineWorkflowExecutionStart, RegistrationEntry, ResolvedOptions, TrackedWaiterKeys, WorkflowResultWaiter } from './engine-internal-types.ts';
|
|
35
35
|
import type { EngineCleanupIntervalDisposalTracker } from './engine-leak-warnings.ts';
|
|
36
36
|
import type { WorkflowHandle, WorkflowHandleEngine } from './handles.ts';
|
|
37
37
|
import type { WorkflowFeedListener } from './index.ts';
|
|
38
|
+
import type { LeaseManager } from './lease-manager.ts';
|
|
38
39
|
import type { ScheduleHandleEngine } from './schedule-handle.ts';
|
|
39
40
|
import type { SecondInstanceDetector } from './second-instance-detector.ts';
|
|
40
|
-
type EngineRuntime = WorkflowHandleEngine & ScheduleHandleEngine
|
|
41
|
+
type EngineRuntime = WorkflowHandleEngine & ScheduleHandleEngine & {
|
|
42
|
+
start(type: string, input: unknown, options?: StartWorkflowOptions): Promise<WorkflowHandle>;
|
|
43
|
+
};
|
|
41
44
|
export interface EngineInternals {
|
|
42
45
|
engine: EngineRuntime;
|
|
43
46
|
storage: WeftStorage;
|
|
@@ -67,6 +70,23 @@ export interface EngineInternals {
|
|
|
67
70
|
resultResolvers: Map<string, WorkflowResultWaiter>;
|
|
68
71
|
signalWaiters: Map<string, () => void>;
|
|
69
72
|
signalWaitersByWorkflow: Map<string, TrackedWaiterKeys>;
|
|
73
|
+
/**
|
|
74
|
+
* In-process resolvers for inline `ctx.waitUntil` waits, keyed by `workflowId`
|
|
75
|
+
* ALONE — a workflow has at most one active wait-condition because its inline
|
|
76
|
+
* generator is suspended at exactly one yield, and the top-level-only guard in
|
|
77
|
+
* `executeSubOperation` rejects `waitUntil` as a `race`/`all`/`speculate`
|
|
78
|
+
* branch. So unlike `signalWaiters` (which need a per-workflow string-or-Set
|
|
79
|
+
* index because they CAN be concurrent sub-operations), this is a flat map with
|
|
80
|
+
* no secondary index. Calling the resolver wakes `processWaitConditionOperation`
|
|
81
|
+
* to re-evaluate its predicate. Never checkpointed — engine-memory state cleared
|
|
82
|
+
* on terminal cleanup.
|
|
83
|
+
*
|
|
84
|
+
* Note: the deadline TIMER is still keyed `cond:${workflowId}:${step}` (step is
|
|
85
|
+
* stable across replay, so recovery does not double-arm). Only this waiter map
|
|
86
|
+
* keys by `workflowId`. If `waitUntil`-in-`race`/`all`/`speculate` is ever
|
|
87
|
+
* supported, this keying must revert to `${workflowId}:${step}` + a Set index.
|
|
88
|
+
*/
|
|
89
|
+
conditionWaiters: Map<string, () => void>;
|
|
70
90
|
updateWaiters: Map<string, (payload: unknown) => void>;
|
|
71
91
|
updateWaitersByWorkflow: Map<string, TrackedWaiterKeys>;
|
|
72
92
|
sleepResolvers: Map<string, () => void>;
|
|
@@ -110,11 +130,25 @@ export interface EngineInternals {
|
|
|
110
130
|
broadcastChannel: BroadcastChannel | null;
|
|
111
131
|
pendingNestingDepth: number | undefined;
|
|
112
132
|
pendingParentHeaders: Map<string, string> | undefined;
|
|
113
|
-
pendingExecutionStateOwnerId: string | undefined;
|
|
133
|
+
pendingExecutionStateOwnerId: string | null | undefined;
|
|
114
134
|
workflowNestingDepths: Map<string, number>;
|
|
115
135
|
workflowHeaders: Map<string, Map<string, string>>;
|
|
116
136
|
workflowStateWriteChains: Map<string, Promise<void>>;
|
|
117
137
|
heartbeatDetails: Map<string, unknown>;
|
|
138
|
+
/**
|
|
139
|
+
* Last heartbeat payload PER ACTIVITY STEP, so a retry of a step can read the
|
|
140
|
+
* heartbeat its previous attempt recorded (the resumable-batch pattern). Keyed
|
|
141
|
+
* `workflowId -> step -> details`. This is separate from {@link heartbeatDetails}
|
|
142
|
+
* (which is keyed by `workflowId` alone and powers the `activityProgress` query):
|
|
143
|
+
* concurrent activities inside one `ctx.all` would clobber a workflow-keyed map,
|
|
144
|
+
* so a retry could read a sibling's heartbeat. The step is stable across attempts
|
|
145
|
+
* (assigned once at `stepIndex++`), so a retry reads its OWN prior heartbeat.
|
|
146
|
+
* Never checkpointed — held only in engine memory and cleared (by workflowId, the
|
|
147
|
+
* outer key) on terminal cleanup and purge, the same lifecycle as
|
|
148
|
+
* {@link heartbeatDetails}. Inline-execution only; worker-executed activities run
|
|
149
|
+
* their function out of process and never observe this.
|
|
150
|
+
*/
|
|
151
|
+
lastHeartbeatDetailsByStep: Map<string, Map<number, unknown>>;
|
|
118
152
|
/**
|
|
119
153
|
* Per-run, non-serialized `services` value exposed to the workflow body as
|
|
120
154
|
* `ctx.services`. Set at `engine.start({ services })` and re-provided on
|
|
@@ -130,6 +164,12 @@ export interface EngineInternals {
|
|
|
130
164
|
* reloaded by `recoverAll()`. See `async-activity-completion.ts`.
|
|
131
165
|
*/
|
|
132
166
|
pendingAsyncActivities: Map<string, import('./async-activity-completion.ts').PendingAsyncActivity>;
|
|
167
|
+
/**
|
|
168
|
+
* Completed or failed async-activity tokens that were consumed before inline
|
|
169
|
+
* recovery adopted the workflow generator. Replay drains these by workflow id
|
|
170
|
+
* when it reaches the same deterministic async-activity token again.
|
|
171
|
+
*/
|
|
172
|
+
pendingAsyncActivityResolutions: Map<string, import('./async-activity-completion.ts').PendingAsyncActivityResolution[]>;
|
|
133
173
|
pendingStarts: Set<string>;
|
|
134
174
|
pendingScheduleCreations: Set<string>;
|
|
135
175
|
workflowsNeedingTerminalCleanup: Set<string>;
|
|
@@ -142,6 +182,45 @@ export interface EngineInternals {
|
|
|
142
182
|
secondInstanceDetectionInterval: ReturnType<typeof setInterval> | null;
|
|
143
183
|
/** The active second-instance detector; `null` when detection is disabled. */
|
|
144
184
|
secondInstanceDetector: SecondInstanceDetector | null;
|
|
185
|
+
/**
|
|
186
|
+
* The active lease manager for `ownership: 'lease'`; `null` when ownership is
|
|
187
|
+
* `'none'`. Acquired before recovery, renewed on a heartbeat, released on
|
|
188
|
+
* dispose. Unlike the second-instance detector its acquire/renew errors are real
|
|
189
|
+
* (a failed acquire blocks recovery). Renewal loss is surfaced as a warning in
|
|
190
|
+
* Step 1; epoch fencing of durable writes (Step 2) is what makes it enforceable.
|
|
191
|
+
*/
|
|
192
|
+
leaseManager: LeaseManager | null;
|
|
193
|
+
/**
|
|
194
|
+
* The in-flight lease acquisition, or `null` when none is running. Set while
|
|
195
|
+
* `#acquireLeaseIfConfigured` awaits `acquire()` (which can park for the whole
|
|
196
|
+
* `leaseWaitTimeout` waiting for a handoff) and cleared when it settles. Disposal
|
|
197
|
+
* awaits this so a dispose that races a parked acquire is a clean handoff — the
|
|
198
|
+
* holder this engine may take is released before `asyncDispose` resolves, rather
|
|
199
|
+
* than leaking until TTL on an already-disposed engine.
|
|
200
|
+
*/
|
|
201
|
+
inFlightLeaseAcquire: Promise<void> | null;
|
|
202
|
+
/**
|
|
203
|
+
* Set to `true` the instant this engine is detected as deposed under
|
|
204
|
+
* `ownership: 'lease'` — either a fenced durable write's CAS failed against a
|
|
205
|
+
* newer epoch ({@link commitFencedEngineWrite}) or the lease manager reported
|
|
206
|
+
* `onLeaseLost('deposed')`. It is the synchronous half of the deposition halt:
|
|
207
|
+
* {@link commitFencedEngineWrite} short-circuits at its top on this flag, so a
|
|
208
|
+
* write that *starts* after detection is rejected before reaching storage,
|
|
209
|
+
* while writes already in flight are caught by the epoch CAS itself. Set once
|
|
210
|
+
* by {@link handleDeposition}, which also warns the operator and schedules a
|
|
211
|
+
* deferred engine teardown. Always `false` under `ownership: 'none'`.
|
|
212
|
+
*/
|
|
213
|
+
deposed: boolean;
|
|
214
|
+
/**
|
|
215
|
+
* Teardown callback invoked (deferred to a later tick) when this engine is
|
|
216
|
+
* deposed. Set once during engine construction to the engine's own
|
|
217
|
+
* `disposeAfterDeposition` method. It exists as an injected field rather than a
|
|
218
|
+
* direct import so {@link handleDeposition} (reached from the durable-write
|
|
219
|
+
* helper) does not statically import `disposal.ts`, which would close an import
|
|
220
|
+
* cycle (`storage-io → fenced-write → lease-deposition → disposal → … →
|
|
221
|
+
* storage-io`). `null` until the constructor wires it.
|
|
222
|
+
*/
|
|
223
|
+
tearDownAfterDeposition: (() => void) | null;
|
|
145
224
|
reviewCoordinator: ReviewCoordinator;
|
|
146
225
|
reviewWaiters: Map<string, (decision: HumanReviewResult) => void>;
|
|
147
226
|
reviewWaitersByWorkflow: Map<string, TrackedWaiterKeys>;
|
|
@@ -152,6 +231,22 @@ export interface EngineInternals {
|
|
|
152
231
|
workflowReviewIds: Map<string, Set<string>>;
|
|
153
232
|
parkedInlineWorkflows: Set<string>;
|
|
154
233
|
terminalizingWorkflows: Set<string>;
|
|
234
|
+
/**
|
|
235
|
+
* Coordinated update IDs already claimed for delivery by a pending-update
|
|
236
|
+
* drain, keyed by workflow. Several drain triggers (each `update()` schedules a
|
|
237
|
+
* `setTimeout(0)` drain; the post-advance path drains too) can fire
|
|
238
|
+
* near-simultaneously, and the durable consume-delete (`buildResponseOperations`
|
|
239
|
+
* deletes the pending key via async `storage.batch`) lags the in-memory
|
|
240
|
+
* `getPendingUpdates` scan — so overlapping drains would re-read and re-deliver
|
|
241
|
+
* the same buffered update. Each drain claims an update's id SYNCHRONOUSLY (no
|
|
242
|
+
* `await` between the membership check and the add) before delivering it, so a
|
|
243
|
+
* racing drain that scans the same id skips it. The claim persists across
|
|
244
|
+
* drains (unlike a per-drain guard), which is what makes delivery idempotent
|
|
245
|
+
* against the cross-drain race. Cleared per workflow on terminal cleanup; empty
|
|
246
|
+
* after a crash, which matches durable state (recovery re-delivers exactly the
|
|
247
|
+
* updates whose delete never committed).
|
|
248
|
+
*/
|
|
249
|
+
deliveredPendingUpdateIds: Map<string, Set<string>>;
|
|
155
250
|
cancelHandlersByWorkflow: Map<string, Array<() => Promise<void> | void>>;
|
|
156
251
|
reviewTimerIds: Map<string, string[]>;
|
|
157
252
|
pendingWebhooks: Set<AbortController>;
|
|
@@ -159,7 +254,16 @@ export interface EngineInternals {
|
|
|
159
254
|
eventLogHeads: Map<string, Readonly<EventHeadRecord>>;
|
|
160
255
|
workflowFeedListeners: Map<string, Set<WorkflowFeedListener>>;
|
|
161
256
|
workflowVersionTuples: Map<string, WorkflowVersionTuple>;
|
|
257
|
+
/**
|
|
258
|
+
* Cached visibility-index watermark for query planning. `undefined` means the
|
|
259
|
+
* watermark has not been read yet or an in-process caller invalidated it.
|
|
260
|
+
* `workflowVisibilityWatermarkExpiresAt` bounds how long external maintenance
|
|
261
|
+
* commands can leave this process planning against a stale watermark.
|
|
262
|
+
*/
|
|
263
|
+
workflowVisibilityWatermark: 'current' | 'stale' | undefined;
|
|
264
|
+
workflowVisibilityWatermarkExpiresAt: number | undefined;
|
|
162
265
|
pendingTimelineEntries: Map<string, PendingTimelineEntry>;
|
|
266
|
+
pendingAtomicWorkflowCommitSideEffects: Map<string, import('./checkpoint-side-effects.ts').AtomicWorkflowCommitSideEffects>;
|
|
163
267
|
}
|
|
164
268
|
/**
|
|
165
269
|
* Set up an empty `EngineInternals` skeleton in the WeakMap. The Engine
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure codecs for the two lease ownership keys (`lease:epoch` and `lease:holder`).
|
|
3
|
+
* Extracted from `lease-manager.ts` to keep that file under the `max-lines` ceiling;
|
|
4
|
+
* these functions have no engine state, so they live and test cleanly on their own.
|
|
5
|
+
*
|
|
6
|
+
* @module core/engine/lease-codec
|
|
7
|
+
*/
|
|
8
|
+
/** The decoded `lease:holder` record. */
|
|
9
|
+
export type LeaseHolderRecord = {
|
|
10
|
+
holderId: string;
|
|
11
|
+
/** Wall-clock ms (from the engine's `getNow`) after which the lease may be stolen. */
|
|
12
|
+
expiresAt: number;
|
|
13
|
+
/** The ownership epoch this holder acquired. Mirrors `lease:epoch`. */
|
|
14
|
+
epoch: number;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Encode an 8-byte big-endian uint64 epoch. `DataView.setBigUint64` is the
|
|
18
|
+
* standard, overflow-safe encoder — preferred over a hand-rolled byte loop.
|
|
19
|
+
*/
|
|
20
|
+
export declare function encodeEpoch(epoch: number): Uint8Array;
|
|
21
|
+
/**
|
|
22
|
+
* Decode an 8-byte big-endian uint64 epoch, or `null` when the stored value is
|
|
23
|
+
* not a usable epoch — not exactly 8 bytes, or outside `[1, MAX_SAFE_INTEGER)`.
|
|
24
|
+
* The epoch is the monotonic fencing high-water mark, so it must stay exactly
|
|
25
|
+
* comparable AND have room to increment: acquisition always mints `epoch + 1`, so
|
|
26
|
+
* `Number.MAX_SAFE_INTEGER` itself is rejected too — minting `2^53` would fail
|
|
27
|
+
* `Number.isSafeInteger` on the next boot and brick the lease. A value at or above
|
|
28
|
+
* the safe-integer ceiling (or below `1`) routes to the corruption path
|
|
29
|
+
* (fail-closed at boot) rather than silently re-minting an imprecise generation.
|
|
30
|
+
*/
|
|
31
|
+
export declare function decodeEpoch(raw: Uint8Array): number | null;
|
|
32
|
+
/** Encode a holder record to its stored JSON bytes. */
|
|
33
|
+
export declare function encodeHolder(record: LeaseHolderRecord): Uint8Array;
|
|
34
|
+
/** Decode a stored holder record, tolerating any malformed/foreign value as `null`. */
|
|
35
|
+
export declare function decodeHolder(raw: Uint8Array): LeaseHolderRecord | null;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const textEncoder = new TextEncoder, textDecoder = new TextDecoder;
|
|
2
|
+
export function encodeEpoch(epoch) {
|
|
3
|
+
const bytes = new Uint8Array(8);
|
|
4
|
+
new DataView(bytes.buffer).setBigUint64(0, BigInt(epoch), !1);
|
|
5
|
+
return bytes;
|
|
6
|
+
}
|
|
7
|
+
export function decodeEpoch(raw) {
|
|
8
|
+
if (raw.byteLength !== 8)
|
|
9
|
+
return null;
|
|
10
|
+
const value = new DataView(raw.buffer, raw.byteOffset, raw.byteLength).getBigUint64(0, !1), numberValue = Number(value);
|
|
11
|
+
return Number.isSafeInteger(numberValue) && numberValue >= 1 && numberValue < Number.MAX_SAFE_INTEGER ? numberValue : null;
|
|
12
|
+
}
|
|
13
|
+
export function encodeHolder(record) {
|
|
14
|
+
return textEncoder.encode(JSON.stringify(record));
|
|
15
|
+
}
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return typeof value === "object" && value !== null;
|
|
18
|
+
}
|
|
19
|
+
export function decodeHolder(raw) {
|
|
20
|
+
let parsed;
|
|
21
|
+
try {
|
|
22
|
+
parsed = JSON.parse(textDecoder.decode(raw));
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
if (!isRecord(parsed))
|
|
27
|
+
return null;
|
|
28
|
+
const { holderId, expiresAt, epoch } = parsed;
|
|
29
|
+
if (typeof holderId !== "string" || typeof expiresAt !== "number" || !Number.isSafeInteger(expiresAt) || expiresAt < 0 || typeof epoch !== "number" || !Number.isSafeInteger(epoch) || epoch < 1)
|
|
30
|
+
return null;
|
|
31
|
+
return { holderId, expiresAt, epoch };
|
|
32
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deposition handling for `ownership: 'lease'` Step-2 fencing (issue #470).
|
|
3
|
+
*
|
|
4
|
+
* "Deposed" means another instance now holds the ownership lease at a strictly
|
|
5
|
+
* newer epoch, so this instance has lost the store. Deposition is detected at two
|
|
6
|
+
* sites and both funnel here:
|
|
7
|
+
*
|
|
8
|
+
* - A fenced durable write's CAS fails against a newer epoch
|
|
9
|
+
* ({@link commitFencedEngineWrite} re-reads `lease:epoch` to disambiguate).
|
|
10
|
+
* - The lease manager's renewal reports `onLeaseLost('deposed')` (a successor
|
|
11
|
+
* stole the holder out from under an otherwise-idle engine).
|
|
12
|
+
*
|
|
13
|
+
* The renewal-loss reason `'renewal-unconfirmable'` is deliberately NOT routed
|
|
14
|
+
* here: it is a transient storage blip (the holder could not prove it still
|
|
15
|
+
* holds), and halting a healthy holder on a momentary storage hiccup would be a
|
|
16
|
+
* self-inflicted outage. Only a confirmed `'deposed'` triggers the halt.
|
|
17
|
+
*
|
|
18
|
+
* {@link handleDeposition} is idempotent (guarded on {@link EngineInternals.deposed})
|
|
19
|
+
* and does the minimum synchronously — set the flag, warn the operator — then
|
|
20
|
+
* schedules engine teardown on a later tick. Teardown is never run inline:
|
|
21
|
+
* detection happens mid-commit while a workflow generator is advancing, and
|
|
22
|
+
* synchronous {@link disposeEngine} clears maps that the unwinding commit and
|
|
23
|
+
* other in-flight workflows still touch. The flag (plus the
|
|
24
|
+
* {@link commitFencedEngineWrite} top short-circuit) carries the halt guarantee;
|
|
25
|
+
* deferring the actual dispose by a tick is safe because every write in that
|
|
26
|
+
* window is still fenced on the now-stale epoch and loses its CAS.
|
|
27
|
+
*
|
|
28
|
+
* This module is allow-listed for import only from `src/core/engine/**`.
|
|
29
|
+
*/
|
|
30
|
+
import type { EngineInternals } from './internals.ts';
|
|
31
|
+
/**
|
|
32
|
+
* The `name` of the `process` warning emitted when an engine configured with
|
|
33
|
+
* `ownership: 'lease'` can no longer be sure it owns the store. The same name
|
|
34
|
+
* covers all three signals, so a single `process.on('warning')` handler catches
|
|
35
|
+
* every "this engine may no longer own the store" event:
|
|
36
|
+
*
|
|
37
|
+
* - **deposed (confirmed)** — a renewal's CAS failed (a successor stole the lease),
|
|
38
|
+
* or a fenced durable write was rejected because a newer epoch holds the store
|
|
39
|
+
* (Step 2). The engine halts.
|
|
40
|
+
* - **renewal-unconfirmable (transient)** — a storage error left the holder unable
|
|
41
|
+
* to prove it still holds within the lease window. This is NOT a confirmed
|
|
42
|
+
* deposition; the engine keeps running, and a later renewal or fenced write
|
|
43
|
+
* resolves the truth. The warning is informational so operators can investigate.
|
|
44
|
+
*
|
|
45
|
+
* Filter on this name to react to ownership-loss signals (alert, drain, restart).
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* import { ENGINE_LEASE_LOST_WARNING_NAME } from '@lostgradient/weft';
|
|
50
|
+
*
|
|
51
|
+
* process.on('warning', (warning) => {
|
|
52
|
+
* if (warning.name === ENGINE_LEASE_LOST_WARNING_NAME) {
|
|
53
|
+
* // This engine may no longer own the store — investigate / drain / restart.
|
|
54
|
+
* }
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare const ENGINE_LEASE_LOST_WARNING_NAME = "WeftEngineLeaseLostWarning";
|
|
59
|
+
/**
|
|
60
|
+
* React to a confirmed deposition. Idempotent: the first call sets the deposed
|
|
61
|
+
* flag, emits the operator warning, and schedules a deferred engine teardown;
|
|
62
|
+
* subsequent calls are no-ops. Returns immediately — the caller is expected to
|
|
63
|
+
* then throw {@link EngineDeposedError} to unwind the current commit so it does
|
|
64
|
+
* not advance in-memory state past a durable write that did not land.
|
|
65
|
+
*/
|
|
66
|
+
export declare function handleDeposition(internals: EngineInternals): void;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const ENGINE_LEASE_LOST_WARNING_NAME = "WeftEngineLeaseLostWarning";
|
|
2
|
+
export function handleDeposition(internals) {
|
|
3
|
+
if (internals.deposed)
|
|
4
|
+
return;
|
|
5
|
+
internals.deposed = !0;
|
|
6
|
+
process.emitWarning("engine deposed: a durable write was fenced out because another instance now holds this store's ownership lease at a newer epoch. This engine is halting to avoid split-brain writes. Weft supports one engine process per store; ensure your deploy hands off cleanly (graceful shutdown releases the lease) and keep infrastructure-level single-instance enforcement in place.", ENGINE_LEASE_LOST_WARNING_NAME);
|
|
7
|
+
const tearDown = internals.tearDownAfterDeposition;
|
|
8
|
+
if (tearDown !== null)
|
|
9
|
+
Promise.resolve().then(tearDown).catch(() => {});
|
|
10
|
+
}
|