@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
|
@@ -1,12 +1,24 @@
|
|
|
1
|
+
import { decode } from "../../core/codec.js";
|
|
2
|
+
import { loadStoredStreamTailSequence } from "../../core/engine/stream-chunk-loading.js";
|
|
1
3
|
import { KEYS } from "../../storage/interface.js";
|
|
2
|
-
const TOKEN_EVENT_TYPE = "stream:token";
|
|
4
|
+
const TOKEN_EVENT_TYPE = "stream:token", STREAM_CONNECTION_POLICY_CLOSE_CODE = 1008, MAX_WATCH_REPLAY_EVENTS = 1000, MAX_PENDING_WATCH_REPLAY_MESSAGES = 1000;
|
|
5
|
+
export const DEFAULT_MAX_STREAM_CONNECTIONS_PER_WORKFLOW = 100;
|
|
3
6
|
export function sendStreamMessage(ws, sequence, message) {
|
|
4
|
-
if (sequence <= (ws.data.
|
|
7
|
+
if (sequence <= (ws.data.streamLastDeliveredSequence ?? -1))
|
|
5
8
|
return;
|
|
6
9
|
ws.send(message);
|
|
7
|
-
ws.data.
|
|
10
|
+
ws.data.streamLastDeliveredSequence = sequence;
|
|
11
|
+
}
|
|
12
|
+
export function sendWatchMessage(ws, sequence, message) {
|
|
13
|
+
if (sequence <= (ws.data.watchLastDeliveredSequence ?? -1))
|
|
14
|
+
return;
|
|
15
|
+
ws.send(message);
|
|
16
|
+
ws.data.watchLastDeliveredSequence = sequence;
|
|
8
17
|
}
|
|
9
18
|
export async function getHighestStoredStreamSequence(engine, workflowId, key) {
|
|
19
|
+
const tailSequence = await loadStoredStreamTailSequence(engine.storage, workflowId, key);
|
|
20
|
+
if (tailSequence !== null)
|
|
21
|
+
return tailSequence;
|
|
10
22
|
const prefix = KEYS.streamChunkPrefix(workflowId, key);
|
|
11
23
|
for await (const [storageKey] of engine.storage.scan(prefix, { reverse: !0, limit: 1 })) {
|
|
12
24
|
const sequenceText = storageKey.slice(prefix.length), sequence = Number.parseInt(sequenceText, 10);
|
|
@@ -15,13 +27,37 @@ export async function getHighestStoredStreamSequence(engine, workflowId, key) {
|
|
|
15
27
|
}
|
|
16
28
|
return -1;
|
|
17
29
|
}
|
|
30
|
+
export async function getHighestStoredWatchSequence(engine, workflowId) {
|
|
31
|
+
const prefix = KEYS.eventPrefix(workflowId);
|
|
32
|
+
for await (const [storageKey] of engine.storage.scan(prefix, { reverse: !0, limit: 50 })) {
|
|
33
|
+
const sequence = parseSequenceFromEventKey(prefix, storageKey);
|
|
34
|
+
if (sequence === null)
|
|
35
|
+
continue;
|
|
36
|
+
return sequence;
|
|
37
|
+
}
|
|
38
|
+
return -1;
|
|
39
|
+
}
|
|
18
40
|
export function addStreamSocket(context, workflowId, ws) {
|
|
41
|
+
if (!addWorkflowStreamConnection(context, workflowId, ws))
|
|
42
|
+
return !1;
|
|
19
43
|
let sockets = context.streamSockets.get(workflowId);
|
|
20
44
|
if (!sockets) {
|
|
21
45
|
sockets = new Set;
|
|
22
46
|
context.streamSockets.set(workflowId, sockets);
|
|
23
47
|
}
|
|
24
48
|
sockets.add(ws);
|
|
49
|
+
return !0;
|
|
50
|
+
}
|
|
51
|
+
export function addWatchSocket(context, workflowId, ws) {
|
|
52
|
+
if (!addWorkflowStreamConnection(context, workflowId, ws))
|
|
53
|
+
return !1;
|
|
54
|
+
let sockets = context.watchSockets.get(workflowId);
|
|
55
|
+
if (!sockets) {
|
|
56
|
+
sockets = new Set;
|
|
57
|
+
context.watchSockets.set(workflowId, sockets);
|
|
58
|
+
}
|
|
59
|
+
sockets.add(ws);
|
|
60
|
+
return !0;
|
|
25
61
|
}
|
|
26
62
|
export function removeStreamSocket(context, ws) {
|
|
27
63
|
const workflowId = ws.data.workflowId;
|
|
@@ -34,6 +70,39 @@ export function removeStreamSocket(context, ws) {
|
|
|
34
70
|
if (sockets.size === 0)
|
|
35
71
|
context.streamSockets.delete(workflowId);
|
|
36
72
|
}
|
|
73
|
+
export function removeWatchSocket(context, ws) {
|
|
74
|
+
const workflowId = ws.data.workflowId;
|
|
75
|
+
if (!workflowId)
|
|
76
|
+
return;
|
|
77
|
+
const sockets = context.watchSockets.get(workflowId);
|
|
78
|
+
if (!sockets)
|
|
79
|
+
return;
|
|
80
|
+
sockets.delete(ws);
|
|
81
|
+
if (sockets.size === 0)
|
|
82
|
+
context.watchSockets.delete(workflowId);
|
|
83
|
+
}
|
|
84
|
+
export function removeWorkflowStreamConnection(context, ws) {
|
|
85
|
+
const workflowId = ws.data.workflowId;
|
|
86
|
+
if (!workflowId || !ws.data.workflowStreamConnectionAccepted)
|
|
87
|
+
return;
|
|
88
|
+
ws.data.workflowStreamConnectionAccepted = !1;
|
|
89
|
+
const currentCount = context.workflowStreamConnectionCounts.get(workflowId);
|
|
90
|
+
if (currentCount === void 0 || currentCount <= 1) {
|
|
91
|
+
context.workflowStreamConnectionCounts.delete(workflowId);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
context.workflowStreamConnectionCounts.set(workflowId, currentCount - 1);
|
|
95
|
+
}
|
|
96
|
+
function addWorkflowStreamConnection(context, workflowId, ws) {
|
|
97
|
+
const currentCount = context.workflowStreamConnectionCounts.get(workflowId) ?? 0, maxConnections = context.maxStreamConnectionsPerWorkflow;
|
|
98
|
+
if (currentCount >= maxConnections) {
|
|
99
|
+
ws.close(STREAM_CONNECTION_POLICY_CLOSE_CODE, `maximum stream connections per workflow (${maxConnections}) exceeded`);
|
|
100
|
+
return !1;
|
|
101
|
+
}
|
|
102
|
+
context.workflowStreamConnectionCounts.set(workflowId, currentCount + 1);
|
|
103
|
+
ws.data.workflowStreamConnectionAccepted = !0;
|
|
104
|
+
return !0;
|
|
105
|
+
}
|
|
37
106
|
export function flushPendingStreamMessages(_context, ws) {
|
|
38
107
|
const pendingMessages = ws.data.pendingStreamMessages ?? [];
|
|
39
108
|
pendingMessages.sort((left, right) => left.sequence - right.sequence);
|
|
@@ -41,12 +110,19 @@ export function flushPendingStreamMessages(_context, ws) {
|
|
|
41
110
|
sendStreamMessage(ws, pending.sequence, pending.message);
|
|
42
111
|
ws.data.pendingStreamMessages = [];
|
|
43
112
|
}
|
|
113
|
+
export function flushPendingWatchMessages(_context, ws) {
|
|
114
|
+
const pendingMessages = ws.data.pendingWatchMessages ?? [];
|
|
115
|
+
pendingMessages.sort((left, right) => left.sequence - right.sequence);
|
|
116
|
+
for (const pending of pendingMessages)
|
|
117
|
+
sendWatchMessage(ws, pending.sequence, pending.message);
|
|
118
|
+
ws.data.pendingWatchMessages = [];
|
|
119
|
+
}
|
|
44
120
|
export function publishTokenMessage(context, workflowId, sequence, message) {
|
|
45
121
|
const sockets = context.streamSockets.get(workflowId);
|
|
46
122
|
if (!sockets)
|
|
47
123
|
return;
|
|
48
124
|
for (const ws of sockets) {
|
|
49
|
-
if (ws.data.
|
|
125
|
+
if (ws.data.streamReplayInProgress) {
|
|
50
126
|
ws.data.pendingStreamMessages ??= [];
|
|
51
127
|
ws.data.pendingStreamMessages.push({ sequence, message });
|
|
52
128
|
continue;
|
|
@@ -54,11 +130,29 @@ export function publishTokenMessage(context, workflowId, sequence, message) {
|
|
|
54
130
|
sendStreamMessage(ws, sequence, message);
|
|
55
131
|
}
|
|
56
132
|
}
|
|
133
|
+
export function publishWatchMessage(context, workflowId, sequence, message) {
|
|
134
|
+
const sockets = context.watchSockets.get(workflowId);
|
|
135
|
+
if (!sockets)
|
|
136
|
+
return;
|
|
137
|
+
for (const ws of sockets) {
|
|
138
|
+
if (ws.data.watchReplayInProgress) {
|
|
139
|
+
const pendingMessages = ws.data.pendingWatchMessages ?? [];
|
|
140
|
+
if (pendingMessages.length >= MAX_PENDING_WATCH_REPLAY_MESSAGES) {
|
|
141
|
+
closeWatchSocketForReplayLimit(context, ws, `watch replay buffer exceeded ${MAX_PENDING_WATCH_REPLAY_MESSAGES} pending messages`);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
pendingMessages.push({ sequence, message });
|
|
145
|
+
ws.data.pendingWatchMessages = pendingMessages;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
sendWatchMessage(ws, sequence, message);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
57
151
|
export async function replayTokenStream(context, engine, ws, workflowId) {
|
|
58
|
-
ws.data.
|
|
152
|
+
ws.data.streamLastDeliveredSequence = -1;
|
|
59
153
|
try {
|
|
60
154
|
const requestedResumeFrom = ws.data.resumeFrom, after = requestedResumeFrom === void 0 ? -1 : Math.min(requestedResumeFrom, await getHighestStoredStreamSequence(engine, workflowId, "tokens"));
|
|
61
|
-
ws.data.
|
|
155
|
+
ws.data.streamLastDeliveredSequence = after;
|
|
62
156
|
const chunks = after >= 0 ? await engine.getStreamChunks(workflowId, "tokens", { after }) : await engine.getStreamChunks(workflowId, "tokens");
|
|
63
157
|
for (const chunk of chunks) {
|
|
64
158
|
if (typeof chunk.value !== "object" || chunk.value === null)
|
|
@@ -73,7 +167,79 @@ export async function replayTokenStream(context, engine, ws, workflowId) {
|
|
|
73
167
|
} catch (error) {
|
|
74
168
|
console.error(`[weft] Failed to replay token stream for workflow "${workflowId}":`, error);
|
|
75
169
|
} finally {
|
|
76
|
-
ws.data.
|
|
170
|
+
ws.data.streamReplayInProgress = !1;
|
|
77
171
|
flushPendingStreamMessages(context, ws);
|
|
78
172
|
}
|
|
79
173
|
}
|
|
174
|
+
export async function replayWatchEvents(context, engine, ws, workflowId) {
|
|
175
|
+
ws.data.watchLastDeliveredSequence = -1;
|
|
176
|
+
let shouldFlushPending = !0, replayedEvents = 0;
|
|
177
|
+
try {
|
|
178
|
+
const requestedResumeFrom = ws.data.resumeFrom, after = requestedResumeFrom === void 0 ? -1 : Math.min(requestedResumeFrom, await getHighestStoredWatchSequence(engine, workflowId));
|
|
179
|
+
ws.data.watchLastDeliveredSequence = after;
|
|
180
|
+
const prefix = KEYS.eventPrefix(workflowId), scanOptions = after >= 0 ? { gt: KEYS.event(workflowId, after) } : void 0;
|
|
181
|
+
for await (const [storageKey, value] of engine.storage.scan(prefix, scanOptions)) {
|
|
182
|
+
const sequence = parseSequenceFromEventKey(prefix, storageKey);
|
|
183
|
+
if (sequence === null || sequence <= after)
|
|
184
|
+
continue;
|
|
185
|
+
const decoded = decodeStoredWatchEvent(value);
|
|
186
|
+
if (!isStoredWatchEvent(decoded))
|
|
187
|
+
continue;
|
|
188
|
+
if (replayedEvents >= MAX_WATCH_REPLAY_EVENTS) {
|
|
189
|
+
shouldFlushPending = !1;
|
|
190
|
+
closeWatchSocketForReplayLimit(context, ws, `watch replay window exceeds ${MAX_WATCH_REPLAY_EVENTS} events; reconnect with a newer resumeFrom`);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
sendWatchMessage(ws, sequence, JSON.stringify({
|
|
194
|
+
type: decoded.type,
|
|
195
|
+
timestamp: decoded.timestamp,
|
|
196
|
+
data: decoded.data,
|
|
197
|
+
sequence,
|
|
198
|
+
cursor: String(sequence)
|
|
199
|
+
}));
|
|
200
|
+
replayedEvents += 1;
|
|
201
|
+
}
|
|
202
|
+
} catch (error) {
|
|
203
|
+
console.error(`[weft] Failed to replay watch events for workflow "${workflowId}":`, error);
|
|
204
|
+
shouldFlushPending = !1;
|
|
205
|
+
closeWatchSocketForReplayLimit(context, ws, "watch replay failed before catch-up completed; reconnect with a newer resumeFrom");
|
|
206
|
+
} finally {
|
|
207
|
+
ws.data.watchReplayInProgress = !1;
|
|
208
|
+
if (shouldFlushPending)
|
|
209
|
+
flushPendingWatchMessages(context, ws);
|
|
210
|
+
else
|
|
211
|
+
ws.data.pendingWatchMessages = [];
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function closeWatchSocketForReplayLimit(context, ws, reason) {
|
|
215
|
+
ws.data.pendingWatchMessages = [];
|
|
216
|
+
ws.data.watchReplayInProgress = !1;
|
|
217
|
+
ws.close(STREAM_CONNECTION_POLICY_CLOSE_CODE, reason);
|
|
218
|
+
removeWatchSocket(context, ws);
|
|
219
|
+
removeWorkflowStreamConnection(context, ws);
|
|
220
|
+
}
|
|
221
|
+
function decodeStoredWatchEvent(value) {
|
|
222
|
+
try {
|
|
223
|
+
return decode(value);
|
|
224
|
+
} catch {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function parseSequenceFromEventKey(prefix, storageKey) {
|
|
229
|
+
if (!storageKey.startsWith(prefix))
|
|
230
|
+
return null;
|
|
231
|
+
const rawSequence = storageKey.slice(prefix.length);
|
|
232
|
+
if (!/^\d+$/.test(rawSequence))
|
|
233
|
+
return null;
|
|
234
|
+
const sequence = Number.parseInt(rawSequence, 10);
|
|
235
|
+
return Number.isSafeInteger(sequence) ? sequence : null;
|
|
236
|
+
}
|
|
237
|
+
function isStoredWatchEvent(value) {
|
|
238
|
+
if (typeof value !== "object" || value === null)
|
|
239
|
+
return !1;
|
|
240
|
+
const record = value;
|
|
241
|
+
return typeof record.type === "string" && typeof record.timestamp === "number" && Number.isFinite(record.timestamp) && isPlainRecord(record.data);
|
|
242
|
+
}
|
|
243
|
+
function isPlainRecord(value) {
|
|
244
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
245
|
+
}
|
|
@@ -37,7 +37,7 @@ function rejectsCrossOriginUpgrade(context, request) {
|
|
|
37
37
|
return context.corsPolicy !== null && origin !== null && !isOriginAllowed(context.corsPolicy, origin);
|
|
38
38
|
}
|
|
39
39
|
function resolvePrincipalForUpgrade(connectionType, authContext) {
|
|
40
|
-
if (connectionType !== "jsonrpc" && connectionType !== "worker")
|
|
40
|
+
if (connectionType !== "jsonrpc" && connectionType !== "worker" && connectionType !== "watch" && connectionType !== "stream")
|
|
41
41
|
return { ok: !0, principal: void 0 };
|
|
42
42
|
if (authContext === void 0)
|
|
43
43
|
return { ok: !0, principal: void 0 };
|
|
@@ -48,10 +48,29 @@ function resolvePrincipalForUpgrade(connectionType, authContext) {
|
|
|
48
48
|
console.error("[weft] WebSocket upgrade principal resolution failed", error);
|
|
49
49
|
return { ok: !1, response: new Response("Authentication context invalid", { status: 401 }) };
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
const requiredScope = upgradeScope(connectionType);
|
|
52
|
+
if (requiredScope !== null) {
|
|
53
|
+
if (!isAuthenticated(principal))
|
|
54
|
+
return { ok: !1, response: new Response("Authentication required", { status: 401 }) };
|
|
55
|
+
if (!principal.hasScope(requiredScope))
|
|
56
|
+
return { ok: !1, response: new Response("Insufficient scope", { status: 403 }) };
|
|
57
|
+
}
|
|
53
58
|
return { ok: !0, principal };
|
|
54
59
|
}
|
|
60
|
+
function upgradeScope(connectionType) {
|
|
61
|
+
switch (connectionType) {
|
|
62
|
+
case "worker":
|
|
63
|
+
return "workers:write";
|
|
64
|
+
case "watch":
|
|
65
|
+
return "events:read";
|
|
66
|
+
case "stream":
|
|
67
|
+
return "streams:read";
|
|
68
|
+
case "jsonrpc":
|
|
69
|
+
case "generic":
|
|
70
|
+
case void 0:
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
55
74
|
export function handleWebSocketUpgrade(server, context, options, request, url, authContext) {
|
|
56
75
|
if (request.headers.get("upgrade") !== "websocket")
|
|
57
76
|
return null;
|
|
@@ -3,5 +3,5 @@ import type { ServeOptions } from '../index.ts';
|
|
|
3
3
|
import type { WebSocketData } from '../json-rpc-websocket-runtime.ts';
|
|
4
4
|
import type { ServerContext } from './context.ts';
|
|
5
5
|
export { isInflightRecord } from '../task-state.ts';
|
|
6
|
-
export
|
|
6
|
+
export { withRetry } from './retry.ts';
|
|
7
7
|
export declare function handleWorkerWebSocketMessage(context: ServerContext, options: ServeOptions, ws: ServerWebSocket<WebSocketData>, rawMessage: string | Buffer, cleanupWorkflowIndex: (operationId: string) => void): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { decode, encode } from "../../core/codec.js";
|
|
2
|
+
import { WorkerConnectedEvent } from "../../core/events.js";
|
|
2
3
|
import { KEYS } from "../../storage/interface.js";
|
|
3
4
|
import {
|
|
4
5
|
REMOTE_WORKER_PROTOCOL_VERSION,
|
|
@@ -7,35 +8,20 @@ import {
|
|
|
7
8
|
} from "../../worker/protocol.js";
|
|
8
9
|
import { workerProtocolIncompatibleMessage } from "../../worker/worker-protocol-incompatible-error.js";
|
|
9
10
|
import { isAuthenticated } from "../principal.js";
|
|
11
|
+
import { isInflightRecord, readInflightRecord } from "../task-state.js";
|
|
12
|
+
import { withRetry } from "./retry.js";
|
|
13
|
+
import { recordWorkerCapacitySaturationMetric } from "./task-metrics.js";
|
|
10
14
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from "../task-state.js";
|
|
15
|
-
import {
|
|
16
|
-
recordTaskExecutionLatencyMetric,
|
|
17
|
-
recordWorkerCapacitySaturationMetric
|
|
18
|
-
} from "./task-metrics.js";
|
|
15
|
+
taskResultPayloadSizeError,
|
|
16
|
+
transitionTaskResultToResolvedWithRetry
|
|
17
|
+
} from "./task-result-resolution.js";
|
|
19
18
|
import { WORKER_STREAM_RE } from "./websocket-upgrade.js";
|
|
20
19
|
const MAX_WORKER_CONCURRENCY = 1000, DEFAULT_WORKER_CONCURRENCY = 10, WORKER_PROTOCOL_CLOSE_CODE = 1002, WORKER_REGISTRATION_CLOSE_CODE = 1008;
|
|
21
20
|
function isWorkerConnection(pathname) {
|
|
22
21
|
return WORKER_STREAM_RE.test(pathname);
|
|
23
22
|
}
|
|
24
23
|
export { isInflightRecord } from "../task-state.js";
|
|
25
|
-
export
|
|
26
|
-
let lastError;
|
|
27
|
-
for (let attempt = 1;attempt <= maxAttempts; attempt++)
|
|
28
|
-
try {
|
|
29
|
-
return await operation();
|
|
30
|
-
} catch (error) {
|
|
31
|
-
lastError = error;
|
|
32
|
-
if (attempt < maxAttempts) {
|
|
33
|
-
console.warn(`[weft] Retrying "${label}" (attempt ${attempt + 1}/${maxAttempts})`);
|
|
34
|
-
await Bun.sleep(100 * attempt);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
throw lastError;
|
|
38
|
-
}
|
|
24
|
+
export { withRetry } from "./retry.js";
|
|
39
25
|
function sendWorkerProtocolMessage(ws, message) {
|
|
40
26
|
ws.send(JSON.stringify(message));
|
|
41
27
|
}
|
|
@@ -83,7 +69,7 @@ function buildWorkerRegistrationInfo(message, queue, concurrency) {
|
|
|
83
69
|
...message.capabilities !== void 0 ? { capabilities: message.capabilities } : {}
|
|
84
70
|
};
|
|
85
71
|
}
|
|
86
|
-
function registerWorker(context, ws, message) {
|
|
72
|
+
function registerWorker(context, options, ws, message) {
|
|
87
73
|
if (!principalMayRegisterWorker(ws.data.principal)) {
|
|
88
74
|
rejectRegistration(ws, "invalid_registration", "Worker registration requires the workers:write scope", message.protocolVersion);
|
|
89
75
|
return;
|
|
@@ -106,6 +92,7 @@ function registerWorker(context, ws, message) {
|
|
|
106
92
|
activities: [...message.activities],
|
|
107
93
|
concurrency: clampedConcurrency
|
|
108
94
|
});
|
|
95
|
+
options.engine.dispatchEvent(new WorkerConnectedEvent(message.workerId, queue, [...message.activities], clampedConcurrency));
|
|
109
96
|
}
|
|
110
97
|
function resolveTaskResultStatus(message) {
|
|
111
98
|
return message.status === "completed" ? "completed" : "failed";
|
|
@@ -120,20 +107,49 @@ function onTaskResultMessage(context, options, ws, message, cleanupWorkflowIndex
|
|
|
120
107
|
});
|
|
121
108
|
return;
|
|
122
109
|
}
|
|
110
|
+
if (!context.registry.isAssignedToAttempt(operationId, workerId, message.attemptToken)) {
|
|
111
|
+
sendWorkerProtocolMessage(ws, {
|
|
112
|
+
type: "protocolError",
|
|
113
|
+
code: "invalid_message",
|
|
114
|
+
message: `taskResult for operation "${operationId}" rejected \u2014 stale attempt token`
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const resolvedStatus = resolveTaskResultStatus(message), payloadError = taskResultPayloadSizeError({
|
|
119
|
+
operationId,
|
|
120
|
+
status: resolvedStatus,
|
|
121
|
+
resolutionReason: resolvedStatus,
|
|
122
|
+
...message.status === "completed" ? { value: message.value } : { error: message.error }
|
|
123
|
+
}, context.payloadSizeMaxBytes);
|
|
123
124
|
context.registry.completeTask(operationId);
|
|
124
125
|
context.deadlineTracker.remove(operationId);
|
|
125
126
|
cleanupWorkflowIndex(operationId);
|
|
126
127
|
recordWorkerCapacitySaturationMetric(context.metricsCollector, context.registry);
|
|
127
128
|
(async () => {
|
|
128
|
-
const inflightRecord = await readInflightRecord(options.engine.storage, operationId)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
const inflightRecord = await readInflightRecord(options.engine.storage, operationId);
|
|
130
|
+
if (payloadError !== null) {
|
|
131
|
+
sendWorkerProtocolMessage(ws, {
|
|
132
|
+
type: "protocolError",
|
|
133
|
+
code: "invalid_message",
|
|
134
|
+
message: payloadError.message
|
|
135
|
+
});
|
|
136
|
+
await transitionTaskResultToResolvedWithRetry(context, options, {
|
|
137
|
+
operationId,
|
|
138
|
+
status: "failed",
|
|
139
|
+
resolutionReason: "failed",
|
|
140
|
+
inflightRecord,
|
|
141
|
+
error: payloadError.message,
|
|
142
|
+
skipPayloadSizeCheck: !0
|
|
143
|
+
});
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
await transitionTaskResultToResolvedWithRetry(context, options, {
|
|
147
|
+
operationId,
|
|
148
|
+
status: resolvedStatus,
|
|
132
149
|
resolutionReason: resolvedStatus,
|
|
150
|
+
inflightRecord,
|
|
133
151
|
...message.status === "completed" ? { value: message.value } : { error: message.error }
|
|
134
152
|
});
|
|
135
|
-
if (inflightRecord !== null)
|
|
136
|
-
recordTaskExecutionLatencyMetric(context.metricsCollector, inflightRecord, resolvedAt);
|
|
137
153
|
})().catch((error) => {
|
|
138
154
|
console.error(`[weft] Failed to transition task "${operationId}" to resolved \u2014 inflight record may leak:`, error);
|
|
139
155
|
});
|
|
@@ -207,7 +223,7 @@ export function handleWorkerWebSocketMessage(context, options, ws, rawMessage, c
|
|
|
207
223
|
}
|
|
208
224
|
switch (message.type) {
|
|
209
225
|
case "register": {
|
|
210
|
-
registerWorker(context, ws, message);
|
|
226
|
+
registerWorker(context, options, ws, message);
|
|
211
227
|
break;
|
|
212
228
|
}
|
|
213
229
|
case "taskResult": {
|
|
@@ -11,12 +11,22 @@ import type { WebSocketData } from './json-rpc-websocket-runtime.ts';
|
|
|
11
11
|
import { createServerWebSocketHandlers } from './runtime/authentication-bridge.ts';
|
|
12
12
|
import type { ServerContext } from './runtime/context.ts';
|
|
13
13
|
import { type EventBroadcastingHandle } from './runtime/event-broadcasting.ts';
|
|
14
|
+
/**
|
|
15
|
+
* @internal
|
|
16
|
+
*
|
|
17
|
+
* Warning emitted when a server starts with no authentication and no explicit
|
|
18
|
+
* `unauthenticatedAccess` policy. Exported only for internal tests (see
|
|
19
|
+
* `tests/auth-warning-filter.test.ts`) and not part of the public API surface.
|
|
20
|
+
*/
|
|
21
|
+
export declare const NO_AUTHENTICATION_WARNING = "[weft] WARNING: server started with NO authentication; all non-public operations are publicly accessible. Configure serve({ auth }) to lock down, or set unauthenticatedAccess: \"reject\" in production to fail closed.";
|
|
22
|
+
export declare const MCP_ORIGIN_CONFIGURATION_WARNING = "[weft] WARNING: MCP HTTP transport is enabled without publicOrigin or trustedHosts. Cross-origin /mcp requests are rejected, and discovery routes that emit absolute URLs return 503. Configure serve({ publicOrigin: \"https://api.example.com\" }) or serve({ trustedHosts: [\"api.example.com\"] }) before exposing the server through a browser or reverse proxy.";
|
|
14
23
|
/**
|
|
15
24
|
* Clamp a user-supplied `workerReconnectGracePeriodMs` into `[0, 5_000]`.
|
|
16
|
-
* Returns the default when undefined or non-finite.
|
|
25
|
+
* Returns the 2000ms default when undefined or non-finite.
|
|
17
26
|
*/
|
|
18
27
|
export declare function clampWorkerReconnectGracePeriod(value: number | undefined): number;
|
|
19
28
|
export declare function assertAuthenticationPosture(options: ServeOptions, environmentRequirement?: string | undefined): void;
|
|
29
|
+
export declare function warnIfMcpOriginConfigurationMissing(options: ServeOptions): void;
|
|
20
30
|
/**
|
|
21
31
|
* A mutable holder for the Bun server instance. The `fetch` handler needs to
|
|
22
32
|
* call back into the server (for WebSocket upgrades) but the server isn't
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { decode } from "../core/codec.js";
|
|
2
|
+
import { getEnginePayloadSizeMaxBytes } from "../core/engine/payload-size-policy.js";
|
|
2
3
|
import { createMcpSessionManager } from "../mcp/session.js";
|
|
3
4
|
import { createMetricsCollectorExporter, MetricsCollector } from "../observability/metrics.js";
|
|
4
5
|
import { WorkerRegistry } from "../worker/registry.js";
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
} from "./authentication.js";
|
|
12
13
|
import { DeadlineTracker } from "./deadline-tracker.js";
|
|
13
14
|
import { createEngineEventFeedBackend } from "./engine-event-feed-backend.js";
|
|
15
|
+
import { createFleetEventFeed } from "./fleet-event-feed.js";
|
|
14
16
|
import { closeJsonRpcSessionsForShutdown } from "./json-rpc-websocket-runtime.js";
|
|
15
17
|
import { createLiveOperationRegistry, createLiveRestBindings } from "./rest-bindings.js";
|
|
16
18
|
import {
|
|
@@ -22,11 +24,15 @@ import { resolveCorsPolicy, validateCorsOptions } from "./runtime/cors.js";
|
|
|
22
24
|
import {
|
|
23
25
|
registerWorkflowEventLifecycle
|
|
24
26
|
} from "./runtime/event-broadcasting.js";
|
|
27
|
+
import { shutdownAllWorkers } from "./runtime/shutdown.js";
|
|
25
28
|
import { reconcileOrphanedRecords, scanExpiredTasks } from "./runtime/task-reconciliation.js";
|
|
29
|
+
import { DEFAULT_MAX_STREAM_CONNECTIONS_PER_WORKFLOW } from "./runtime/websocket-stream.js";
|
|
26
30
|
import { isInflightRecord, withRetry } from "./runtime/websocket-worker.js";
|
|
27
31
|
import { TaskQueue } from "./task-queue.js";
|
|
28
32
|
import { createWorkflowEventFeed } from "./workflow-event-feed.js";
|
|
29
|
-
const RECONCILIATION_MULTIPLIER = 12, DEFAULT_WORKER_RECONNECT_GRACE_PERIOD_MS =
|
|
33
|
+
const RECONCILIATION_MULTIPLIER = 12, DEFAULT_WORKER_RECONNECT_GRACE_PERIOD_MS = 2000, MAX_WORKER_RECONNECT_GRACE_PERIOD_MS = 5000, AUTHENTICATION_REQUIRED_ENVIRONMENT_VARIABLE = "WEFT_SERVER_AUTHENTICATION_REQUIRED";
|
|
34
|
+
export const NO_AUTHENTICATION_WARNING = '[weft] WARNING: server started with NO authentication; all non-public operations are publicly accessible. Configure serve({ auth }) to lock down, or set unauthenticatedAccess: "reject" in production to fail closed.', MCP_ORIGIN_CONFIGURATION_WARNING = '[weft] WARNING: MCP HTTP transport is enabled without publicOrigin or trustedHosts. Cross-origin /mcp requests are rejected, and discovery routes that emit absolute URLs return 503. Configure serve({ publicOrigin: "https://api.example.com" }) or serve({ trustedHosts: ["api.example.com"] }) before exposing the server through a browser or reverse proxy.';
|
|
35
|
+
const NO_AUTHENTICATION_REJECT_ERROR = '[weft] Refusing to start server with no authentication. Configure serve({ auth }) or set unauthenticatedAccess: "allow" only for trusted local development.', NO_AUTHENTICATION_ENVIRONMENT_ERROR = "[weft] Refusing to start server with no authentication because WEFT_SERVER_AUTHENTICATION_REQUIRED requires authentication. Configure serve({ auth }) or unset WEFT_SERVER_AUTHENTICATION_REQUIRED only for trusted local development.", TRUTHY_AUTHENTICATION_REQUIREMENT_VALUES = new Set(["1", "true", "yes", "on"]), FALSY_AUTHENTICATION_REQUIREMENT_VALUES = new Set(["0", "false", "no", "off"]);
|
|
30
36
|
export function clampWorkerReconnectGracePeriod(value) {
|
|
31
37
|
if (value === void 0 || !Number.isFinite(value))
|
|
32
38
|
return DEFAULT_WORKER_RECONNECT_GRACE_PERIOD_MS;
|
|
@@ -59,6 +65,11 @@ export function assertAuthenticationPosture(options, environmentRequirement = Bu
|
|
|
59
65
|
return;
|
|
60
66
|
console.warn(NO_AUTHENTICATION_WARNING);
|
|
61
67
|
}
|
|
68
|
+
export function warnIfMcpOriginConfigurationMissing(options) {
|
|
69
|
+
if (options.publicOrigin !== void 0 || options.trustedHosts !== void 0)
|
|
70
|
+
return;
|
|
71
|
+
console.warn(MCP_ORIGIN_CONFIGURATION_WARNING);
|
|
72
|
+
}
|
|
62
73
|
export function resolveNetworkConfig(options) {
|
|
63
74
|
if (options.auth)
|
|
64
75
|
validateAuthConfig(options.auth);
|
|
@@ -67,6 +78,7 @@ export function resolveNetworkConfig(options) {
|
|
|
67
78
|
if (options.cors)
|
|
68
79
|
validateCorsOptions(options.cors, options.auth !== void 0);
|
|
69
80
|
assertAuthenticationPosture(options);
|
|
81
|
+
warnIfMcpOriginConfigurationMissing(options);
|
|
70
82
|
const port = options.port ?? 7233, hostname = options.hostname ?? "0.0.0.0", development = options.development ?? !1, serverMetricsCollector = new MetricsCollector, prometheusExporter = options.prometheusExporter ?? createMetricsCollectorExporter(serverMetricsCollector), serverOptions = { ...options, prometheusExporter }, tlsOptions = buildTLSOptions(options.auth);
|
|
71
83
|
return { port, hostname, development, tlsOptions, serverOptions, serverMetricsCollector };
|
|
72
84
|
}
|
|
@@ -77,6 +89,9 @@ export function buildServerContext(options, serverMetricsCollector) {
|
|
|
77
89
|
taskQueue,
|
|
78
90
|
workerSockets: new Map,
|
|
79
91
|
streamSockets: new Map,
|
|
92
|
+
watchSockets: new Map,
|
|
93
|
+
workflowStreamConnectionCounts: new Map,
|
|
94
|
+
maxStreamConnectionsPerWorkflow: options.maxStreamConnectionsPerWorkflow ?? DEFAULT_MAX_STREAM_CONNECTIONS_PER_WORKFLOW,
|
|
80
95
|
workerAffinity: new Map,
|
|
81
96
|
workflowOperations: new Map,
|
|
82
97
|
operationToWorkflow: new Map,
|
|
@@ -93,12 +108,14 @@ export function buildServerContext(options, serverMetricsCollector) {
|
|
|
93
108
|
metricsCollector: serverMetricsCollector,
|
|
94
109
|
eventFeedBackend,
|
|
95
110
|
workflowEventFeed: createWorkflowEventFeed(eventFeedBackend),
|
|
111
|
+
fleetEventFeed: createFleetEventFeed(options.engine.storage),
|
|
96
112
|
activeJsonRpcSessions: new Set,
|
|
97
113
|
mcpSessionManager: createMcpSessionManager(options.engine),
|
|
98
114
|
authenticatorPromise: options.auth ? createAuthenticator(options.auth) : null,
|
|
99
115
|
rateLimiter: options.rateLimit ? createRateLimiter(options.rateLimit) : null,
|
|
100
116
|
visibilityPollMs: options.visibilityPollIntervalMs ?? 5000,
|
|
101
117
|
workerReconnectGracePeriodMs: clampWorkerReconnectGracePeriod(options.workerReconnectGracePeriodMs),
|
|
118
|
+
payloadSizeMaxBytes: getEnginePayloadSizeMaxBytes(options.engine),
|
|
102
119
|
pendingWorkerRequeues: new Map,
|
|
103
120
|
scanRunning: !1,
|
|
104
121
|
processingOperations: new Set,
|
|
@@ -146,6 +163,7 @@ export function registerStackDisposers(stack, context, options, broadcastingHand
|
|
|
146
163
|
});
|
|
147
164
|
stack.defer(() => context.mcpSessionManager[Symbol.asyncDispose]());
|
|
148
165
|
stack.defer(registerWorkflowEventLifecycle(options.engine, context, broadcastingHandle));
|
|
166
|
+
stack.defer(() => shutdownAllWorkers(context, options.workerShutdownTimeoutMs === void 0 ? void 0 : { timeoutMs: options.workerShutdownTimeoutMs, stopWaitingWhenIdle: !0 }));
|
|
149
167
|
const visibilityPollHandle = setInterval(() => {
|
|
150
168
|
scanExpiredTasks(context, options, onOperationCleanup);
|
|
151
169
|
}, context.visibilityPollMs), reconciliationIntervalMs = context.visibilityPollMs * RECONCILIATION_MULTIPLIER, reconciliationHandle = setInterval(() => {
|
|
@@ -179,7 +197,7 @@ export function restoreInflightTasks(context, options) {
|
|
|
179
197
|
continue;
|
|
180
198
|
}
|
|
181
199
|
const remaining = record.deadline - now;
|
|
182
|
-
context.registry.assignTask(record.workerId, record.operationId, remaining);
|
|
200
|
+
context.registry.assignTask(record.workerId, record.operationId, remaining, void 0, record.attemptToken);
|
|
183
201
|
context.deadlineTracker.add({ operationId: record.operationId, deadline: record.deadline });
|
|
184
202
|
const tracked = context.registry.getWorkerTasks(record.workerId).find((t) => t.operationId === record.operationId);
|
|
185
203
|
if (tracked)
|
|
@@ -20,6 +20,8 @@ export type TaskState = 'queued' | 'inflight' | 'resolved';
|
|
|
20
20
|
export type TaskRequeueReason = 'visibility-timeout' | 'worker-disconnect';
|
|
21
21
|
/** Final reason captured when a task reaches the resolved state. */
|
|
22
22
|
export type TaskResolutionReason = 'completed' | 'failed' | 'cancelled' | 'max-attempts-exceeded';
|
|
23
|
+
/** Why a task-result transition was moved to the operator dead-letter guard. */
|
|
24
|
+
export type TaskDeadLetterReason = 'result-resolution-storage-exhausted';
|
|
23
25
|
/** Lifecycle evidence persisted with task records for diagnostics. */
|
|
24
26
|
export interface TaskLifecycleFields {
|
|
25
27
|
/** First time this operation entered a task queue. */
|
|
@@ -67,6 +69,14 @@ export interface InflightRecord extends TaskLifecycleFields {
|
|
|
67
69
|
retryPolicy?: RetryPolicy | undefined;
|
|
68
70
|
/** Workflow that dispatched this activity. Present when the dispatch included a workflowId. */
|
|
69
71
|
workflowId?: string | undefined;
|
|
72
|
+
/**
|
|
73
|
+
* Unique, unguessable token identifying this dispatch attempt. Rotated on every
|
|
74
|
+
* (re-)dispatch because each dispatch writes a fresh InflightRecord. The
|
|
75
|
+
* long-poll completion handler rejects a result whose echoed token does not
|
|
76
|
+
* match this value. Optional for back-compatible decoding of records written
|
|
77
|
+
* before this field existed; a missing token disables the check for that record.
|
|
78
|
+
*/
|
|
79
|
+
attemptToken?: string | undefined;
|
|
70
80
|
}
|
|
71
81
|
/** Persisted record for a task in the resolved state. */
|
|
72
82
|
export interface ResolvedRecord {
|
|
@@ -95,6 +105,24 @@ export interface ResolvedRecord {
|
|
|
95
105
|
queueLatencyMs?: number | undefined;
|
|
96
106
|
executionLatencyMs?: number | undefined;
|
|
97
107
|
}
|
|
108
|
+
/** Durable operator guard for a task result whose resolved write exhausted retries. */
|
|
109
|
+
export interface DeadLetteredTaskRecord {
|
|
110
|
+
operationId: string;
|
|
111
|
+
reason: TaskDeadLetterReason;
|
|
112
|
+
deadLetteredAt: number;
|
|
113
|
+
errorMessage: string;
|
|
114
|
+
retryAttempts: number;
|
|
115
|
+
status: 'completed' | 'failed';
|
|
116
|
+
activityName?: string | undefined;
|
|
117
|
+
queue?: string | undefined;
|
|
118
|
+
workerId?: string | undefined;
|
|
119
|
+
attempt?: number | undefined;
|
|
120
|
+
visibilityTimeout?: number | undefined;
|
|
121
|
+
workflowId?: string | undefined;
|
|
122
|
+
retryCount?: number | undefined;
|
|
123
|
+
requeueCount?: number | undefined;
|
|
124
|
+
lastRequeueReason?: TaskRequeueReason | undefined;
|
|
125
|
+
}
|
|
98
126
|
export interface TransitionInflightToResolvedOptions {
|
|
99
127
|
resolutionReason?: TaskResolutionReason;
|
|
100
128
|
resolvedAt?: number;
|
|
@@ -123,10 +151,20 @@ export declare function isQueuedRecord(value: unknown): value is QueuedRecord;
|
|
|
123
151
|
export declare function isInflightRecord(value: unknown): value is InflightRecord;
|
|
124
152
|
/** Type guard for decoded storage records in the resolved state. */
|
|
125
153
|
export declare function isResolvedRecord(value: unknown): value is ResolvedRecord;
|
|
154
|
+
/** Type guard for decoded task-result dead-letter records. */
|
|
155
|
+
export declare function isDeadLetteredTaskRecord(value: unknown): value is DeadLetteredTaskRecord;
|
|
126
156
|
/** Decode the queued record for an operation, returning null when absent or malformed. */
|
|
127
157
|
export declare function readQueuedRecord(storage: Storage, operationId: string): Promise<QueuedRecord | null>;
|
|
128
158
|
/** Decode the inflight record for an operation, returning null when absent or malformed. */
|
|
129
159
|
export declare function readInflightRecord(storage: Storage, operationId: string): Promise<InflightRecord | null>;
|
|
160
|
+
/** Decode the task-result dead-letter record for an operation. */
|
|
161
|
+
export declare function readDeadLetteredTaskRecord(storage: Storage, operationId: string): Promise<DeadLetteredTaskRecord | null>;
|
|
162
|
+
/** True when reconciliation should not re-dispatch the operation. */
|
|
163
|
+
export declare function isTaskDeadLettered(storage: Storage, operationId: string): Promise<boolean>;
|
|
164
|
+
/** Persist a task-result dead-letter guard. */
|
|
165
|
+
export declare function writeDeadLetteredTaskRecord(storage: Storage, record: DeadLetteredTaskRecord): Promise<void>;
|
|
166
|
+
/** Clear a task-result dead-letter guard so reconciliation may handle the inflight record again. */
|
|
167
|
+
export declare function clearDeadLetteredTaskRecord(storage: Storage, operationId: string): Promise<void>;
|
|
130
168
|
export declare function calculateQueueLatencyMs(record: TaskLifecycleFields): number | undefined;
|
|
131
169
|
export declare function calculateExecutionLatencyMs(record: TaskLifecycleFields, completedAt: number): number | undefined;
|
|
132
170
|
export declare function calculateHeartbeatAgeMs(record: TaskLifecycleFields & {
|
|
@@ -49,6 +49,12 @@ export function isResolvedRecord(value) {
|
|
|
49
49
|
const record = value;
|
|
50
50
|
return typeof record.operationId === "string" && (record.status === "completed" || record.status === "failed") && typeof record.resolvedAt === "number";
|
|
51
51
|
}
|
|
52
|
+
export function isDeadLetteredTaskRecord(value) {
|
|
53
|
+
if (value === null || typeof value !== "object")
|
|
54
|
+
return !1;
|
|
55
|
+
const record = value;
|
|
56
|
+
return typeof record.operationId === "string" && record.reason === "result-resolution-storage-exhausted" && typeof record.deadLetteredAt === "number" && typeof record.errorMessage === "string" && typeof record.retryAttempts === "number" && (record.status === "completed" || record.status === "failed");
|
|
57
|
+
}
|
|
52
58
|
export async function readQueuedRecord(storage, operationId) {
|
|
53
59
|
const value = await storage.get(KEYS.operationQueued(operationId));
|
|
54
60
|
if (value === null)
|
|
@@ -63,6 +69,28 @@ export async function readInflightRecord(storage, operationId) {
|
|
|
63
69
|
const decoded = decode(value);
|
|
64
70
|
return isInflightRecord(decoded) ? decoded : null;
|
|
65
71
|
}
|
|
72
|
+
export async function readDeadLetteredTaskRecord(storage, operationId) {
|
|
73
|
+
const value = await storage.get(KEYS.operationDeadLetter(operationId));
|
|
74
|
+
if (value === null)
|
|
75
|
+
return null;
|
|
76
|
+
const decoded = decode(value);
|
|
77
|
+
return isDeadLetteredTaskRecord(decoded) ? decoded : null;
|
|
78
|
+
}
|
|
79
|
+
export async function isTaskDeadLettered(storage, operationId) {
|
|
80
|
+
return await readDeadLetteredTaskRecord(storage, operationId) !== null;
|
|
81
|
+
}
|
|
82
|
+
export async function writeDeadLetteredTaskRecord(storage, record) {
|
|
83
|
+
await storage.batch([
|
|
84
|
+
{
|
|
85
|
+
type: "put",
|
|
86
|
+
key: KEYS.operationDeadLetter(record.operationId),
|
|
87
|
+
value: encode(record)
|
|
88
|
+
}
|
|
89
|
+
]);
|
|
90
|
+
}
|
|
91
|
+
export async function clearDeadLetteredTaskRecord(storage, operationId) {
|
|
92
|
+
await storage.delete(KEYS.operationDeadLetter(operationId));
|
|
93
|
+
}
|
|
66
94
|
function normalizeQueuedRecordLifecycle(record, previous) {
|
|
67
95
|
const normalized = {
|
|
68
96
|
...record,
|