@lostgradient/weft 0.2.1 → 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 +123 -43
- package/dist/cli/conformance.js +7 -1
- package/dist/cli/generated/operation-client.generated.d.ts +55 -13
- package/dist/cli/generated/operation-client.generated.js +3 -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 +80 -79
- package/dist/client/handle-delegation.d.ts +7 -2
- package/dist/client/handle-delegation.js +9 -1
- package/dist/client/http-client-requests.d.ts +4 -1
- package/dist/client/http-client-requests.js +10 -0
- package/dist/client/http-client.d.ts +10 -4
- package/dist/client/http-client.js +18 -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 +100 -4
- package/dist/client/local.d.ts +7 -1
- package/dist/client/local.js +17 -2
- package/dist/client/start-body.d.ts +9 -2
- package/dist/client/start-body.js +13 -4
- 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 +104 -8
- package/dist/core/codec/index.d.ts +1 -0
- package/dist/core/codec/index.js +1 -0
- package/dist/core/codec/serializer-registry.d.ts +122 -0
- package/dist/core/codec/serializer-registry.js +51 -0
- 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 +18 -5
- package/dist/core/context/index.js +17 -12
- package/dist/core/context/internals.d.ts +29 -0
- package/dist/core/context/internals.js +6 -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 +21 -3
- package/dist/core/context/run-operation.js +56 -93
- 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 -1
- package/dist/core/engine/construction.js +30 -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 +25 -0
- package/dist/core/engine/engine-create-types.d.ts +14 -18
- package/dist/core/engine/engine-internal-types.d.ts +38 -3
- package/dist/core/engine/engine-leak-warnings.d.ts +6 -0
- package/dist/core/engine/engine-leak-warnings.js +4 -0
- package/dist/core/engine/engine-runtime-helpers.d.ts +17 -0
- package/dist/core/engine/engine-runtime-helpers.js +26 -5
- package/dist/core/engine/errors.d.ts +121 -0
- package/dist/core/engine/errors.js +45 -1
- 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/handle-result.js +1 -1
- package/dist/core/engine/handles.d.ts +97 -40
- package/dist/core/engine/handles.js +25 -27
- package/dist/core/engine/index.d.ts +125 -15
- package/dist/core/engine/index.js +207 -11
- package/dist/core/engine/inline-launch-queue.d.ts +14 -0
- package/dist/core/engine/inline-launch-queue.js +32 -7
- package/dist/core/engine/inline-parking.js +3 -2
- package/dist/core/engine/internals.d.ts +125 -13
- 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/fork-helpers.js +1 -7
- package/dist/core/engine/lifecycle/persist.js +15 -52
- 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 +39 -11
- 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 +56 -0
- package/dist/core/engine/lifecycle/start-commit.js +69 -0
- package/dist/core/engine/lifecycle/start-exec.d.ts +30 -2
- package/dist/core/engine/lifecycle/start-exec.js +38 -0
- 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-resolution.d.ts +79 -0
- package/dist/core/engine/lifecycle/start-or-signal-resolution.js +60 -0
- package/dist/core/engine/lifecycle/start-or-signal.d.ts +46 -0
- package/dist/core/engine/lifecycle/start-or-signal.js +71 -0
- 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 +6 -6
- package/dist/core/engine/lifecycle/start.js +47 -45
- package/dist/core/engine/lifecycle/transition.js +17 -5
- package/dist/core/engine/lifecycle.d.ts +3 -2
- package/dist/core/engine/lifecycle.js +9 -2
- package/dist/core/engine/list-candidate-resolution.js +2 -2
- package/dist/core/engine/listing.js +10 -6
- 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/persisted-data-version.d.ts +5 -9
- package/dist/core/engine/persisted-data-version.js +4 -5
- 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-handle.d.ts +45 -0
- package/dist/core/engine/schedule-handle.js +26 -0
- 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 +8 -5
- package/dist/core/engine/schedules.js +44 -19
- 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/second-instance-detector.d.ts +96 -0
- package/dist/core/engine/second-instance-detector.js +108 -0
- package/dist/core/engine/signals.d.ts +38 -0
- package/dist/core/engine/signals.js +55 -19
- 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 +28 -1
- package/dist/core/engine/termination/cleanup.js +44 -2
- package/dist/core/engine/termination/complete.js +48 -9
- 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 +70 -0
- package/dist/core/engine/termination/suspend.js +41 -0
- package/dist/core/engine/termination.d.ts +5 -2
- package/dist/core/engine/termination.js +6 -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 +54 -3
- 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 +9 -8
- 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 +19 -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 +108 -20
- package/dist/core/events/workflow-events.js +35 -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/list-filter-validation.js +2 -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 +56 -0
- package/dist/core/start-workflow-validation.js +30 -1
- package/dist/core/step-context.d.ts +10 -6
- package/dist/core/step-context.js +7 -15
- 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 +86 -64
- package/dist/core/types/bulk.d.ts +50 -9
- package/dist/core/types/checkpoint.d.ts +44 -8
- package/dist/core/types/identity.d.ts +8 -1
- package/dist/core/types/launch-metadata.d.ts +33 -0
- package/dist/core/types/launch-metadata.js +0 -0
- package/dist/core/types/list-options.d.ts +146 -0
- package/dist/core/types/list-options.js +0 -0
- package/dist/core/types/message-handles.d.ts +25 -0
- package/dist/core/types/options.d.ts +165 -215
- package/dist/core/types/reviews.d.ts +2 -1
- package/dist/core/types/schedules.d.ts +39 -3
- package/dist/core/types/services-resolution.d.ts +47 -0
- package/dist/core/types/services-resolution.js +0 -0
- package/dist/core/types/state.d.ts +11 -11
- 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 +47 -15
- 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 +101 -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/workflow-snapshot.d.ts +29 -0
- package/dist/core/types/workflow-snapshot.js +0 -0
- package/dist/core/types.d.ts +7 -0
- package/dist/core/types.js +7 -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 +19 -1
- 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/doctor.js +6 -3
- package/dist/diagnostics/format.js +3 -8
- package/dist/diagnostics/types.d.ts +5 -5
- package/dist/diagnostics/validate.d.ts +0 -1
- package/dist/diagnostics/version-check.d.ts +1 -1
- package/dist/diagnostics/version-check.js +10 -13
- package/dist/index.d.ts +13 -11
- package/dist/index.js +39 -4
- package/dist/json-schema.js +3 -3
- package/dist/mcp/cli.js +35 -35
- package/dist/mcp/dispatcher.js +1 -1
- package/dist/mcp/http.js +16 -6
- package/dist/mcp/list-filter.js +2 -1
- 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 +60 -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 +31 -31
- package/dist/server/index.d.ts +32 -2
- package/dist/server/index.js +35 -35
- package/dist/server/interactive-operations.js +1 -0
- 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/resume-workflow.js +2 -2
- 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 +44 -0
- package/dist/server/operations/start-or-signal-workflow.js +130 -0
- package/dist/server/operations/start-workflow-options.d.ts +38 -0
- package/dist/server/operations/start-workflow-options.js +63 -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 +15 -92
- 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/suspend-workflow.d.ts +13 -0
- package/dist/server/operations/suspend-workflow.js +36 -0
- 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 +21 -8
- package/dist/server/rest-bindings.js +24 -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-dispatch.js +5 -3
- package/dist/server/runtime/task-polling.d.ts +16 -2
- package/dist/server/runtime/task-polling.js +80 -24
- 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 +46 -30
- package/dist/server/serve-internals.d.ts +11 -1
- package/dist/server/serve-internals.js +20 -2
- package/dist/server/task-state.d.ts +38 -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 +31 -31
- package/dist/storage/auto.js +1 -1
- package/dist/storage/bun-sql.js +24 -0
- package/dist/storage/capabilities.d.ts +10 -2
- package/dist/storage/capabilities.js +2 -2
- 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 +81 -4
- package/dist/storage/indexeddb.js +1 -1
- package/dist/storage/interface.d.ts +189 -0
- package/dist/storage/interface.js +1 -1
- package/dist/storage/key-prefixes.d.ts +1 -1
- package/dist/storage/key-prefixes.js +12 -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 +74 -0
- package/dist/storage/neon-value-mapping.js +23 -0
- package/dist/storage/neon.d.ts +124 -0
- package/dist/storage/neon.js +15 -0
- package/dist/storage/node-sqlite-loader.d.ts +71 -0
- package/dist/storage/node-sqlite-loader.js +41 -0
- package/dist/storage/node-sqlite.d.ts +1 -19
- package/dist/storage/node-sqlite.js +61 -32
- package/dist/storage/postgres-key-value-queries.d.ts +104 -0
- package/dist/storage/postgres-key-value-queries.js +101 -0
- package/dist/storage/resolve.d.ts +2 -165
- package/dist/storage/resolve.js +1 -1
- package/dist/storage/scoped-storage.js +1 -1
- package/dist/storage/storage-configuration.d.ts +209 -0
- package/dist/storage/storage-configuration.js +0 -0
- package/dist/storage/testing.d.ts +168 -0
- package/dist/storage/testing.js +2 -0
- package/dist/storage/text-value-store.d.ts +9 -9
- 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 +35 -35
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker/index.js +9 -5
- package/dist/worker/long-poll.js +4 -0
- package/dist/worker/protocol-messages.d.ts +20 -0
- package/dist/worker/protocol-schemas.d.ts +32 -0
- package/dist/worker/protocol-schemas.js +8 -4
- package/dist/worker/protocol-task-result.d.ts +28 -0
- package/dist/worker/protocol-task-result.js +76 -0
- package/dist/worker/protocol.d.ts +4 -15
- package/dist/worker/protocol.js +1 -1
- package/dist/worker/registry/fair-share.d.ts +29 -0
- package/dist/worker/registry/fair-share.js +30 -0
- package/dist/worker/registry/routing.d.ts +18 -0
- package/dist/worker/registry/routing.js +14 -0
- package/dist/worker/registry/types.d.ts +7 -0
- package/dist/worker/registry.d.ts +16 -1
- package/dist/worker/registry.js +24 -36
- 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 +21 -4
|
@@ -28,13 +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
|
-
import type {
|
|
35
|
+
import type { EngineCleanupIntervalDisposalTracker } from './engine-leak-warnings.ts';
|
|
36
|
+
import type { WorkflowHandle, WorkflowHandleEngine } from './handles.ts';
|
|
36
37
|
import type { WorkflowFeedListener } from './index.ts';
|
|
37
|
-
type
|
|
38
|
+
import type { LeaseManager } from './lease-manager.ts';
|
|
39
|
+
import type { ScheduleHandleEngine } from './schedule-handle.ts';
|
|
40
|
+
import type { SecondInstanceDetector } from './second-instance-detector.ts';
|
|
41
|
+
type EngineRuntime = WorkflowHandleEngine & ScheduleHandleEngine & {
|
|
42
|
+
start(type: string, input: unknown, options?: StartWorkflowOptions): Promise<WorkflowHandle>;
|
|
43
|
+
};
|
|
38
44
|
export interface EngineInternals {
|
|
39
45
|
engine: EngineRuntime;
|
|
40
46
|
storage: WeftStorage;
|
|
@@ -64,6 +70,23 @@ export interface EngineInternals {
|
|
|
64
70
|
resultResolvers: Map<string, WorkflowResultWaiter>;
|
|
65
71
|
signalWaiters: Map<string, () => void>;
|
|
66
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>;
|
|
67
90
|
updateWaiters: Map<string, (payload: unknown) => void>;
|
|
68
91
|
updateWaitersByWorkflow: Map<string, TrackedWaiterKeys>;
|
|
69
92
|
sleepResolvers: Map<string, () => void>;
|
|
@@ -75,11 +98,15 @@ export interface EngineInternals {
|
|
|
75
98
|
activityRegistry: ActivityRegistry;
|
|
76
99
|
/**
|
|
77
100
|
* Per-workflow activity registries built from
|
|
78
|
-
* `workflow({ name }).activities({ ... }).execute(...)
|
|
79
|
-
* type.
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
101
|
+
* `workflow({ name }).activities({ ... }).execute(...)`, indexed by workflow
|
|
102
|
+
* type. Activity lookup is per-activity: it consults the workflow's
|
|
103
|
+
* `activityRegistriesByWorkflow.get(type)` registry first and, for any activity
|
|
104
|
+
* that registry does not contain, falls back to the engine-wide
|
|
105
|
+
* {@link EngineInternals.activityRegistry}. Both sources are first-class — a
|
|
106
|
+
* workflow can resolve some activities from its own `activities(...)` map and
|
|
107
|
+
* others (shared/globally-registered) from the global registry, and a workflow
|
|
108
|
+
* with no per-workflow map resolves entirely from the global one. Each
|
|
109
|
+
* per-workflow registry is a defensive deep clone+freeze of the workflow's
|
|
83
110
|
* `activities` map so post-registration mutation cannot reach the engine.
|
|
84
111
|
*/
|
|
85
112
|
activityRegistriesByWorkflow: Map<string, ActivityRegistry>;
|
|
@@ -103,11 +130,25 @@ export interface EngineInternals {
|
|
|
103
130
|
broadcastChannel: BroadcastChannel | null;
|
|
104
131
|
pendingNestingDepth: number | undefined;
|
|
105
132
|
pendingParentHeaders: Map<string, string> | undefined;
|
|
106
|
-
pendingExecutionStateOwnerId: string | undefined;
|
|
133
|
+
pendingExecutionStateOwnerId: string | null | undefined;
|
|
107
134
|
workflowNestingDepths: Map<string, number>;
|
|
108
135
|
workflowHeaders: Map<string, Map<string, string>>;
|
|
109
136
|
workflowStateWriteChains: Map<string, Promise<void>>;
|
|
110
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>>;
|
|
111
152
|
/**
|
|
112
153
|
* Per-run, non-serialized `services` value exposed to the workflow body as
|
|
113
154
|
* `ctx.services`. Set at `engine.start({ services })` and re-provided on
|
|
@@ -123,17 +164,63 @@ export interface EngineInternals {
|
|
|
123
164
|
* reloaded by `recoverAll()`. See `async-activity-completion.ts`.
|
|
124
165
|
*/
|
|
125
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[]>;
|
|
126
173
|
pendingStarts: Set<string>;
|
|
127
174
|
pendingScheduleCreations: Set<string>;
|
|
128
175
|
workflowsNeedingTerminalCleanup: Set<string>;
|
|
129
176
|
cleanupInterval: ReturnType<typeof setInterval> | null;
|
|
130
|
-
cleanupIntervalDisposalTracker:
|
|
131
|
-
disposed: boolean;
|
|
132
|
-
cleanupInterval: ReturnType<typeof setInterval> | null;
|
|
133
|
-
} | null;
|
|
177
|
+
cleanupIntervalDisposalTracker: EngineCleanupIntervalDisposalTracker | null;
|
|
134
178
|
retentionSweepInterval: ReturnType<typeof setInterval> | null;
|
|
135
179
|
retentionSweepInFlight: Promise<void> | null;
|
|
136
180
|
nextRetentionSweepAt: number | null;
|
|
181
|
+
/** Interval driving the best-effort second-instance detector; `null` when off. */
|
|
182
|
+
secondInstanceDetectionInterval: ReturnType<typeof setInterval> | null;
|
|
183
|
+
/** The active second-instance detector; `null` when detection is disabled. */
|
|
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;
|
|
137
224
|
reviewCoordinator: ReviewCoordinator;
|
|
138
225
|
reviewWaiters: Map<string, (decision: HumanReviewResult) => void>;
|
|
139
226
|
reviewWaitersByWorkflow: Map<string, TrackedWaiterKeys>;
|
|
@@ -144,6 +231,22 @@ export interface EngineInternals {
|
|
|
144
231
|
workflowReviewIds: Map<string, Set<string>>;
|
|
145
232
|
parkedInlineWorkflows: Set<string>;
|
|
146
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>>;
|
|
147
250
|
cancelHandlersByWorkflow: Map<string, Array<() => Promise<void> | void>>;
|
|
148
251
|
reviewTimerIds: Map<string, string[]>;
|
|
149
252
|
pendingWebhooks: Set<AbortController>;
|
|
@@ -151,7 +254,16 @@ export interface EngineInternals {
|
|
|
151
254
|
eventLogHeads: Map<string, Readonly<EventHeadRecord>>;
|
|
152
255
|
workflowFeedListeners: Map<string, Set<WorkflowFeedListener>>;
|
|
153
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;
|
|
154
265
|
pendingTimelineEntries: Map<string, PendingTimelineEntry>;
|
|
266
|
+
pendingAtomicWorkflowCommitSideEffects: Map<string, import('./checkpoint-side-effects.ts').AtomicWorkflowCommitSideEffects>;
|
|
155
267
|
}
|
|
156
268
|
/**
|
|
157
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
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { WeftError } from '../weft-error.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when `ownership: 'lease'` is configured and the engine cannot acquire the
|
|
4
|
+
* storage lease within the configured wait window — another live instance still
|
|
5
|
+
* holds it. Raised from the lease-acquire step, which runs both inside
|
|
6
|
+
* {@link Engine.create} and at the top of {@link Engine.recoverAll} (so a
|
|
7
|
+
* `new Engine({ ownership: 'lease' })` + `recoverAll()` boot must handle it too).
|
|
8
|
+
* In a rolling deploy this means the
|
|
9
|
+
* outgoing instance has not released the lease (or its lease has not yet expired)
|
|
10
|
+
* within `leaseWaitTimeout`; size that window above the outgoing instance's drain
|
|
11
|
+
* time and above the lease TTL so a crash (no clean release) still resolves once
|
|
12
|
+
* the held lease expires. Inspect `waitedMs` for how long this instance waited
|
|
13
|
+
* and `heldBy` for the holder id observed when it gave up (`null` if unknown).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { EngineLeaseAcquisitionTimeoutError } from '@lostgradient/weft';
|
|
18
|
+
*
|
|
19
|
+
* function isLeaseHandoffStuck(error: unknown): boolean {
|
|
20
|
+
* return error instanceof EngineLeaseAcquisitionTimeoutError;
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare class EngineLeaseAcquisitionTimeoutError extends WeftError<'EngineLeaseAcquisitionTimeoutError'> {
|
|
25
|
+
readonly waitedMs: number;
|
|
26
|
+
readonly heldBy: string | null;
|
|
27
|
+
constructor(waitedMs: number, heldBy: string | null);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Thrown when `ownership: 'lease'` is configured but the lease keys in storage are
|
|
31
|
+
* corrupt. Raised from the lease-acquire step, which runs both inside
|
|
32
|
+
* {@link Engine.create} and at the top of {@link Engine.recoverAll} (so a
|
|
33
|
+
* `new Engine({ ownership: 'lease' })` + `recoverAll()` boot must handle it too) —
|
|
34
|
+
* the `lease:epoch` high-water mark is present
|
|
35
|
+
* but does not decode to a valid epoch, or a holder record exists with no epoch
|
|
36
|
+
* key (which the lease protocol never produces, since `release()` deletes only the
|
|
37
|
+
* holder and never the epoch). The lease epoch is the sole source of truth for the
|
|
38
|
+
* monotonic fencing generation, so a booting instance fails closed rather than
|
|
39
|
+
* re-minting a generation at or below the true high-water mark and risking a
|
|
40
|
+
* split-brain. Resolve by operator repair: delete both `lease:` keys only if you
|
|
41
|
+
* are certain no other instance is running, or reset `lease:epoch` above its last
|
|
42
|
+
* known value.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* import { EngineLeaseCorruptedError } from '@lostgradient/weft';
|
|
47
|
+
*
|
|
48
|
+
* function isLeaseCorrupted(error: unknown): boolean {
|
|
49
|
+
* return error instanceof EngineLeaseCorruptedError;
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare class EngineLeaseCorruptedError extends WeftError<'EngineLeaseCorruptedError'> {
|
|
54
|
+
constructor(detail: string);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Thrown by {@link Engine.start} and {@link Engine.startOrSignal} when
|
|
58
|
+
* `ownership: 'lease'` is configured but the engine does not yet hold the lease —
|
|
59
|
+
* it was constructed with `new Engine({ ownership: 'lease' })` and used to start
|
|
60
|
+
* work before `recoverAll()` ran, or created with `recover: false`. In lease mode
|
|
61
|
+
* an engine must hold the single-writer lease (and have recovered existing runs)
|
|
62
|
+
* before it durably writes new workflow state, or a fresh start could race an
|
|
63
|
+
* instance that legitimately owns the store. {@link Engine.create} acquires the
|
|
64
|
+
* lease and recovers for you; a direct constructor must `await engine.recoverAll()`
|
|
65
|
+
* first.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* import { Engine, EngineLeaseNotHeldError } from '@lostgradient/weft';
|
|
70
|
+
*
|
|
71
|
+
* const engine = new Engine({ ownership: 'lease' });
|
|
72
|
+
* try {
|
|
73
|
+
* await engine.start('ping', null);
|
|
74
|
+
* } catch (error) {
|
|
75
|
+
* if (error instanceof EngineLeaseNotHeldError) {
|
|
76
|
+
* await engine.recoverAll(); // acquires the lease, then retry the start
|
|
77
|
+
* }
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export declare class EngineLeaseNotHeldError extends WeftError<'EngineLeaseNotHeldError'> {
|
|
82
|
+
constructor();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Thrown internally on the durable-write path when `ownership: 'lease'` is
|
|
86
|
+
* configured and a fenced write's CAS fails because the held lease epoch is
|
|
87
|
+
* stale — a successor instance has taken the lease and bumped the epoch, so this
|
|
88
|
+
* instance has been deposed. It is the local unwind that stops the commit from
|
|
89
|
+
* advancing in-memory state past a durable write that did not land; the actual
|
|
90
|
+
* "this engine is deposed" reaction (set the `deposed` flag, warn the operator,
|
|
91
|
+
* tear the engine down) is driven by {@link handleDeposition} at the detection
|
|
92
|
+
* site, not by this error propagating — the inline strategy swallows turn
|
|
93
|
+
* rejections, so the throw never reaches a handler. Internal-only: it is never
|
|
94
|
+
* surfaced to user code (deposition reaches operators through the
|
|
95
|
+
* `WeftEngineLeaseLostWarning` process warning).
|
|
96
|
+
*/
|
|
97
|
+
export declare class EngineDeposedError extends WeftError<'EngineDeposedError'> {
|
|
98
|
+
constructor();
|
|
99
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { WeftError } from "../weft-error.js";
|
|
2
|
+
|
|
3
|
+
export class EngineLeaseAcquisitionTimeoutError extends WeftError {
|
|
4
|
+
waitedMs;
|
|
5
|
+
heldBy;
|
|
6
|
+
constructor(waitedMs, heldBy) {
|
|
7
|
+
const heldClause = heldBy === null ? "" : ` (currently held by "${heldBy}")`;
|
|
8
|
+
super("EngineLeaseAcquisitionTimeoutError", `Could not acquire the storage ownership lease within ${waitedMs}ms${heldClause}. Another engine instance still holds it. Increase leaseWaitTimeout above the outgoing instance's drain time and the lease TTL, or ensure the previous instance releases the lease on shutdown.`);
|
|
9
|
+
this.waitedMs = waitedMs;
|
|
10
|
+
this.heldBy = heldBy;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class EngineLeaseCorruptedError extends WeftError {
|
|
15
|
+
constructor(detail) {
|
|
16
|
+
super("EngineLeaseCorruptedError", `The ownership lease state in storage is corrupt and cannot be used safely: ${detail}. Resolve by operator repair (delete both "lease:" keys only if no other instance is running, or reset "lease:epoch" above its last known value).`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class EngineLeaseNotHeldError extends WeftError {
|
|
21
|
+
constructor() {
|
|
22
|
+
super("EngineLeaseNotHeldError", "ownership: 'lease' requires the engine to hold the lease before starting work. Use `Engine.create({ ownership: \"lease\" })` (which acquires the lease and recovers), or call `await engine.recoverAll()` on a directly-constructed engine before `start()`.");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class EngineDeposedError extends WeftError {
|
|
27
|
+
constructor() {
|
|
28
|
+
super("EngineDeposedError", "Engine was deposed: a fenced durable write lost its CAS race because another instance now holds the ownership lease at a newer epoch. This engine is halting.");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lease-fenced single-writer ownership over a shared durable store. This is the
|
|
3
|
+
* opt-in `Engine.create({ ownership: 'lease' })` mechanism — a CORRECTNESS-PATH
|
|
4
|
+
* coordinator, NOT a best-effort smoke alarm like the second-instance detector.
|
|
5
|
+
* Lease outcomes are acted on, never silently dropped, but the channel differs by
|
|
6
|
+
* phase: a failed acquire THROWS and blocks recovery (corruption fails closed, a
|
|
7
|
+
* timed-out handoff raises a typed error); a renewal reports loss through
|
|
8
|
+
* `onLeaseLost` rather than throwing — a CAS-failed renewal means a successor took
|
|
9
|
+
* the lease ('deposed'), and a transient renewal storage error is tolerated until
|
|
10
|
+
* the lease is too close to lapsing to prove ownership ('renewal-unconfirmable').
|
|
11
|
+
* In Step 1 that loss is observability only (the engine warns); Step 2 will make it
|
|
12
|
+
* enforceable by fencing every durable write on the lease epoch.
|
|
13
|
+
*
|
|
14
|
+
* **Why a lease.** Weft's supported model is one engine process per durable
|
|
15
|
+
* store. Without a lease, a rolling deploy briefly runs two engines (old draining
|
|
16
|
+
* + new booting), both recovering and both issuing at-least-once activities. The
|
|
17
|
+
* lease turns that into a clean handoff: the booting instance ACQUIRES the lease
|
|
18
|
+
* before recovering, the draining instance RELEASES it on dispose, and the new
|
|
19
|
+
* instance only recovers once it owns the lease.
|
|
20
|
+
*
|
|
21
|
+
* **Two-key schema (this is load-bearing, not an optimization).** Ownership is
|
|
22
|
+
* tracked by two keys, never one:
|
|
23
|
+
*
|
|
24
|
+
* - `lease:epoch` — an 8-byte big-endian uint64 fencing token. It changes ONLY on
|
|
25
|
+
* ownership transfer (cold acquire, or a steal/re-acquire after the prior
|
|
26
|
+
* holder's lease lapsed), NEVER on a renewal. It is a monotonic high-water mark
|
|
27
|
+
* that SURVIVES release.
|
|
28
|
+
* - `lease:holder` — a JSON `{ holderId, expiresAt, epoch }` record renewed on
|
|
29
|
+
* every heartbeat (its `expiresAt` advances), so its bytes churn constantly.
|
|
30
|
+
*
|
|
31
|
+
* They are split because `conditionalBatch` compares the WHOLE stored value as
|
|
32
|
+
* bytes. If the epoch lived inside the churning holder record, the fencing token
|
|
33
|
+
* (Step 2) would change on every renewal, spuriously failing the fence and
|
|
34
|
+
* self-terminating healthy holders. Keeping the epoch in its own stable key lets
|
|
35
|
+
* a holder cache its epoch once ({@link LeaseManager.currentEpochBytes}) and use
|
|
36
|
+
* it unchanged as a fencing condition across many renewals.
|
|
37
|
+
*
|
|
38
|
+
* **Epoch monotonicity is the anti-split-brain invariant.** Every transfer
|
|
39
|
+
* conditions on BOTH the holder AND the current epoch and bumps the epoch by one;
|
|
40
|
+
* `release()` deletes ONLY the holder and never the epoch. This guarantees a
|
|
41
|
+
* deposed zombie from generation N can never see its epoch re-minted under it —
|
|
42
|
+
* so once Step 2 fences durable writes on the epoch, the zombie always loses.
|
|
43
|
+
*
|
|
44
|
+
* @module core/engine/lease-manager
|
|
45
|
+
*/
|
|
46
|
+
import { type Storage } from '../../storage/interface.ts';
|
|
47
|
+
/** Why a holder lost its lease — surfaced to {@link LeaseManagerOptions.onLeaseLost}. */
|
|
48
|
+
export type LeaseLostReason = 'deposed' | 'renewal-unconfirmable';
|
|
49
|
+
/** Options for {@link createLeaseManager}. */
|
|
50
|
+
export type LeaseManagerOptions = {
|
|
51
|
+
storage: Storage;
|
|
52
|
+
/** This engine's unique instance id (the lease holder id). */
|
|
53
|
+
holderId: string;
|
|
54
|
+
/** Wall-clock source (ms), injected so tests can advance time deterministically. */
|
|
55
|
+
getNow: () => number;
|
|
56
|
+
/** Lease time-to-live (ms). A stolen lease becomes available `ttlMs` after its last renewal. */
|
|
57
|
+
ttlMs: number;
|
|
58
|
+
/** Renewal interval (ms). The holder re-asserts the lease this often; must be `< ttlMs`. */
|
|
59
|
+
renewIntervalMs: number;
|
|
60
|
+
/** Boot-time wait window (ms) before {@link LeaseManager.acquire} throws. */
|
|
61
|
+
waitTimeoutMs: number;
|
|
62
|
+
/** Poll interval (ms) for the acquire wait loop. */
|
|
63
|
+
acquirePollIntervalMs?: number;
|
|
64
|
+
/**
|
|
65
|
+
* Delay primitive for the acquire poll loop. Defaults to a real `setTimeout`.
|
|
66
|
+
* Injected so tests drive the loop deterministically: a test `delay` advances
|
|
67
|
+
* the same injected clock by the poll interval and resolves immediately, so the
|
|
68
|
+
* wait deadline (read from `getNow`) trips without real waiting. Mirrors the
|
|
69
|
+
* `getNow` injection — not test-only scaffolding.
|
|
70
|
+
*/
|
|
71
|
+
delay?: (ms: number) => Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Called once when this holder loses the lease while running — either CAS-false
|
|
74
|
+
* on renewal (`'deposed'`: a successor stole it) or storage failures that make
|
|
75
|
+
* the holder unable to prove it still holds before the lease lapses
|
|
76
|
+
* (`'renewal-unconfirmable'`). In Step 1 the engine reacts by warning; Step 2
|
|
77
|
+
* uses this to halt fenced writes. The lease manager never throws into the
|
|
78
|
+
* renewal timer — it reports through this seam instead.
|
|
79
|
+
*/
|
|
80
|
+
onLeaseLost?: (reason: LeaseLostReason) => void;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* A running lease manager.
|
|
84
|
+
*
|
|
85
|
+
* - `acquire()` blocks until this instance owns the lease, then resolves; throws
|
|
86
|
+
* {@link EngineLeaseAcquisitionTimeoutError} on timeout. Call before recovery.
|
|
87
|
+
* One exception to "resolved ⇒ owned": if the manager was `stop()`ped (disposal)
|
|
88
|
+
* while acquire was waiting, it resolves WITHOUT owning — the engine gates
|
|
89
|
+
* recovery on `disposed` so a stopped-then-resolved acquire never proceeds.
|
|
90
|
+
* - `startRenewal()` begins the heartbeat that keeps the lease held (drives
|
|
91
|
+
* {@link LeaseManager.renewOnce} on an interval).
|
|
92
|
+
* - `renewOnce()` runs a single renewal round; exposed for tests so renewal can be
|
|
93
|
+
* driven deterministically with an injected clock, mirroring the detector's
|
|
94
|
+
* `tick()`.
|
|
95
|
+
* - `currentEpochBytes()` returns the held epoch as bytes for Step-2 fencing
|
|
96
|
+
* conditions; `null` before a successful acquire. Synchronous and stable across
|
|
97
|
+
* renewals.
|
|
98
|
+
* - `release()` best-effort relinquishes the lease (holder key only) on dispose.
|
|
99
|
+
*/
|
|
100
|
+
export type LeaseManager = {
|
|
101
|
+
acquire(): Promise<void>;
|
|
102
|
+
startRenewal(): void;
|
|
103
|
+
renewOnce(): Promise<void>;
|
|
104
|
+
currentEpochBytes(): Uint8Array | null;
|
|
105
|
+
release(): Promise<void>;
|
|
106
|
+
stop(): void;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Create a lease manager. Does not start any timer or acquire anything itself —
|
|
110
|
+
* the engine drives `acquire()` at the boot gate (before recovery) and
|
|
111
|
+
* `startRenewal()` afterward, and clears the renewal timer through its own
|
|
112
|
+
* disposal path (mirroring how it owns the second-instance detector's interval).
|
|
113
|
+
*/
|
|
114
|
+
export declare function createLeaseManager(options: LeaseManagerOptions): LeaseManager;
|