@lostgradient/weft 0.3.0 → 0.5.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 +61 -5
- package/dist/client/local-handles.d.ts +22 -0
- package/dist/client/local-handles.js +23 -0
- package/dist/client/local.d.ts +6 -3
- package/dist/client/local.js +12 -26
- 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 +46 -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 +136 -9
- 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 +6 -13
- package/dist/core/engine/operations-activity.js +26 -53
- package/dist/core/engine/operations-coordination.d.ts +10 -0
- package/dist/core/engine/operations-coordination.js +44 -9
- 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 +25 -6
- 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 +19 -19
- 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 +32 -32
- 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 +20 -20
- 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,8 +225,11 @@ export class Engine extends EventTarget {
|
|
|
207
225
|
throw new EngineCreateNameMismatchError("workflow", name, definition.name);
|
|
208
226
|
engine.register(definition);
|
|
209
227
|
}
|
|
210
|
-
|
|
228
|
+
await engine.#acquireLeaseIfConfigured();
|
|
229
|
+
if (options.recover !== !1) {
|
|
211
230
|
await engine.recoverAll(options.acknowledgeUnknownWorkflowTypes !== void 0 ? { acknowledgeUnknownWorkflowTypes: options.acknowledgeUnknownWorkflowTypes } : {});
|
|
231
|
+
getInternals(engine).scheduler.start();
|
|
232
|
+
}
|
|
212
233
|
} catch (error) {
|
|
213
234
|
await engine[Symbol.asyncDispose]();
|
|
214
235
|
throw error;
|
|
@@ -230,7 +251,9 @@ export class Engine extends EventTarget {
|
|
|
230
251
|
getComposedWorkflowInterceptor: () => getComposedWorkflowInterceptor(getInternals(this)),
|
|
231
252
|
resolveWorkflowType: this.#resolveWorkflowTypeTarget.bind(this),
|
|
232
253
|
registerCancelHandler: (workflowId, handler) => registerCancelHandler(getInternals(this), workflowId, handler),
|
|
233
|
-
|
|
254
|
+
recordFinalizerState: (workflowId, value) => recordFinalizerState(getInternals(this), workflowId, value),
|
|
255
|
+
getWorkflowServices: (workflowId) => getInternals(this).workflowServices.get(workflowId),
|
|
256
|
+
getLogSink: () => resolvedOptions.onLog ?? void 0
|
|
234
257
|
});
|
|
235
258
|
getInternals(this).storage = storage;
|
|
236
259
|
getInternals(this).abortController = new AbortController;
|
|
@@ -239,6 +262,7 @@ export class Engine extends EventTarget {
|
|
|
239
262
|
getInternals(this).resultResolvers = new Map;
|
|
240
263
|
getInternals(this).signalWaiters = new Map;
|
|
241
264
|
getInternals(this).signalWaitersByWorkflow = new Map;
|
|
265
|
+
getInternals(this).conditionWaiters = new Map;
|
|
242
266
|
getInternals(this).updateWaiters = new Map;
|
|
243
267
|
getInternals(this).updateWaitersByWorkflow = new Map;
|
|
244
268
|
getInternals(this).sleepResolvers = new Map;
|
|
@@ -265,7 +289,8 @@ export class Engine extends EventTarget {
|
|
|
265
289
|
getInternals(this).scheduler = new Scheduler({
|
|
266
290
|
storage,
|
|
267
291
|
onTimerFired: (entry) => handleTimerFiredFromInternals(getInternals(this), entry, this.#createTimeOperationCallbacks()),
|
|
268
|
-
getNow
|
|
292
|
+
getNow,
|
|
293
|
+
commitTimerCleanup: (operations) => commitFencedEngineWrite(getInternals(this), operations, [], () => Error("Timer cleanup lost its fenced CAS race"))
|
|
269
294
|
});
|
|
270
295
|
getInternals(this).strategy = strategyBundle.strategy;
|
|
271
296
|
getInternals(this).inlineStrategy = strategyBundle.inlineStrategy;
|
|
@@ -278,8 +303,10 @@ export class Engine extends EventTarget {
|
|
|
278
303
|
if (queuedInlineWorkflowStartChannel !== null)
|
|
279
304
|
queuedInlineWorkflowStartChannel.port1.onmessage = createQueuedInlineWorkflowStartHandler(weakEngine, queuedInlineWorkflowStartChannel);
|
|
280
305
|
getInternals(this).heartbeatDetails = new Map;
|
|
306
|
+
getInternals(this).lastHeartbeatDetailsByStep = new Map;
|
|
281
307
|
getInternals(this).workflowServices = new Map;
|
|
282
308
|
getInternals(this).pendingAsyncActivities = new Map;
|
|
309
|
+
getInternals(this).pendingAsyncActivityResolutions = new Map;
|
|
283
310
|
getInternals(this).pendingStarts = new Set;
|
|
284
311
|
getInternals(this).pendingScheduleCreations = new Set;
|
|
285
312
|
getInternals(this).workflowsNeedingTerminalCleanup = new Set;
|
|
@@ -290,10 +317,12 @@ export class Engine extends EventTarget {
|
|
|
290
317
|
getInternals(this).workflowReviewIds = new Map;
|
|
291
318
|
getInternals(this).parkedInlineWorkflows = new Set;
|
|
292
319
|
getInternals(this).terminalizingWorkflows = new Set;
|
|
320
|
+
getInternals(this).deliveredPendingUpdateIds = new Map;
|
|
293
321
|
getInternals(this).cancelHandlersByWorkflow = new Map;
|
|
294
322
|
getInternals(this).reviewTimerIds = new Map;
|
|
295
323
|
getInternals(this).pendingWebhooks = new Set;
|
|
296
324
|
getInternals(this).pendingTimelineEntries = new Map;
|
|
325
|
+
getInternals(this).pendingAtomicWorkflowCommitSideEffects = new Map;
|
|
297
326
|
getInternals(this).cleanupIntervalDisposalTracker = null;
|
|
298
327
|
const cleanupIntervalDisposalTracker = {
|
|
299
328
|
disposed: !1,
|
|
@@ -310,15 +339,90 @@ export class Engine extends EventTarget {
|
|
|
310
339
|
getInternals(this).nextRetentionSweepAt = null;
|
|
311
340
|
getInternals(this).secondInstanceDetectionInterval = null;
|
|
312
341
|
getInternals(this).secondInstanceDetector = null;
|
|
342
|
+
getInternals(this).leaseManager = null;
|
|
343
|
+
getInternals(this).inFlightLeaseAcquire = null;
|
|
344
|
+
getInternals(this).deposed = !1;
|
|
345
|
+
getInternals(this).tearDownAfterDeposition = () => {
|
|
346
|
+
this.#disposeAfterDeposition();
|
|
347
|
+
};
|
|
313
348
|
getInternals(this).eventLogHeads = new Map;
|
|
314
349
|
getInternals(this).workflowFeedListeners = new Map;
|
|
315
350
|
getInternals(this).workflowVersionTuples = new Map;
|
|
351
|
+
getInternals(this).workflowVisibilityWatermark = void 0;
|
|
352
|
+
getInternals(this).workflowVisibilityWatermarkExpiresAt = void 0;
|
|
316
353
|
getInternals(this).activityWorkerDispatcher = createActivityWorkerDispatcher(options?.activityExecution);
|
|
317
354
|
getInternals(this).strategy.onMessage(this.#handleStrategyMessage.bind(this));
|
|
318
355
|
getInternals(this).alertManager = createAlertManagerForEngine(this, options?.alerts, getNow);
|
|
319
356
|
this.#ensureRetentionSweepInterval();
|
|
320
357
|
this.#startSecondInstanceDetection();
|
|
321
358
|
}
|
|
359
|
+
addEventListener(type, listener, options) {
|
|
360
|
+
super.addEventListener(type, listener, options);
|
|
361
|
+
}
|
|
362
|
+
removeEventListener(type, listener, options) {
|
|
363
|
+
super.removeEventListener(type, listener, options);
|
|
364
|
+
}
|
|
365
|
+
async#acquireLeaseIfConfigured() {
|
|
366
|
+
const internals = getInternals(this);
|
|
367
|
+
if (internals.options.ownershipMode !== "lease")
|
|
368
|
+
return;
|
|
369
|
+
if (internals.disposed)
|
|
370
|
+
throw new EngineDisposedError;
|
|
371
|
+
if (internals.inFlightLeaseAcquire !== null) {
|
|
372
|
+
await internals.inFlightLeaseAcquire;
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
if (internals.leaseManager !== null)
|
|
376
|
+
return;
|
|
377
|
+
requireStorageCapability(internals.storage, "conditionalBatch", "ownership: 'lease'");
|
|
378
|
+
const manager = createLeaseManager({
|
|
379
|
+
storage: internals.storage,
|
|
380
|
+
holderId: crypto.randomUUID(),
|
|
381
|
+
getNow: internals.options.getNow,
|
|
382
|
+
ttlMs: internals.options.leaseTtlMs,
|
|
383
|
+
renewIntervalMs: internals.options.leaseRenewIntervalMs,
|
|
384
|
+
waitTimeoutMs: internals.options.leaseWaitTimeoutMs,
|
|
385
|
+
onLeaseLost: (reason) => {
|
|
386
|
+
if (reason === "deposed") {
|
|
387
|
+
handleDeposition(internals);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
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);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
internals.leaseManager = manager;
|
|
394
|
+
const acquisition = (async () => {
|
|
395
|
+
try {
|
|
396
|
+
await manager.acquire();
|
|
397
|
+
} catch (error) {
|
|
398
|
+
if (internals.leaseManager === manager)
|
|
399
|
+
internals.leaseManager = null;
|
|
400
|
+
throw error;
|
|
401
|
+
}
|
|
402
|
+
if (internals.disposed) {
|
|
403
|
+
await manager.release();
|
|
404
|
+
if (internals.leaseManager === manager)
|
|
405
|
+
internals.leaseManager = null;
|
|
406
|
+
throw new EngineDisposedError;
|
|
407
|
+
}
|
|
408
|
+
manager.startRenewal();
|
|
409
|
+
})();
|
|
410
|
+
internals.inFlightLeaseAcquire = acquisition;
|
|
411
|
+
try {
|
|
412
|
+
await acquisition;
|
|
413
|
+
} finally {
|
|
414
|
+
if (internals.inFlightLeaseAcquire === acquisition)
|
|
415
|
+
internals.inFlightLeaseAcquire = null;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
#disposeAfterDeposition() {
|
|
419
|
+
const internals = getInternals(this);
|
|
420
|
+
if (internals.disposed)
|
|
421
|
+
return;
|
|
422
|
+
const leaseManager = internals.leaseManager;
|
|
423
|
+
disposeEngine(internals);
|
|
424
|
+
leaseManager?.release().catch(() => {});
|
|
425
|
+
}
|
|
322
426
|
#startSecondInstanceDetection() {
|
|
323
427
|
const internals = getInternals(this);
|
|
324
428
|
if (!internals.options.secondInstanceDetectionEnabled)
|
|
@@ -437,11 +541,13 @@ export class Engine extends EventTarget {
|
|
|
437
541
|
return getInternals(this).activityRegistry.listDefinitions();
|
|
438
542
|
}
|
|
439
543
|
async start(type, input, options) {
|
|
544
|
+
assertLeaseHeldForEngineWork(getInternals(this));
|
|
440
545
|
if (options?.idempotencyKey !== void 0)
|
|
441
546
|
return startWithIdempotencyFromLifecycle(getInternals(this), type, input, options, this.#createLifecycleCallbacks());
|
|
442
547
|
return startWorkflowFromLifecycle(getInternals(this), type, input, options, void 0, this.#createLifecycleCallbacks());
|
|
443
548
|
}
|
|
444
549
|
async startOrSignal(type, input, signal, options) {
|
|
550
|
+
assertLeaseHeldForEngineWork(getInternals(this));
|
|
445
551
|
return startOrSignalFromLifecycle(getInternals(this), type, input, signal, options, this.#createStartOrSignalCallbacks());
|
|
446
552
|
}
|
|
447
553
|
#createStartOrSignalCallbacks() {
|
|
@@ -474,6 +580,9 @@ export class Engine extends EventTarget {
|
|
|
474
580
|
async cancelAll(filter, options) {
|
|
475
581
|
return cancelAllWorkflows(getInternals(this), filter, options);
|
|
476
582
|
}
|
|
583
|
+
async retryFailedAll(filter, options) {
|
|
584
|
+
return retryFailedAllWorkflows(getInternals(this), filter, options);
|
|
585
|
+
}
|
|
477
586
|
async signalAll(filter, name, payloadOrOptions, options) {
|
|
478
587
|
if (options === void 0)
|
|
479
588
|
return signalAllWorkflows(getInternals(this), filter, name, payloadOrOptions);
|
|
@@ -494,7 +603,8 @@ export class Engine extends EventTarget {
|
|
|
494
603
|
return scheduleFromInternals(getInternals(this), workflowType, definition.input, definitionSpec, {
|
|
495
604
|
...definition.id !== void 0 && { id: definition.id },
|
|
496
605
|
...definition.overlapPolicy !== void 0 && { overlap: definition.overlapPolicy },
|
|
497
|
-
...definition.backfill !== void 0 && { backfill: definition.backfill }
|
|
606
|
+
...definition.backfill !== void 0 && { backfill: definition.backfill },
|
|
607
|
+
...definition.jitter !== void 0 && { jitter: definition.jitter }
|
|
498
608
|
});
|
|
499
609
|
}
|
|
500
610
|
if (spec === void 0)
|
|
@@ -554,20 +664,32 @@ export class Engine extends EventTarget {
|
|
|
554
664
|
return getOffloadFromInternals(getInternals(this), workflowId, key);
|
|
555
665
|
}
|
|
556
666
|
async fork(sourceWorkflowId, options) {
|
|
667
|
+
assertLeaseHeldForEngineWork(getInternals(this));
|
|
557
668
|
return forkFromLifecycle(getInternals(this), sourceWorkflowId, options, this.#createLifecycleCallbacks());
|
|
558
669
|
}
|
|
559
670
|
async resume(workflowId) {
|
|
671
|
+
assertLeaseHeldForEngineWork(getInternals(this));
|
|
560
672
|
return resumeFromLifecycle(getInternals(this), workflowId, this.#createLifecycleCallbacks());
|
|
561
673
|
}
|
|
562
674
|
async recoverAll(options) {
|
|
675
|
+
await this.#acquireLeaseIfConfigured();
|
|
676
|
+
if (getInternals(this).disposed)
|
|
677
|
+
throw new EngineDisposedError;
|
|
563
678
|
await recoverPendingAsyncActivities(getInternals(this));
|
|
679
|
+
await recoverOrphanedScheduleTimers(getInternals(this));
|
|
564
680
|
return recoverAllFromLifecycle(getInternals(this), this.#createLifecycleCallbacks(), options);
|
|
565
681
|
}
|
|
566
682
|
async completeAsyncActivity(token, result) {
|
|
567
|
-
await completeAsyncActivityFromInternals(getInternals(this), token, result,
|
|
683
|
+
await completeAsyncActivityFromInternals(getInternals(this), token, result, {
|
|
684
|
+
feedOperationResult: (workflowId, outcome) => feedOperationResult(getInternals(this), workflowId, outcome),
|
|
685
|
+
finalizeTimeline: (workflowId, status, output) => finalizePendingTimelineEntry(getInternals(this), workflowId, status, output)
|
|
686
|
+
});
|
|
568
687
|
}
|
|
569
688
|
async failAsyncActivity(token, error) {
|
|
570
|
-
await failAsyncActivityFromInternals(getInternals(this), token, error,
|
|
689
|
+
await failAsyncActivityFromInternals(getInternals(this), token, error, {
|
|
690
|
+
feedOperationResult: (workflowId, outcome, originalReason) => feedOperationResult(getInternals(this), workflowId, outcome, originalReason),
|
|
691
|
+
finalizeTimeline: (workflowId, status, output) => finalizePendingTimelineEntry(getInternals(this), workflowId, status, output)
|
|
692
|
+
});
|
|
571
693
|
}
|
|
572
694
|
async cancel(workflowId) {
|
|
573
695
|
await cancelWorkflowFromTermination(getInternals(this), workflowId, this.#createTerminationCallbacks());
|
|
@@ -647,14 +769,19 @@ export class Engine extends EventTarget {
|
|
|
647
769
|
return submitCoordinatedUpdateFromInternals(getInternals(this), workflowId, name, payload, options, this.#createUpdateCallbacks());
|
|
648
770
|
}
|
|
649
771
|
[Symbol.dispose]() {
|
|
772
|
+
const leaseManager = getInternals(this).leaseManager;
|
|
650
773
|
disposeEngine(getInternals(this));
|
|
774
|
+
leaseManager?.release();
|
|
651
775
|
}
|
|
652
776
|
async[Symbol.asyncDispose]() {
|
|
653
777
|
if (!getInternals(this).disposed) {
|
|
778
|
+
const leaseManager = getInternals(this).leaseManager;
|
|
654
779
|
try {
|
|
655
780
|
await drainQueuedInlineWorkflowStartsForEngine(this);
|
|
656
781
|
} finally {
|
|
657
|
-
this
|
|
782
|
+
disposeEngine(getInternals(this));
|
|
783
|
+
await getInternals(this).inFlightLeaseAcquire?.catch(() => {});
|
|
784
|
+
await leaseManager?.release();
|
|
658
785
|
}
|
|
659
786
|
return;
|
|
660
787
|
}
|
|
@@ -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
|
+
}
|