@lostgradient/weft 0.8.0 → 0.10.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 +9 -5
- package/dist/alerting/alert-manager.d.ts +3 -1
- package/dist/alerting/alert-manager.js +5 -2
- package/dist/cli/generated/operation-client.generated.d.ts +1 -0
- package/dist/cli-main.js +78 -78
- package/dist/core/context/durable-operations.js +1 -1
- package/dist/core/context/index.d.ts +3 -1
- package/dist/core/context/index.js +10 -0
- package/dist/core/context/operation-request.d.ts +4 -0
- package/dist/core/context/parallel-cache-entry.d.ts +1 -1
- package/dist/core/context/parallel-cache-entry.js +12 -8
- package/dist/core/context/parallel-operations.d.ts +1 -1
- package/dist/core/context/parallel-operations.js +50 -8
- package/dist/core/context/run-operation.js +3 -2
- package/dist/core/context/speculative-child.js +1 -0
- package/dist/core/context/types.d.ts +1 -0
- package/dist/core/engine/activity-heartbeat-tracking.d.ts +1 -1
- package/dist/core/engine/activity-heartbeat-tracking.js +5 -1
- package/dist/core/engine/anonymous-signal-sequence.js +0 -2
- package/dist/core/engine/async-activity-completion.d.ts +24 -54
- package/dist/core/engine/async-activity-completion.js +25 -90
- package/dist/core/engine/async-activity-records.d.ts +106 -0
- package/dist/core/engine/async-activity-records.js +124 -0
- package/dist/core/engine/bulk-operations-purge.js +2 -1
- package/dist/core/engine/bulk-operations.js +10 -29
- package/dist/core/engine/callback-creators-bundles.js +1 -1
- package/dist/core/engine/callback-creators-core.js +1 -0
- package/dist/core/engine/construction.d.ts +1 -3
- package/dist/core/engine/construction.js +2 -5
- package/dist/core/engine/deferred-consume-envelope.d.ts +10 -1
- package/dist/core/engine/deferred-consume-envelope.js +9 -1
- package/dist/core/engine/disposal.js +1 -0
- package/dist/core/engine/engine-internal-types.d.ts +2 -0
- package/dist/core/engine/engine-runtime-helpers.d.ts +8 -0
- package/dist/core/engine/engine-runtime-helpers.js +9 -0
- package/dist/core/engine/errors.js +1 -1
- package/dist/core/engine/index.d.ts +12 -2
- package/dist/core/engine/index.js +35 -16
- package/dist/core/engine/inline-launch-queue.js +1 -1
- package/dist/core/engine/internals.d.ts +25 -3
- package/dist/core/engine/lifecycle/fork-helpers.js +1 -0
- package/dist/core/engine/lifecycle/recovered-services.d.ts +4 -2
- package/dist/core/engine/lifecycle/recovered-services.js +37 -7
- package/dist/core/engine/lifecycle/resume.d.ts +2 -2
- package/dist/core/engine/lifecycle/resume.js +45 -7
- package/dist/core/engine/lifecycle/shared.d.ts +34 -0
- package/dist/core/engine/lifecycle/start-exec.d.ts +2 -2
- package/dist/core/engine/lifecycle/start-exec.js +6 -4
- package/dist/core/engine/lifecycle/start.js +1 -0
- package/dist/core/engine/lifecycle/transition.d.ts +1 -1
- package/dist/core/engine/lifecycle/transition.js +9 -3
- package/dist/core/engine/lifecycle.d.ts +1 -1
- package/dist/core/engine/memo-durable-activity.js +8 -10
- package/dist/core/engine/operations-activity.d.ts +1 -1
- package/dist/core/engine/operations-activity.js +8 -6
- package/dist/core/engine/operations-coordination.d.ts +15 -1
- package/dist/core/engine/operations-coordination.js +43 -10
- package/dist/core/engine/operations-time.d.ts +2 -2
- package/dist/core/engine/operations-time.js +40 -6
- package/dist/core/engine/ownership-options.d.ts +1 -0
- package/dist/core/engine/ownership-options.js +14 -0
- package/dist/core/engine/retention.js +4 -0
- package/dist/core/engine/schedule-run-metadata.d.ts +3 -0
- package/dist/core/engine/schedule-run-metadata.js +29 -0
- package/dist/core/engine/schedule-run.js +19 -4
- package/dist/core/engine/schedules.js +7 -3
- package/dist/core/engine/sub-operation.js +6 -13
- package/dist/core/engine/termination/cleanup.js +18 -14
- package/dist/core/engine/termination/finalizer-activity.d.ts +1 -1
- package/dist/core/engine/termination/finalizer-activity.js +5 -1
- package/dist/core/engine/termination/finalizer.js +1 -1
- package/dist/core/engine/validation/schedule-options.d.ts +9 -0
- package/dist/core/engine/validation/schedule-options.js +59 -0
- package/dist/core/engine/validation/schedule.d.ts +3 -8
- package/dist/core/engine/validation/schedule.js +40 -56
- package/dist/core/engine/validation.js +1 -0
- package/dist/core/execution-strategy.d.ts +1 -0
- package/dist/core/inline-execution-strategy.context-options.d.ts +1 -0
- package/dist/core/inline-execution-strategy.context-options.js +3 -0
- package/dist/core/types/activity.d.ts +11 -0
- package/dist/core/types/checkpoint.d.ts +1 -0
- package/dist/core/types/options.d.ts +12 -0
- package/dist/core/types/schedules.d.ts +10 -0
- package/dist/core/types/services-resolution.d.ts +8 -3
- package/dist/core/types/state.d.ts +6 -0
- package/dist/core/types/workflow-builder.d.ts +2 -2
- package/dist/core/types/workflow-context.d.ts +40 -0
- package/dist/core/worker-execution-strategy.d.ts +1 -0
- package/dist/core/worker-inbound-message.d.ts +1 -0
- package/dist/core/worker-inbound-message.js +3 -0
- package/dist/index.d.ts +2 -2
- package/dist/json-schema.js +2 -2
- package/dist/mcp/cli.js +32 -32
- package/dist/observability/index.js +2 -2
- package/dist/runtime/portable.d.ts +1 -1
- package/dist/server/authorization.d.ts +3 -3
- package/dist/server/fault-to-json-rpc.d.ts +2 -1
- package/dist/server/handler.js +28 -28
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +30 -30
- package/dist/server/json-rpc-dispatch.d.ts +2 -2
- package/dist/server/json-rpc-parse.d.ts +1 -1
- package/dist/server/json-rpc-protocol.d.ts +2 -2
- package/dist/server/json-rpc-websocket.d.ts +1 -1
- package/dist/server/operation-fault.d.ts +3 -1
- package/dist/server/operations/async-activity.js +2 -2
- package/dist/server/operations/create-schedule.d.ts +2 -0
- package/dist/server/operations/create-schedule.js +13 -2
- package/dist/server/principal.d.ts +2 -2
- package/dist/server/rest-binding.d.ts +1 -1
- package/dist/server/runtime/task-dispatch.js +6 -0
- package/dist/server/runtime/task-polling.js +9 -1
- package/dist/server/runtime/task-reconciliation.js +2 -1
- package/dist/server/stdio-session.d.ts +4 -4
- package/dist/server/task-queue-types.d.ts +2 -0
- package/dist/server/task-state.d.ts +4 -0
- package/dist/service-worker/index.d.ts +15 -14
- package/dist/service-worker/index.js +28 -28
- package/dist/service-worker/scheduler.d.ts +41 -1
- package/dist/storage/auto.d.ts +36 -18
- package/dist/storage/auto.js +1 -1
- package/dist/storage/compressed-storage.js +1 -1
- package/dist/storage/http.js +2 -2
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/indexeddb.d.ts +6 -1
- package/dist/storage/indexeddb.js +1 -1
- package/dist/storage/interface.d.ts +1 -0
- package/dist/storage/interface.js +1 -1
- package/dist/storage/lmdb.js +1 -1
- package/dist/storage/memory.js +1 -1
- package/dist/storage/neon.js +1 -1
- package/dist/storage/resolve.js +1 -1
- package/dist/storage/scoped-storage.js +1 -1
- package/dist/storage/testing.js +1 -1
- package/dist/storage/turso.js +1 -1
- package/dist/storage/typed-storage.js +1 -1
- package/dist/storage/web-extension.d.ts +1 -1
- package/dist/storage/web-extension.js +1 -1
- package/dist/testing/index.js +33 -33
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker/execute-with-interceptors.d.ts +9 -3
- package/dist/worker/execute-with-interceptors.js +14 -2
- package/dist/worker/long-poll.d.ts +2 -1
- package/dist/worker/long-poll.js +1 -1
- package/dist/worker/protocol-messages.d.ts +2 -0
- package/dist/worker/protocol-schemas.d.ts +8 -0
- package/dist/worker/protocol-schemas.js +1 -0
- package/dist/worker/protocol.js +1 -1
- package/dist/worker/remote-activity-context.d.ts +2 -0
- package/dist/workers/activity-runner.d.ts +2 -0
- package/dist/workers/activity-runner.js +9 -1
- package/dist/workers/workflow-runner.d.ts +2 -1
- package/dist/workers/workflow-runner.js +1 -0
- package/package.json +2 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AlertManager } from "../../alerting/alert-manager.js";
|
|
1
2
|
import {
|
|
2
3
|
KEYS,
|
|
3
4
|
requireStorageCapability
|
|
@@ -19,9 +20,9 @@ import {
|
|
|
19
20
|
} from "./aggregate.js";
|
|
20
21
|
import {
|
|
21
22
|
completeAsyncActivity as completeAsyncActivityFromInternals,
|
|
22
|
-
failAsyncActivity as failAsyncActivityFromInternals
|
|
23
|
-
recoverPendingAsyncActivities
|
|
23
|
+
failAsyncActivity as failAsyncActivityFromInternals
|
|
24
24
|
} from "./async-activity-completion.js";
|
|
25
|
+
import { recoverPendingAsyncActivities } from "./async-activity-records.js";
|
|
25
26
|
import { broadcast as broadcastFromInternals } from "./broadcast.js";
|
|
26
27
|
import {
|
|
27
28
|
cancelAll as cancelAllWorkflows,
|
|
@@ -52,7 +53,6 @@ import {
|
|
|
52
53
|
import {
|
|
53
54
|
copyWorkflowDefinition,
|
|
54
55
|
createActivityWorkerDispatcher,
|
|
55
|
-
createAlertManagerForEngine,
|
|
56
56
|
createExecutionStrategyBundle,
|
|
57
57
|
definitionEntries,
|
|
58
58
|
resolveEngineInterceptors,
|
|
@@ -70,7 +70,9 @@ import {
|
|
|
70
70
|
createQueuedInlineWorkflowStartHandler,
|
|
71
71
|
createSecondInstanceDetectorResolver,
|
|
72
72
|
drainQueuedInlineWorkflowStartsForEngine,
|
|
73
|
-
isActivityDefinition
|
|
73
|
+
isActivityDefinition,
|
|
74
|
+
shouldStartEngineScheduler,
|
|
75
|
+
validateEngineCreateBackgroundTaskOptions
|
|
74
76
|
} from "./engine-runtime-helpers.js";
|
|
75
77
|
import { EngineCreateNameMismatchError, EngineDisposedError } from "./errors.js";
|
|
76
78
|
import { assertLeaseHeldForEngineWork, commitFencedEngineWrite } from "./fenced-write.js";
|
|
@@ -209,9 +211,20 @@ export { assertCompatiblePersistedDataVersion };
|
|
|
209
211
|
export const ENGINE_PARKED_WORKFLOW_COUNT_FOR_TESTING = Symbol("engineParkedWorkflowCountForTesting"), ENGINE_SIGNAL_WAITER_COUNT_FOR_TESTING = Symbol("engineSignalWaiterCountForTesting"), ENGINE_SLEEP_RESOLVER_COUNT_FOR_TESTING = Symbol("engineSleepResolverCountForTesting"), ENGINE_LEASE_SYNCHRONOUS_DISPOSE_WARNING_NAME = "WeftEngineLeaseSynchronousDisposeWarning";
|
|
210
212
|
|
|
211
213
|
export { ENGINE_LEASE_LOST_WARNING_NAME };
|
|
214
|
+
function scheduleDefinitionFromInternals(internals, definition) {
|
|
215
|
+
const workflowType = typeof definition.workflow === "string" ? definition.workflow : definition.workflow.name, definitionSpec = definition.every !== void 0 ? { every: definition.every } : { cron: definition.cron ?? "" };
|
|
216
|
+
return scheduleFromInternals(internals, workflowType, definition.input, definitionSpec, {
|
|
217
|
+
...definition.id !== void 0 && { id: definition.id },
|
|
218
|
+
...definition.description !== void 0 && { description: definition.description },
|
|
219
|
+
...definition.overlapPolicy !== void 0 && { overlap: definition.overlapPolicy },
|
|
220
|
+
...definition.backfill !== void 0 && { backfill: definition.backfill },
|
|
221
|
+
...definition.jitter !== void 0 && { jitter: definition.jitter }
|
|
222
|
+
});
|
|
223
|
+
}
|
|
212
224
|
|
|
213
225
|
export class Engine extends EventTarget {
|
|
214
226
|
static async create(options) {
|
|
227
|
+
validateEngineCreateBackgroundTaskOptions(options);
|
|
215
228
|
const engine = new Engine(options);
|
|
216
229
|
try {
|
|
217
230
|
await assertCompatiblePersistedDataVersion(getInternals(engine).storage);
|
|
@@ -228,7 +241,7 @@ export class Engine extends EventTarget {
|
|
|
228
241
|
await engine.#acquireLeaseIfConfigured();
|
|
229
242
|
if (options.recover !== !1)
|
|
230
243
|
await engine.recoverAll(options.acknowledgeUnknownWorkflowTypes !== void 0 ? { acknowledgeUnknownWorkflowTypes: options.acknowledgeUnknownWorkflowTypes } : {});
|
|
231
|
-
if (options.
|
|
244
|
+
if (shouldStartEngineScheduler(options, getInternals(engine).options.backgroundTaskMode))
|
|
232
245
|
getInternals(engine).scheduler.start();
|
|
233
246
|
} catch (error) {
|
|
234
247
|
await engine[Symbol.asyncDispose]();
|
|
@@ -267,6 +280,7 @@ export class Engine extends EventTarget {
|
|
|
267
280
|
getInternals(this).updateWaitersByWorkflow = new Map;
|
|
268
281
|
getInternals(this).sleepResolvers = new Map;
|
|
269
282
|
getInternals(this).sleepResolversByWorkflow = new Map;
|
|
283
|
+
getInternals(this).sleepTimersFiredWithoutResolver = new Map;
|
|
270
284
|
getInternals(this).interceptors = resolveEngineInterceptors(options);
|
|
271
285
|
getInternals(this).composedWorkflowInterceptor = void 0;
|
|
272
286
|
getInternals(this).composedActivityInterceptor = void 0;
|
|
@@ -330,7 +344,7 @@ export class Engine extends EventTarget {
|
|
|
330
344
|
cleanupInterval: null,
|
|
331
345
|
secondInstanceDetectionInterval: null,
|
|
332
346
|
testToken: consumeNextEngineLeakWarningTokenForTesting()
|
|
333
|
-
}, cleanupInterval = setInterval(createCleanupIntervalTick(weakEngine, cleanupIntervalDisposalTracker), 60000);
|
|
347
|
+
}, cleanupInterval = resolvedOptions.backgroundTaskMode === "automatic" ? setInterval(createCleanupIntervalTick(weakEngine, cleanupIntervalDisposalTracker), 60000) : null;
|
|
334
348
|
cleanupIntervalDisposalTracker.cleanupInterval = cleanupInterval;
|
|
335
349
|
getInternals(this).cleanupInterval = cleanupInterval;
|
|
336
350
|
getInternals(this).cleanupIntervalDisposalTracker = cleanupIntervalDisposalTracker;
|
|
@@ -353,7 +367,7 @@ export class Engine extends EventTarget {
|
|
|
353
367
|
getInternals(this).workflowVisibilityWatermarkExpiresAt = void 0;
|
|
354
368
|
getInternals(this).activityWorkerDispatcher = createActivityWorkerDispatcher(options?.activityExecution);
|
|
355
369
|
getInternals(this).strategy.onMessage(this.#handleStrategyMessage.bind(this));
|
|
356
|
-
getInternals(this).alertManager =
|
|
370
|
+
getInternals(this).alertManager = options?.alerts ? new AlertManager(this, options.alerts, getNow, resolvedOptions.backgroundTaskMode === "automatic") : null;
|
|
357
371
|
this.#ensureRetentionSweepInterval();
|
|
358
372
|
this.#startSecondInstanceDetection();
|
|
359
373
|
}
|
|
@@ -575,6 +589,18 @@ export class Engine extends EventTarget {
|
|
|
575
589
|
getRetentionOverview() {
|
|
576
590
|
return getRetentionOverviewSnapshot(getInternals(this), (type) => resolveWorkflowTypeRetention(getInternals(this), type));
|
|
577
591
|
}
|
|
592
|
+
async runMaintenance(now = getInternals(this).options.getNow()) {
|
|
593
|
+
const internals = getInternals(this);
|
|
594
|
+
await internals.scheduler.tick(now);
|
|
595
|
+
try {
|
|
596
|
+
await internals.updateCoordinator.cleanupExpiredResponses();
|
|
597
|
+
} catch (error) {
|
|
598
|
+
this.#createTerminationCallbacks().handleCleanupError("cleanupExpiredResponses", error);
|
|
599
|
+
}
|
|
600
|
+
if (this.#hasConfiguredRetention())
|
|
601
|
+
await this.#runRetentionSweep();
|
|
602
|
+
internals.alertManager?.tick();
|
|
603
|
+
}
|
|
578
604
|
async purge(filter) {
|
|
579
605
|
return purgeWorkflows(getInternals(this), filter, (workflowId) => cleanupWaitersFromTermination(getInternals(this), workflowId, this.#createTerminationCallbacks()));
|
|
580
606
|
}
|
|
@@ -599,15 +625,8 @@ export class Engine extends EventTarget {
|
|
|
599
625
|
return untagAllWorkflows(getInternals(this), filter, tags, options);
|
|
600
626
|
}
|
|
601
627
|
async schedule(typeOrDefinition, input, spec, options) {
|
|
602
|
-
if (typeof typeOrDefinition === "object")
|
|
603
|
-
|
|
604
|
-
return scheduleFromInternals(getInternals(this), workflowType, definition.input, definitionSpec, {
|
|
605
|
-
...definition.id !== void 0 && { id: definition.id },
|
|
606
|
-
...definition.overlapPolicy !== void 0 && { overlap: definition.overlapPolicy },
|
|
607
|
-
...definition.backfill !== void 0 && { backfill: definition.backfill },
|
|
608
|
-
...definition.jitter !== void 0 && { jitter: definition.jitter }
|
|
609
|
-
});
|
|
610
|
-
}
|
|
628
|
+
if (typeof typeOrDefinition === "object")
|
|
629
|
+
return scheduleDefinitionFromInternals(getInternals(this), typeOrDefinition);
|
|
611
630
|
if (spec === void 0)
|
|
612
631
|
throw Error("A cron string or schedule spec must be provided when scheduling by workflow type.");
|
|
613
632
|
return scheduleFromInternals(getInternals(this), typeOrDefinition, input, spec, options);
|
|
@@ -58,7 +58,7 @@ async function startQueuedInlineWorkflowExecution(internals, start, callbacks) {
|
|
|
58
58
|
return;
|
|
59
59
|
internals.queuedInlineWorkflowStartIds.delete(start.workflowId);
|
|
60
60
|
internals.engine.dispatchEvent(new WorkflowStartedEvent(start.workflowId, start.workflowType, start.input));
|
|
61
|
-
startWorkflowExecution(internals, start.workflowId, start.workflowType, start.input, start.checkpoint, start.nestingDepth, start.executionDeadline, start.executionStateOwnerId);
|
|
61
|
+
startWorkflowExecution(internals, start.workflowId, state.workflowExecutionToken, start.workflowType, start.input, start.checkpoint, start.nestingDepth, start.executionDeadline, start.executionStateOwnerId);
|
|
62
62
|
await callbacks.processPendingUpdatesAfterInlineAdvance(start.workflowId);
|
|
63
63
|
} finally {
|
|
64
64
|
internals.queuedInlineWorkflowStartIds.delete(start.workflowId);
|
|
@@ -90,8 +90,30 @@ export interface EngineInternals {
|
|
|
90
90
|
conditionWaiters: Map<string, () => void>;
|
|
91
91
|
updateWaiters: Map<string, (payload: unknown) => void>;
|
|
92
92
|
updateWaitersByWorkflow: Map<string, TrackedWaiterKeys>;
|
|
93
|
-
|
|
93
|
+
/**
|
|
94
|
+
* In-process sleep resolvers keyed by `${workflowId}:${operationId}`. Each
|
|
95
|
+
* carries the run's expected `fireAt` so a fired timer only settles the sleep
|
|
96
|
+
* whose deadline it represents — a stale timer from a terminated run that
|
|
97
|
+
* reused the same deterministic operationId (its durable timer outlives
|
|
98
|
+
* terminal cleanup) is ignored rather than resolving a replacement run early.
|
|
99
|
+
*/
|
|
100
|
+
sleepResolvers: Map<string, {
|
|
101
|
+
resolve: () => void;
|
|
102
|
+
fireAt: number;
|
|
103
|
+
}>;
|
|
94
104
|
sleepResolversByWorkflow: Map<string, Set<string>>;
|
|
105
|
+
/**
|
|
106
|
+
* Per-workflow maps of `operationId` → fired timer `fireAt` for sleep timers
|
|
107
|
+
* the scheduler tick fired before the resolver was registered. Keyed by
|
|
108
|
+
* `workflowId` so per-workflow cleanup (`cleanupSleepResolvers`,
|
|
109
|
+
* `evictSleepResolversWithoutResolving`) can call `.delete(workflowId)` and
|
|
110
|
+
* sweep all markers in O(1) — including orphaned ones where the timer fired
|
|
111
|
+
* while the workflow was suspended (no `sleepResolversByWorkflow` entry) or
|
|
112
|
+
* during recovery of an already-elapsed sleep that takes the early-return
|
|
113
|
+
* path. The `fireAt` lets `processSleepOperation` ignore a marker left by a
|
|
114
|
+
* stale earlier-run timer. Cleared entirely at engine disposal.
|
|
115
|
+
*/
|
|
116
|
+
sleepTimersFiredWithoutResolver: Map<string, Map<string, number>>;
|
|
95
117
|
interceptors: Interceptor[];
|
|
96
118
|
composedWorkflowInterceptor: ComposedWorkflowInterceptor | null | undefined;
|
|
97
119
|
composedActivityInterceptor: ComposedActivityInterceptor | null | undefined;
|
|
@@ -164,13 +186,13 @@ export interface EngineInternals {
|
|
|
164
186
|
* by their durable task token. Mirrored to storage (`KEYS.asyncActivity`) and
|
|
165
187
|
* reloaded by `recoverAll()`. See `async-activity-completion.ts`.
|
|
166
188
|
*/
|
|
167
|
-
pendingAsyncActivities: Map<string, import('./async-activity-
|
|
189
|
+
pendingAsyncActivities: Map<string, import('./async-activity-records.ts').PendingAsyncActivity>;
|
|
168
190
|
/**
|
|
169
191
|
* Completed or failed async-activity tokens that were consumed before inline
|
|
170
192
|
* recovery adopted the workflow generator. Replay drains these by workflow id
|
|
171
193
|
* when it reaches the same deterministic async-activity token again.
|
|
172
194
|
*/
|
|
173
|
-
pendingAsyncActivityResolutions: Map<string, import('./async-activity-
|
|
195
|
+
pendingAsyncActivityResolutions: Map<string, import('./async-activity-records.ts').PendingAsyncActivityResolution[]>;
|
|
174
196
|
pendingStarts: Set<string>;
|
|
175
197
|
pendingScheduleCreations: Set<string>;
|
|
176
198
|
workflowsNeedingTerminalCleanup: Set<string>;
|
|
@@ -22,6 +22,7 @@ export function createForkedWorkflowState(_internals, workflowId, sourceState, v
|
|
|
22
22
|
type: sourceState.type,
|
|
23
23
|
status: "running",
|
|
24
24
|
input: sourceState.input,
|
|
25
|
+
workflowExecutionToken: crypto.randomUUID(),
|
|
25
26
|
versionTuple,
|
|
26
27
|
executionStateOwnerId: workflowId,
|
|
27
28
|
createdAt: forkedAt,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WorkflowState } from '../../types.ts';
|
|
1
|
+
import type { WorkflowServicesResolverInfo, WorkflowState } from '../../types.ts';
|
|
2
2
|
import type { EngineInternals } from '../internals.ts';
|
|
3
3
|
/**
|
|
4
4
|
* Re-provide a recovered inline workflow's non-serialized `services` before its
|
|
@@ -36,7 +36,9 @@ import type { EngineInternals } from '../internals.ts';
|
|
|
36
36
|
* @param onCommitError - Records a fail-warn when `failRun` itself throws, so the
|
|
37
37
|
* swallowed terminal-commit fault is still observable.
|
|
38
38
|
*/
|
|
39
|
-
export declare function reprovideRecoveredServices(internals: EngineInternals, state: WorkflowState, failRun: (workflowId: string, error: Error) => Promise<void>, onCommitError: (source: string, error: unknown, workflowId: string) => void, dispatchDiagnostic?: (event: Event) => void): Promise<boolean>;
|
|
39
|
+
export declare function reprovideRecoveredServices(internals: EngineInternals, state: WorkflowState, failRun: (workflowId: string, error: Error) => Promise<void>, onCommitError: (source: string, error: unknown, workflowId: string) => void, dispatchDiagnostic?: (event: Event) => void, resolverInfo?: WorkflowServicesResolverInfo): Promise<boolean>;
|
|
40
|
+
/** Build the durable recovery context shared by the services resolver and recovery hook. */
|
|
41
|
+
export declare function workflowServicesResolverInfoFromState(internals: EngineInternals, state: WorkflowState): Promise<WorkflowServicesResolverInfo>;
|
|
40
42
|
/**
|
|
41
43
|
* The canonical terminal error for any run whose services could not be provided —
|
|
42
44
|
* both recovery re-provision paths and a scheduled-occurrence launch. Shared so
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { KEYS } from "../../../storage/interface.js";
|
|
2
2
|
import { DevelopmentWarningEvent } from "../../events.js";
|
|
3
|
+
import { decodeScheduleRunMetadata } from "../schedule-run-metadata.js";
|
|
4
|
+
import { loadScheduleState } from "../storage-io.js";
|
|
3
5
|
const RESOLVE_WORKFLOW_SERVICES_OPTION_PATH = "EngineOptions.resolveWorkflowServices";
|
|
4
|
-
export async function reprovideRecoveredServices(internals, state, failRun, onCommitError, dispatchDiagnostic = () => {}) {
|
|
6
|
+
export async function reprovideRecoveredServices(internals, state, failRun, onCommitError, dispatchDiagnostic = () => {}, resolverInfo) {
|
|
5
7
|
const resolver = internals.options.resolveWorkflowServices;
|
|
6
8
|
if (internals.inlineStrategy === null)
|
|
7
9
|
return !1;
|
|
@@ -20,13 +22,9 @@ export async function reprovideRecoveredServices(internals, state, failRun, onCo
|
|
|
20
22
|
return !0;
|
|
21
23
|
}
|
|
22
24
|
let reason;
|
|
25
|
+
const info = resolverInfo ?? await workflowServicesResolverInfoFromState(internals, state);
|
|
23
26
|
try {
|
|
24
|
-
const resolution = await resolver(
|
|
25
|
-
workflowId: state.id,
|
|
26
|
-
workflowType: state.type,
|
|
27
|
-
input: state.input,
|
|
28
|
-
launchOptions: launchOptionsFromWorkflowState(state)
|
|
29
|
-
});
|
|
27
|
+
const resolution = await resolver(info);
|
|
30
28
|
if (resolution.status === "available") {
|
|
31
29
|
internals.workflowServices.set(state.id, resolution.services);
|
|
32
30
|
return !1;
|
|
@@ -42,6 +40,16 @@ export async function reprovideRecoveredServices(internals, state, failRun, onCo
|
|
|
42
40
|
}
|
|
43
41
|
return !0;
|
|
44
42
|
}
|
|
43
|
+
export async function workflowServicesResolverInfoFromState(internals, state) {
|
|
44
|
+
const schedule = await scheduleFromWorkflowState(internals, state);
|
|
45
|
+
return {
|
|
46
|
+
workflowId: state.id,
|
|
47
|
+
workflowType: state.type,
|
|
48
|
+
input: state.input,
|
|
49
|
+
launchOptions: launchOptionsFromWorkflowState(state),
|
|
50
|
+
...schedule !== null ? { schedule } : {}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
45
53
|
export function unavailableServicesError(workflowId, reason) {
|
|
46
54
|
return Error(`Workflow "${workflowId}" services unavailable: ${reason}`);
|
|
47
55
|
}
|
|
@@ -54,3 +62,25 @@ function launchOptionsFromWorkflowState(state) {
|
|
|
54
62
|
...state.tags !== void 0 && state.tags.length > 0 ? { tags: [...state.tags] } : {}
|
|
55
63
|
};
|
|
56
64
|
}
|
|
65
|
+
async function scheduleFromWorkflowState(internals, state) {
|
|
66
|
+
const bytes = await internals.storage.get(KEYS.scheduleRun(state.id));
|
|
67
|
+
if (bytes === null)
|
|
68
|
+
return null;
|
|
69
|
+
const metadata = decodeScheduleRunMetadata(bytes);
|
|
70
|
+
if (metadata === null)
|
|
71
|
+
return null;
|
|
72
|
+
const scheduleState = await loadScheduleState(internals, metadata.id);
|
|
73
|
+
if (scheduleState === null)
|
|
74
|
+
return null;
|
|
75
|
+
if (scheduleState.workflowType !== state.type)
|
|
76
|
+
return null;
|
|
77
|
+
if (scheduleState.overlap === "allow")
|
|
78
|
+
return metadata.occurrence !== void 0 ? metadata : null;
|
|
79
|
+
if (scheduleState.currentWorkflowId === state.id)
|
|
80
|
+
return metadata;
|
|
81
|
+
if (metadata.occurrence !== void 0 && scheduleState.nextFireAt === metadata.occurrence)
|
|
82
|
+
return metadata;
|
|
83
|
+
if (metadata.occurrence === void 0 && scheduleState.overlap === "queue" && scheduleState.queuedRuns > 0)
|
|
84
|
+
return metadata;
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { type WorkflowHandle } from '../handles.ts';
|
|
2
2
|
import type { EngineInternals } from '../internals.ts';
|
|
3
|
-
import { type LifecycleCallbacks } from './shared.ts';
|
|
4
|
-
export declare function resumeWorkflowFromStorage(internals: EngineInternals, workflowId: string, dispatchResumedEvent: boolean, callbacks: LifecycleCallbacks): Promise<WorkflowHandle>;
|
|
3
|
+
import { type LifecycleCallbacks, type RecoverAllOptions } from './shared.ts';
|
|
4
|
+
export declare function resumeWorkflowFromStorage(internals: EngineInternals, workflowId: string, dispatchResumedEvent: boolean, callbacks: LifecycleCallbacks, onRecoveredWorkflow?: RecoverAllOptions['onRecoveredWorkflow']): Promise<WorkflowHandle>;
|
|
@@ -15,15 +15,47 @@ import { getComposedWorkflowInterceptor } from "../strategy-helpers.js";
|
|
|
15
15
|
import { decodeWorkflowState } from "../validation.js";
|
|
16
16
|
import { buildWorkflowVisibilityIndexTransition } from "../workflow-indexes.js";
|
|
17
17
|
import { prepareResumeState } from "./persist.js";
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
reprovideRecoveredServices,
|
|
20
|
+
workflowServicesResolverInfoFromState
|
|
21
|
+
} from "./recovered-services.js";
|
|
19
22
|
import {
|
|
20
23
|
enforceHistoryPolicyBeforeReplay,
|
|
21
24
|
loadTerminalCleanupTrackedState,
|
|
22
25
|
loadWorkflowStartHeaders,
|
|
23
26
|
setWorkflowStartHeaders
|
|
24
27
|
} from "./shared.js";
|
|
25
|
-
async function prepareRecoveredServicesOrFail(internals, state, callbacks) {
|
|
26
|
-
return reprovideRecoveredServices(internals, state, callbacks.failWorkflowForUnavailableServices, callbacks.handleCleanupError, callbacks.dispatchEvent);
|
|
28
|
+
async function prepareRecoveredServicesOrFail(internals, state, callbacks, resolverInfo) {
|
|
29
|
+
return reprovideRecoveredServices(internals, state, callbacks.failWorkflowForUnavailableServices, callbacks.handleCleanupError, callbacks.dispatchEvent, resolverInfo);
|
|
30
|
+
}
|
|
31
|
+
async function runRecoveredWorkflowHookOrFail(internals, state, handle, resolverInfo, onRecoveredWorkflow, callbacks) {
|
|
32
|
+
const info = {
|
|
33
|
+
...resolverInfo,
|
|
34
|
+
handle,
|
|
35
|
+
launchOptions: resolverInfo.launchOptions ?? { id: state.id },
|
|
36
|
+
services: internals.workflowServices.get(state.id)
|
|
37
|
+
};
|
|
38
|
+
try {
|
|
39
|
+
await onRecoveredWorkflow(info);
|
|
40
|
+
return !1;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
const reason = error instanceof Error ? error.message : String(error), hookError = Error(`Recovery hook failed for workflow "${state.id}": ${reason}`);
|
|
43
|
+
try {
|
|
44
|
+
await callbacks.failWorkflowForRecoveryHook(state.id, hookError);
|
|
45
|
+
} catch (commitError) {
|
|
46
|
+
callbacks.handleCleanupError("onRecoveredWorkflow", commitError, state.id);
|
|
47
|
+
}
|
|
48
|
+
return !0;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function prepareRecoveredWorkflowOrFail(internals, state, callbacks, onRecoveredWorkflow) {
|
|
52
|
+
const resolverInfo = onRecoveredWorkflow === void 0 ? void 0 : await workflowServicesResolverInfoFromState(internals, state), handle = callbacks.getHandle(state.id);
|
|
53
|
+
if (await prepareRecoveredServicesOrFail(internals, state, callbacks, resolverInfo))
|
|
54
|
+
return { handle, shouldStop: !0 };
|
|
55
|
+
if (onRecoveredWorkflow === void 0 || resolverInfo === void 0)
|
|
56
|
+
return { handle, shouldStop: !1 };
|
|
57
|
+
const shouldStop = await runRecoveredWorkflowHookOrFail(internals, state, handle, resolverInfo, onRecoveredWorkflow, callbacks);
|
|
58
|
+
return { handle, shouldStop };
|
|
27
59
|
}
|
|
28
60
|
function assertResumeNotTerminating(internals, workflowId) {
|
|
29
61
|
if (internals.terminalizingWorkflows.has(workflowId))
|
|
@@ -51,6 +83,9 @@ async function relaunchInlineWorkflowAfterResume(internals, latestState, args) {
|
|
|
51
83
|
await rehydrateChildCancellationHandlers(internals, workflowId, callbacks);
|
|
52
84
|
const context = new Context({
|
|
53
85
|
workflowId,
|
|
86
|
+
...latestState.workflowExecutionToken !== void 0 && {
|
|
87
|
+
workflowExecutionToken: latestState.workflowExecutionToken
|
|
88
|
+
},
|
|
54
89
|
workflowType: latestState.type,
|
|
55
90
|
startedAt: getWorkflowExecutionStartedAt(latestState),
|
|
56
91
|
abortController: workflowAbort,
|
|
@@ -85,6 +120,9 @@ async function relaunchWorkerWorkflowAfterResume(internals, latestState, args) {
|
|
|
85
120
|
const serialized = serializeCheckpoint(resumeCheckpoint);
|
|
86
121
|
internals.strategy.startWorkflow({
|
|
87
122
|
workflowId,
|
|
123
|
+
...latestState.workflowExecutionToken !== void 0 && {
|
|
124
|
+
workflowExecutionToken: latestState.workflowExecutionToken
|
|
125
|
+
},
|
|
88
126
|
workflowType: latestState.type,
|
|
89
127
|
input: latestState.input,
|
|
90
128
|
checkpoint: serialized,
|
|
@@ -132,7 +170,7 @@ async function reactivateSuspendedWorkflowState(internals, state) {
|
|
|
132
170
|
}) : []
|
|
133
171
|
], [], () => Error(`Resume of workflow "${state.id}" lost its CAS race.`));
|
|
134
172
|
}
|
|
135
|
-
export async function resumeWorkflowFromStorage(internals, workflowId, dispatchResumedEvent, callbacks) {
|
|
173
|
+
export async function resumeWorkflowFromStorage(internals, workflowId, dispatchResumedEvent, callbacks, onRecoveredWorkflow) {
|
|
136
174
|
const stateBytes = await internals.storage.get(KEYS.workflow(workflowId));
|
|
137
175
|
if (!stateBytes)
|
|
138
176
|
throw Error(`Workflow "${workflowId}" not found in storage`);
|
|
@@ -150,9 +188,9 @@ export async function resumeWorkflowFromStorage(internals, workflowId, dispatchR
|
|
|
150
188
|
return callbacks.getHandle(workflowId);
|
|
151
189
|
const workflowStartHeaders = await loadWorkflowStartHeaders(internals, workflowId, callbacks);
|
|
152
190
|
await loadTerminalCleanupTrackedState(internals, workflowId, callbacks);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
191
|
+
const { handle, shouldStop } = await prepareRecoveredWorkflowOrFail(internals, state, callbacks, onRecoveredWorkflow);
|
|
192
|
+
if (shouldStop)
|
|
193
|
+
return handle;
|
|
156
194
|
await callbacks.runSerializedWorkflowStateWrite(workflowId, () => performSerializedResume(internals, {
|
|
157
195
|
workflowId,
|
|
158
196
|
resumeCheckpoint,
|
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
import type { ComposedWorkflowInterceptor } from '../../interceptor.ts';
|
|
2
|
+
import type { WorkflowServicesResolverLaunchOptions, WorkflowServicesResolverScheduleInfo } from '../../types.ts';
|
|
2
3
|
import type { QueuedInlineWorkflowExecutionStart } from '../engine-internal-types.ts';
|
|
3
4
|
import { type WorkflowHandle } from '../handles.ts';
|
|
4
5
|
import type { EngineInternals } from '../internals.ts';
|
|
5
6
|
export type RegistrationEntry = EngineInternals['registrations'] extends Map<string, infer Entry> ? Entry : never;
|
|
6
7
|
export declare const FORK_LINEAGE_ATTRIBUTE = "weft:forkedFrom";
|
|
7
8
|
export declare const EMPTY_STORAGE_VALUE: Uint8Array<ArrayBuffer>;
|
|
9
|
+
/**
|
|
10
|
+
* Durable run context passed to {@link RecoverAllOptions.onRecoveredWorkflow}
|
|
11
|
+
* before recovered user code advances.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import type { RecoveredWorkflowInfo } from '@lostgradient/weft';
|
|
16
|
+
*
|
|
17
|
+
* function registerSurface(info: RecoveredWorkflowInfo): void {
|
|
18
|
+
* console.log(`Recovered ${info.workflowType}/${info.workflowId}`);
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export interface RecoveredWorkflowInfo {
|
|
23
|
+
/** Handle for the recovered run. */
|
|
24
|
+
handle: WorkflowHandle;
|
|
25
|
+
workflowId: string;
|
|
26
|
+
workflowType: string;
|
|
27
|
+
input: unknown;
|
|
28
|
+
launchOptions: WorkflowServicesResolverLaunchOptions;
|
|
29
|
+
schedule?: WorkflowServicesResolverScheduleInfo;
|
|
30
|
+
/** Re-provided host services, or `undefined` when the run did not use services. */
|
|
31
|
+
services: unknown;
|
|
32
|
+
}
|
|
8
33
|
/**
|
|
9
34
|
* Options for {@link Engine.recoverAll}. The acknowledgement flag is an
|
|
10
35
|
* explicit escape hatch for deployments that intentionally skip stored
|
|
@@ -25,6 +50,13 @@ export type RecoverAllOptions = {
|
|
|
25
50
|
* rolling deploys or explicit operator storage repair.
|
|
26
51
|
*/
|
|
27
52
|
acknowledgeUnknownWorkflowTypes?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Register consumer-owned live state for each recovered run after services
|
|
55
|
+
* are re-provided but before the workflow generator advances. The callback is
|
|
56
|
+
* awaited. If it throws, only that run fails with a `system` failure and
|
|
57
|
+
* recovery continues with its siblings.
|
|
58
|
+
*/
|
|
59
|
+
onRecoveredWorkflow?: (info: RecoveredWorkflowInfo) => void | Promise<void>;
|
|
28
60
|
};
|
|
29
61
|
export type LifecycleCallbacks = {
|
|
30
62
|
dispatchEvent: (event: Event) => void;
|
|
@@ -56,6 +88,8 @@ export type LifecycleCallbacks = {
|
|
|
56
88
|
* `error` is the canonical {@link unavailableServicesError}.
|
|
57
89
|
*/
|
|
58
90
|
failWorkflowForUnavailableServices: (workflowId: string, error: Error) => Promise<void>;
|
|
91
|
+
/** Fail one recovered workflow whose pre-resume consumer hook threw. */
|
|
92
|
+
failWorkflowForRecoveryHook: (workflowId: string, error: Error) => Promise<void>;
|
|
59
93
|
/**
|
|
60
94
|
* Force a recovered workflow to a terminal `failed` state because its
|
|
61
95
|
* persisted checkpoint cannot be decoded on this runtime. The failure is
|
|
@@ -2,8 +2,8 @@ import type { Checkpoint, StartOptions, WorkflowState } from '../../types.ts';
|
|
|
2
2
|
import type { EngineInternals } from '../internals.ts';
|
|
3
3
|
import { type LifecycleCallbacks, type RegistrationEntry } from './shared.ts';
|
|
4
4
|
export declare function runWorkflowStartInterceptor(_internals: EngineInternals, workflowId: string, workflowType: string, input: unknown, parentHeaders: Map<string, string> | undefined, callbacks: LifecycleCallbacks): Map<string, string> | undefined;
|
|
5
|
-
export declare function startWorkflowExecution(internals: EngineInternals, workflowId: string, workflowType: string, input: unknown, checkpoint: Checkpoint, nestingDepth: number, executionDeadline: number | undefined, executionStateOwnerId: string, _callbacks?: LifecycleCallbacks): void;
|
|
6
|
-
export declare function beginWorkflowExecution(internals: EngineInternals, workflowId: string, workflowType: string, input: unknown, checkpoint: Checkpoint, executionDeadline: number | undefined, executionStateOwnerId: string, _registration: RegistrationEntry, callbacks: LifecycleCallbacks, onStarted?: () => void): void;
|
|
5
|
+
export declare function startWorkflowExecution(internals: EngineInternals, workflowId: string, workflowExecutionToken: string | undefined, workflowType: string, input: unknown, checkpoint: Checkpoint, nestingDepth: number, executionDeadline: number | undefined, executionStateOwnerId: string, _callbacks?: LifecycleCallbacks): void;
|
|
6
|
+
export declare function beginWorkflowExecution(internals: EngineInternals, workflowId: string, workflowExecutionToken: string | undefined, workflowType: string, input: unknown, checkpoint: Checkpoint, executionDeadline: number | undefined, executionStateOwnerId: string, _registration: RegistrationEntry, callbacks: LifecycleCallbacks, onStarted?: () => void): void;
|
|
7
7
|
/**
|
|
8
8
|
* `defer: false` is an inline-only liveness gate: it awaits the moment the
|
|
9
9
|
* generator is first driven. A worker-mode start queues to the Worker transport
|
|
@@ -20,12 +20,13 @@ export function runWorkflowStartInterceptor(_internals, workflowId, workflowType
|
|
|
20
20
|
});
|
|
21
21
|
return capturedHeaders;
|
|
22
22
|
}
|
|
23
|
-
export function startWorkflowExecution(internals, workflowId, workflowType, input, checkpoint, nestingDepth, executionDeadline, executionStateOwnerId, _callbacks) {
|
|
23
|
+
export function startWorkflowExecution(internals, workflowId, workflowExecutionToken, workflowType, input, checkpoint, nestingDepth, executionDeadline, executionStateOwnerId, _callbacks) {
|
|
24
24
|
if (nestingDepth !== 0)
|
|
25
25
|
internals.workflowNestingDepths.set(workflowId, nestingDepth);
|
|
26
26
|
internals.workflowTypeByWorkflowId.set(workflowId, workflowType);
|
|
27
27
|
internals.strategy.startWorkflow({
|
|
28
28
|
workflowId,
|
|
29
|
+
...workflowExecutionToken !== void 0 && { workflowExecutionToken },
|
|
29
30
|
workflowType,
|
|
30
31
|
input,
|
|
31
32
|
checkpoint: serializeCheckpoint(checkpoint),
|
|
@@ -39,12 +40,13 @@ export function startWorkflowExecution(internals, workflowId, workflowType, inpu
|
|
|
39
40
|
}
|
|
40
41
|
});
|
|
41
42
|
}
|
|
42
|
-
export function beginWorkflowExecution(internals, workflowId, workflowType, input, checkpoint, executionDeadline, executionStateOwnerId, _registration, callbacks, onStarted) {
|
|
43
|
+
export function beginWorkflowExecution(internals, workflowId, workflowExecutionToken, workflowType, input, checkpoint, executionDeadline, executionStateOwnerId, _registration, callbacks, onStarted) {
|
|
43
44
|
const nestingDepth = internals.pendingNestingDepth ?? 0;
|
|
44
45
|
internals.pendingNestingDepth = void 0;
|
|
45
46
|
if (internals.inlineStrategy !== null) {
|
|
46
47
|
callbacks.queueInlineWorkflowExecutionStart({
|
|
47
48
|
workflowId,
|
|
49
|
+
...workflowExecutionToken !== void 0 && { workflowExecutionToken },
|
|
48
50
|
workflowType,
|
|
49
51
|
input,
|
|
50
52
|
checkpoint,
|
|
@@ -56,7 +58,7 @@ export function beginWorkflowExecution(internals, workflowId, workflowType, inpu
|
|
|
56
58
|
return;
|
|
57
59
|
}
|
|
58
60
|
callbacks.dispatchEvent(new WorkflowStartedEvent(workflowId, workflowType, input));
|
|
59
|
-
startWorkflowExecution(internals, workflowId, workflowType, input, checkpoint, nestingDepth, executionDeadline, executionStateOwnerId, callbacks);
|
|
61
|
+
startWorkflowExecution(internals, workflowId, workflowExecutionToken, workflowType, input, checkpoint, nestingDepth, executionDeadline, executionStateOwnerId, callbacks);
|
|
60
62
|
onStarted?.();
|
|
61
63
|
}
|
|
62
64
|
export function assertDeferSupported(internals, options, isDelayedStart) {
|
|
@@ -71,7 +73,7 @@ export async function beginExecutionAwaitingLiveness(internals, params, workflow
|
|
|
71
73
|
if (params.isDelayed)
|
|
72
74
|
return;
|
|
73
75
|
const liveness = params.options?.defer === !1 ? Promise.withResolvers() : void 0;
|
|
74
|
-
beginWorkflowExecution(internals, workflowId, params.type, params.input, params.checkpoint, params.state.executionDeadline, params.state.executionStateOwnerId ?? workflowId, params.registration, callbacks, liveness ? () => liveness.resolve() : void 0);
|
|
76
|
+
beginWorkflowExecution(internals, workflowId, params.state.workflowExecutionToken, params.type, params.input, params.checkpoint, params.state.executionDeadline, params.state.executionStateOwnerId ?? workflowId, params.registration, callbacks, liveness ? () => liveness.resolve() : void 0);
|
|
75
77
|
if (liveness)
|
|
76
78
|
await liveness.promise;
|
|
77
79
|
}
|
|
@@ -145,6 +145,7 @@ function buildInitialIdentitySlice(workflowId, type, input, versionTuple, execut
|
|
|
145
145
|
status: delayedStartTimer ? "pending" : "running",
|
|
146
146
|
input,
|
|
147
147
|
versionTuple,
|
|
148
|
+
workflowExecutionToken: crypto.randomUUID(),
|
|
148
149
|
...executionStateOwnerId !== void 0 && { executionStateOwnerId },
|
|
149
150
|
createdAt: now,
|
|
150
151
|
...!delayedStartTimer && { startedAt: now },
|
|
@@ -3,6 +3,6 @@ import { type WorkflowHandle } from '../handles.ts';
|
|
|
3
3
|
import type { EngineInternals } from '../internals.ts';
|
|
4
4
|
import { type LifecycleCallbacks, type RecoverAllOptions, type RegistrationEntry } from './shared.ts';
|
|
5
5
|
export declare function recoverAll(internals: EngineInternals, callbacks: LifecycleCallbacks, options?: RecoverAllOptions): Promise<WorkflowHandle[]>;
|
|
6
|
-
export declare function resume(internals: EngineInternals, workflowId: string, callbacks: LifecycleCallbacks): Promise<WorkflowHandle>;
|
|
6
|
+
export declare function resume(internals: EngineInternals, workflowId: string, callbacks: LifecycleCallbacks, onRecoveredWorkflow?: RecoverAllOptions['onRecoveredWorkflow']): Promise<WorkflowHandle>;
|
|
7
7
|
export declare function fork(internals: EngineInternals, sourceWorkflowId: string, options: ForkOptions | undefined, callbacks: LifecycleCallbacks): Promise<WorkflowHandle>;
|
|
8
8
|
export declare function launchWorkflowFromCheckpoint(internals: EngineInternals, workflowId: string, state: WorkflowState, checkpoint: Checkpoint, registration: RegistrationEntry, callbacks: LifecycleCallbacks): WorkflowHandle;
|
|
@@ -77,7 +77,7 @@ export async function recoverAll(internals, callbacks, options) {
|
|
|
77
77
|
continue;
|
|
78
78
|
}
|
|
79
79
|
try {
|
|
80
|
-
handles.push(await resume(internals, entry.workflowId, callbacks));
|
|
80
|
+
handles.push(await resume(internals, entry.workflowId, callbacks, options?.onRecoveredWorkflow));
|
|
81
81
|
} catch (error) {
|
|
82
82
|
if (error instanceof RegExpExtensionDecodeError) {
|
|
83
83
|
await callbacks.failWorkflowForCheckpointDecodeError(entry.workflowId, error);
|
|
@@ -88,7 +88,7 @@ export async function recoverAll(internals, callbacks, options) {
|
|
|
88
88
|
}
|
|
89
89
|
return handles;
|
|
90
90
|
}
|
|
91
|
-
export async function resume(internals, workflowId, callbacks) {
|
|
91
|
+
export async function resume(internals, workflowId, callbacks, onRecoveredWorkflow) {
|
|
92
92
|
const workflowState = await loadWorkflowState(internals, workflowId);
|
|
93
93
|
if (workflowState !== null) {
|
|
94
94
|
if (callbacks.isInlineWorkflowLocallyOwned(workflowId, workflowState.status) || callbacks.hasLocalCheckpointOwnership(workflowId, workflowState.status)) {
|
|
@@ -96,7 +96,7 @@ export async function resume(internals, workflowId, callbacks) {
|
|
|
96
96
|
return callbacks.getHandle(workflowId);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
return resumeWorkflowFromStorage(internals, workflowId, !0, callbacks);
|
|
99
|
+
return resumeWorkflowFromStorage(internals, workflowId, !0, callbacks, onRecoveredWorkflow);
|
|
100
100
|
}
|
|
101
101
|
export async function fork(internals, sourceWorkflowId, options, callbacks) {
|
|
102
102
|
const sourceState = await loadWorkflowState(internals, sourceWorkflowId);
|
|
@@ -144,6 +144,9 @@ function launchInlineWorkflowFromCheckpoint(internals, workflowId, state, checkp
|
|
|
144
144
|
resetCancelHandlers(internals, workflowId);
|
|
145
145
|
const context = new Context({
|
|
146
146
|
workflowId,
|
|
147
|
+
...state.workflowExecutionToken !== void 0 && {
|
|
148
|
+
workflowExecutionToken: state.workflowExecutionToken
|
|
149
|
+
},
|
|
147
150
|
workflowType: state.type,
|
|
148
151
|
startedAt: getWorkflowExecutionStartedAt(state),
|
|
149
152
|
abortController: workflowAbort,
|
|
@@ -172,6 +175,9 @@ function launchWorkerWorkflowFromCheckpoint(internals, workflowId, state, checkp
|
|
|
172
175
|
const serialized = serializeCheckpoint(checkpoint);
|
|
173
176
|
internals.strategy.startWorkflow({
|
|
174
177
|
workflowId,
|
|
178
|
+
...state.workflowExecutionToken !== void 0 && {
|
|
179
|
+
workflowExecutionToken: state.workflowExecutionToken
|
|
180
|
+
},
|
|
175
181
|
workflowType: state.type,
|
|
176
182
|
input: state.input,
|
|
177
183
|
checkpoint: serialized,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { EMPTY_STORAGE_VALUE, createWorkflowHandle, loadTerminalCleanupTrackedState, loadWorkflowStartHeaders, normalizeStartWorkflowTags, processPendingUpdatesAfterReplay, setWorkflowStartHeaders, type LifecycleCallbacks, type RecoverAllOptions, } from './lifecycle/shared.ts';
|
|
1
|
+
export { EMPTY_STORAGE_VALUE, createWorkflowHandle, loadTerminalCleanupTrackedState, loadWorkflowStartHeaders, normalizeStartWorkflowTags, processPendingUpdatesAfterReplay, setWorkflowStartHeaders, type LifecycleCallbacks, type RecoverAllOptions, type RecoveredWorkflowInfo, } from './lifecycle/shared.ts';
|
|
2
2
|
export { createWorkflowVersionTuple, derivePreparedExecutionState, prepareResumeState, throwVersionMismatch, workflowStateWithVersionTuple, workflowVersionTupleFromState, } from './lifecycle/persist.ts';
|
|
3
3
|
export { createInitialCheckpoint, createInitialWorkflowState, parseStartOptionDuration, resolveScheduledStartAt, start, startWorkflow, } from './lifecycle/start.ts';
|
|
4
4
|
export { startOrSignal, startWithIdempotency, type StartOrSignalCallbacks, type StartOrSignalResult, } from './lifecycle/start-or-signal.ts';
|
|
@@ -149,7 +149,7 @@ class MemoDurableActivityScope {
|
|
|
149
149
|
finishTimerWrite();
|
|
150
150
|
}
|
|
151
151
|
this.#throwIfClosed();
|
|
152
|
-
registerSleepResolver(this.#internals, this.#workflowId, operation.operationId, resolve);
|
|
152
|
+
registerSleepResolver(this.#internals, this.#workflowId, operation.operationId, resolve, operation.scheduledFireAt);
|
|
153
153
|
await promise;
|
|
154
154
|
}
|
|
155
155
|
#activityOperationId(ordinal, attempt) {
|
|
@@ -198,15 +198,13 @@ class MemoDurableActivityScope {
|
|
|
198
198
|
onAbort();
|
|
199
199
|
return;
|
|
200
200
|
}
|
|
201
|
-
operation.then((value) => {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
});
|
|
209
|
-
}).catch((error) => {
|
|
201
|
+
operation.then((value) => settle(() => {
|
|
202
|
+
if (this.#closed || signal.aborted) {
|
|
203
|
+
reject(this.#abortError());
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
resolve(value);
|
|
207
|
+
})).catch((error) => {
|
|
210
208
|
settle(() => reject(error));
|
|
211
209
|
});
|
|
212
210
|
});
|
|
@@ -25,7 +25,7 @@ export type ActivityOperationCallbacks = {
|
|
|
25
25
|
};
|
|
26
26
|
export declare function buildActivityVerification(_internals: EngineInternals, activityName: string, verify: ActivityFunctionWithMetadata['verify'], context: ReturnType<typeof buildActivityVerificationContext>, result: unknown): Promise<void>;
|
|
27
27
|
export declare function buildActivityCompensation(internals: EngineInternals, workflowId: string, operation: ActivityOperation, result: unknown): (() => Promise<void>) | undefined;
|
|
28
|
-
export declare function invokeWorkerActivity(internals: EngineInternals, operationId: string, activityName: string, input: unknown, attempt: number): Promise<unknown>;
|
|
28
|
+
export declare function invokeWorkerActivity(internals: EngineInternals, operationId: string, activityName: string, input: unknown, attempt: number, workflowExecutionToken: string | undefined, activityAttemptToken: string | undefined): Promise<unknown>;
|
|
29
29
|
export declare function invokeInlineActivity(internals: EngineInternals, workflowId: string, operation: ActivityOperation, activityContext: ActivityContext, _activityName: string, input: unknown): unknown;
|
|
30
30
|
/**
|
|
31
31
|
* Execute an activity function, dispatching to a Web Worker pool when
|