@lostgradient/weft 0.24.0 → 0.24.1
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 +2 -2
- package/dist/cli/conformance.js +5 -2
- package/dist/cli/generated/operation-catalog.snapshot.json +95 -1
- package/dist/cli/generated/operation-client.generated.d.ts +28 -2
- package/dist/cli/generated/operation-client.generated.js +2 -0
- package/dist/cli/parse-schedule-arguments.js +15 -3
- package/dist/cli/schedule.js +2 -1
- package/dist/cli/types.d.ts +2 -1
- package/dist/client/http-client-requests.js +6 -9
- package/dist/client/start-body.js +1 -0
- package/dist/core/catalog/index.d.ts +2 -2
- package/dist/core/catalog/index.js +9 -2
- package/dist/core/catalog/reference-counts.d.ts +29 -14
- package/dist/core/catalog/removal.d.ts +63 -10
- package/dist/core/catalog/removal.js +18 -2
- package/dist/core/catalog/storage-io.d.ts +23 -0
- package/dist/core/catalog/storage-io.js +1 -1
- package/dist/core/engine/activity-resolution.js +17 -17
- package/dist/core/engine/bulk-operations-purge.js +14 -20
- package/dist/core/engine/bulk-operations-retry.d.ts +9 -0
- package/dist/core/engine/bulk-operations-retry.js +177 -0
- package/dist/core/engine/bulk-operations-shared.d.ts +18 -0
- package/dist/core/engine/bulk-operations-shared.js +14 -0
- package/dist/core/engine/bulk-operations.js +8 -167
- package/dist/core/engine/callback-creators-bundles.js +4 -2
- package/dist/core/engine/callback-creators-core.js +9 -1
- package/dist/core/engine/callback-creators-schedule.js +2 -2
- package/dist/core/engine/catalog-readiness.js +2 -0
- package/dist/core/engine/catalog-removal.d.ts +107 -3
- package/dist/core/engine/catalog-removal.js +66 -13
- package/dist/core/engine/catalog-tombstone-recovery.d.ts +56 -0
- package/dist/core/engine/catalog-tombstone-recovery.js +39 -0
- package/dist/core/engine/constraints.js +5 -1
- package/dist/core/engine/construction.d.ts +1 -1
- package/dist/core/engine/decode-revision.d.ts +32 -0
- package/dist/core/engine/decode-revision.js +9 -0
- package/dist/core/engine/disposal.d.ts +6 -2
- package/dist/core/engine/disposal.js +24 -10
- package/dist/core/engine/dynamic-source-errors.d.ts +81 -0
- package/dist/core/engine/dynamic-source-errors.js +25 -0
- package/dist/core/engine/dynamic-source-execution.d.ts +173 -0
- package/dist/core/engine/dynamic-source-execution.js +108 -0
- package/dist/core/engine/engine-internal-types.d.ts +16 -0
- package/dist/core/engine/engine-workflows-namespace.d.ts +10 -0
- package/dist/core/engine/engine-workflows-namespace.js +3 -1
- package/dist/core/engine/index.d.ts +18 -10
- package/dist/core/engine/index.js +21 -10
- package/dist/core/engine/inline-launch-queue.js +1 -1
- package/dist/core/engine/internals.d.ts +26 -30
- package/dist/core/engine/lifecycle/checkpoint-launch.d.ts +29 -0
- package/dist/core/engine/lifecycle/checkpoint-launch.js +75 -0
- package/dist/core/engine/lifecycle/delayed-start-registration.d.ts +30 -0
- package/dist/core/engine/lifecycle/delayed-start-registration.js +11 -0
- package/dist/core/engine/lifecycle/fork-helpers.d.ts +33 -1
- package/dist/core/engine/lifecycle/fork-helpers.js +3 -1
- package/dist/core/engine/lifecycle/recovery-revision-groups.d.ts +75 -0
- package/dist/core/engine/lifecycle/recovery-revision-groups.js +64 -0
- package/dist/core/engine/lifecycle/resume-generation-guard.d.ts +58 -0
- package/dist/core/engine/lifecycle/resume-generation-guard.js +11 -0
- package/dist/core/engine/lifecycle/resume.js +18 -9
- package/dist/core/engine/lifecycle/shared.d.ts +43 -0
- package/dist/core/engine/lifecycle/standalone-claim-acquire.d.ts +12 -0
- package/dist/core/engine/lifecycle/standalone-claim-acquire.js +6 -0
- package/dist/core/engine/lifecycle/start-commit-errors.d.ts +12 -0
- package/dist/core/engine/lifecycle/start-commit-errors.js +6 -0
- package/dist/core/engine/lifecycle/start-commit.d.ts +22 -7
- package/dist/core/engine/lifecycle/start-commit.js +49 -15
- package/dist/core/engine/lifecycle/start-exec.d.ts +18 -2
- package/dist/core/engine/lifecycle/start-exec.js +7 -5
- package/dist/core/engine/lifecycle/start-or-signal-create.js +1 -3
- package/dist/core/engine/lifecycle/start-or-signal.js +1 -1
- package/dist/core/engine/lifecycle/start-precondition-attribution.d.ts +49 -0
- package/dist/core/engine/lifecycle/start-precondition-attribution.js +13 -0
- package/dist/core/engine/lifecycle/start-revision-resolution.d.ts +34 -0
- package/dist/core/engine/lifecycle/start-revision-resolution.js +11 -0
- package/dist/core/engine/lifecycle/start-state.d.ts +16 -0
- package/dist/core/engine/lifecycle/start-state.js +60 -0
- package/dist/core/engine/lifecycle/start-terminal-conflict-purge.d.ts +66 -3
- package/dist/core/engine/lifecycle/start-terminal-conflict-purge.js +13 -3
- package/dist/core/engine/lifecycle/start.d.ts +16 -6
- package/dist/core/engine/lifecycle/start.js +38 -68
- package/dist/core/engine/lifecycle/transition.d.ts +3 -4
- package/dist/core/engine/lifecycle/transition.js +30 -80
- package/dist/core/engine/lifecycle.d.ts +4 -2
- package/dist/core/engine/lifecycle.js +6 -6
- package/dist/core/engine/listing.js +15 -1
- package/dist/core/engine/memo-durable-activity.js +4 -1
- package/dist/core/engine/nonterminal-revision-count.d.ts +25 -0
- package/dist/core/engine/nonterminal-revision-count.js +16 -0
- package/dist/core/engine/operations-time.d.ts +5 -23
- package/dist/core/engine/operations-time.js +13 -11
- package/dist/core/engine/ownership-bootstrap.d.ts +1 -1
- package/dist/core/engine/pinned-schedule-revision-count.d.ts +26 -0
- package/dist/core/engine/pinned-schedule-revision-count.js +19 -0
- package/dist/core/engine/pinned-schedule-revision.d.ts +76 -0
- package/dist/core/engine/pinned-schedule-revision.js +53 -0
- package/dist/core/engine/registration.d.ts +30 -0
- package/dist/core/engine/registration.js +17 -2
- package/dist/core/engine/retention.js +9 -2
- package/dist/core/engine/revision-errors.d.ts +71 -0
- package/dist/core/engine/revision-errors.js +13 -0
- package/dist/core/engine/schedule-revision-fire.d.ts +17 -0
- package/dist/core/engine/schedule-revision-fire.js +3 -0
- package/dist/core/engine/schedule-run.js +2 -1
- package/dist/core/engine/schedules.d.ts +2 -1
- package/dist/core/engine/schedules.js +28 -9
- package/dist/core/engine/source-diagnostics.d.ts +76 -0
- package/dist/core/engine/source-diagnostics.js +105 -0
- package/dist/core/engine/source-registration.js +4 -2
- package/dist/core/engine/source-resolution.d.ts +25 -9
- package/dist/core/engine/source-resolution.js +49 -14
- package/dist/core/engine/source-runtime-state.d.ts +92 -0
- package/dist/core/engine/source-runtime-state.js +36 -0
- package/dist/core/engine/storage-io.d.ts +19 -2
- package/dist/core/engine/storage-io.js +21 -2
- package/dist/core/engine/termination/finalizer-registration.d.ts +18 -0
- package/dist/core/engine/termination/finalizer-registration.js +21 -0
- package/dist/core/engine/termination/finalizer.d.ts +16 -19
- package/dist/core/engine/termination/finalizer.js +3 -3
- package/dist/core/engine/time-operation-callbacks.d.ts +32 -0
- package/dist/core/engine/time-operation-callbacks.js +0 -0
- package/dist/core/engine/validation/schedule-cadence.d.ts +16 -0
- package/dist/core/engine/validation/schedule-cadence.js +20 -0
- package/dist/core/engine/validation/schedule-options.d.ts +4 -1
- package/dist/core/engine/validation/schedule-options.js +14 -0
- package/dist/core/engine/validation/schedule-revision.d.ts +24 -0
- package/dist/core/engine/validation/schedule-revision.js +21 -0
- package/dist/core/engine/validation/schedule-warnings.d.ts +11 -0
- package/dist/core/engine/validation/schedule-warnings.js +5 -0
- package/dist/core/engine/validation/schedule.d.ts +4 -3
- package/dist/core/engine/validation/schedule.js +15 -26
- package/dist/core/engine/validation.js +3 -0
- package/dist/core/engine/workflow-claim-reclaim-target.d.ts +1 -1
- package/dist/core/engine/workflow-claim-reclaim-target.js +16 -2
- package/dist/core/engine/workflow-retention-deadline.d.ts +16 -0
- package/dist/core/engine/workflow-retention-deadline.js +33 -0
- package/dist/core/events/event-map.d.ts +5 -0
- package/dist/core/events/index.d.ts +1 -0
- package/dist/core/events/index.js +1 -0
- package/dist/core/events/workflow-source-events.d.ts +102 -0
- package/dist/core/events/workflow-source-events.js +57 -0
- package/dist/core/execution-strategy.d.ts +7 -0
- package/dist/core/inline-execution-strategy.context-options.d.ts +1 -1
- package/dist/core/inline-execution-strategy.js +1 -1
- package/dist/core/types/checkpoint.d.ts +14 -0
- package/dist/core/types/schedules.d.ts +52 -1
- package/dist/core/types/state.d.ts +16 -0
- package/dist/core/weft-error.d.ts +1 -1
- package/dist/core/weft-error.js +4 -1
- package/dist/core/worker-execution-strategy.d.ts +1 -0
- package/dist/core/worker-execution-strategy.js +13 -1
- package/dist/core/worker-fault-handling.d.ts +10 -0
- package/dist/core/worker-fault-handling.js +1 -0
- package/dist/core/worker-inbound-message.d.ts +1 -0
- package/dist/core/worker-inbound-message.js +1 -0
- package/dist/core/worker-protocol-guard.js +4 -0
- package/dist/core/worker-protocol.d.ts +7 -0
- package/dist/core/worker-protocol.js +2 -1
- package/dist/core/worker-turn-watchdog.d.ts +8 -1
- package/dist/core/worker-turn-watchdog.js +3 -2
- package/dist/diagnostics/format.js +5 -0
- package/dist/diagnostics/types.d.ts +24 -0
- package/dist/diagnostics/version-check.js +8 -2
- package/dist/http.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +7 -0
- package/dist/indexeddb.js +1 -1
- package/dist/json-schema.js +3 -3
- package/dist/server/fault-to-json-rpc.js +2 -1
- package/dist/server/index.d.ts +11 -0
- package/dist/server/operation-fault.d.ts +2 -0
- package/dist/server/operation-fault.js +2 -1
- package/dist/server/operations/create-schedule.d.ts +2 -0
- package/dist/server/operations/create-schedule.js +10 -6
- package/dist/server/operations/get-catalog-diagnostics.d.ts +28 -0
- package/dist/server/operations/get-catalog-diagnostics.js +10 -2
- package/dist/server/operations/get-task-detail-schema.d.ts +8 -0
- package/dist/server/operations/get-task-detail-schema.js +1 -0
- package/dist/server/operations/get-task-detail.d.ts +8 -0
- package/dist/server/operations/get-task-detail.js +1 -0
- package/dist/server/operations/get-task-diagnostics.d.ts +4 -4
- package/dist/server/operations/preload-workflow-revision.d.ts +28 -0
- package/dist/server/operations/preload-workflow-revision.js +60 -0
- package/dist/server/operations/schedule-faults.d.ts +1 -0
- package/dist/server/operations/schedule-faults.js +32 -4
- package/dist/server/operations/schedule-rest-body.d.ts +1 -0
- package/dist/server/operations/schedule-rest-body.js +2 -1
- package/dist/server/operations/static-registrations.js +6 -0
- package/dist/server/operations/update-schedule.d.ts +2 -0
- package/dist/server/operations/update-schedule.js +4 -2
- package/dist/server/operations/workflow-catalog-operation-helpers.js +27 -0
- package/dist/server/runtime/task-dispatch-envelope.d.ts +14 -0
- package/dist/server/runtime/task-dispatch-envelope.js +32 -0
- package/dist/server/runtime/task-dispatch-revision.d.ts +29 -0
- package/dist/server/runtime/task-dispatch-revision.js +13 -0
- package/dist/server/runtime/task-dispatch.js +12 -37
- package/dist/server/runtime/task-ledger-recovery.js +1 -1
- package/dist/server/runtime/task-ledger-runtime.d.ts +14 -2
- package/dist/server/runtime/task-ledger-runtime.js +5 -3
- package/dist/server/runtime/task-polling.js +9 -3
- package/dist/server/runtime/task-reconciliation.js +2 -1
- package/dist/server/runtime/websocket-worker.js +9 -0
- package/dist/server/task-ledger-codec.d.ts +9 -0
- package/dist/server/task-ledger-codec.js +4 -1
- package/dist/server/task-ledger-transition-helpers.js +1 -0
- package/dist/server/task-ledger-types.d.ts +9 -0
- package/dist/server/task-ledger.d.ts +1 -1
- package/dist/server/task-ledger.js +2 -1
- package/dist/server/task-queue-types.d.ts +2 -0
- package/dist/storage/bun-sql.js +3 -1
- package/dist/storage/catalog-keys.d.ts +19 -0
- package/dist/storage/catalog-keys.js +3 -1
- package/dist/storage/compressed-storage.js +1 -1
- package/dist/storage/index.d.ts +3 -1
- package/dist/storage/interface.d.ts +2 -0
- package/dist/storage/interface.js +1 -1
- package/dist/storage/key-prefixes.d.ts +1 -1
- package/dist/storage/key-prefixes.js +1 -0
- package/dist/storage/lmdb.d.ts +30 -2
- package/dist/storage/lmdb.js +1 -1
- package/dist/storage/memory.js +1 -1
- package/dist/storage/neon.js +2 -2
- package/dist/storage/node-sqlite.js +3 -1
- package/dist/storage/postgres.js +2 -2
- 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/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/web-extension.js +1 -1
- package/dist/worker/protocol-messages.d.ts +17 -0
- package/dist/worker/protocol-schemas.d.ts +32 -0
- package/dist/worker/protocol-schemas.js +7 -3
- package/dist/worker/protocol-task-result.js +35 -3
- package/dist/worker/protocol.js +1 -1
- package/dist/worker/registry/types.d.ts +2 -0
- package/dist/worker/registry.d.ts +1 -1
- package/dist/worker/registry.js +3 -1
- package/dist/workers/workflow-runner.d.ts +9 -0
- package/dist/workers/workflow-runner.js +9 -2
- package/dist/workers/workflow-worker-entry.js +9 -4
- package/package.json +2 -2
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the initial in-memory {@link WorkflowState} and {@link Checkpoint}
|
|
3
|
+
* for a fresh `start()` — split out of `start.ts`, which has no headroom
|
|
4
|
+
* under the repository's 500-line implementation-file ceiling for this
|
|
5
|
+
* logic inline.
|
|
6
|
+
*
|
|
7
|
+
* @module core/engine/lifecycle/start-state
|
|
8
|
+
*/
|
|
9
|
+
import type { Checkpoint, Duration, StartOptions, TimerEntry, WorkflowState } from '../../types.ts';
|
|
10
|
+
import { type WorkflowVersionTuple } from '../../workflow-version-tuple.ts';
|
|
11
|
+
import type { EngineInternals } from '../internals.ts';
|
|
12
|
+
import { type LifecycleCallbacks } from './shared.ts';
|
|
13
|
+
export declare function parseStartOptionDuration(_internals: EngineInternals, duration: Duration, fieldName: 'options.executionTimeout' | 'options.startAfter', _callbacks: LifecycleCallbacks): number;
|
|
14
|
+
export declare function createInitialWorkflowState(internals: EngineInternals, workflowId: string, type: string, input: unknown, versionTuple: WorkflowVersionTuple, revision: string, options: StartOptions | undefined, tags: string[] | undefined, executionStateOwnerId: string | undefined, parentWorkflowId: string | undefined, parentWorkflowExecutionToken: string | undefined, delayedStartTimer: TimerEntry | undefined, callbacks: LifecycleCallbacks): WorkflowState;
|
|
15
|
+
export declare function createInitialCheckpoint(internals: EngineInternals, workflowId: string, workflowVersion: string, options: StartOptions | undefined, _callbacks: LifecycleCallbacks): Checkpoint;
|
|
16
|
+
export declare function applyRestartLineage(state: WorkflowState, displacedState: WorkflowState | null): void;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { createCheckpoint } from "../../checkpoint.js";
|
|
2
|
+
import { normalizeStorageTimestamp } from "../../scheduler.js";
|
|
3
|
+
import {
|
|
4
|
+
StartWorkflowValidationError,
|
|
5
|
+
parseStartWorkflowDuration
|
|
6
|
+
} from "../../start-workflow-validation.js";
|
|
7
|
+
export function parseStartOptionDuration(_internals, duration, fieldName, _callbacks) {
|
|
8
|
+
return parseStartWorkflowDuration(duration, fieldName);
|
|
9
|
+
}
|
|
10
|
+
function buildInitialIdentitySlice(workflowId, type, input, versionTuple, revision, executionStateOwnerId, parentWorkflowId, parentWorkflowExecutionToken, delayedStartTimer, now, tags) {
|
|
11
|
+
return {
|
|
12
|
+
id: workflowId,
|
|
13
|
+
type,
|
|
14
|
+
status: delayedStartTimer ? "pending" : "running",
|
|
15
|
+
input,
|
|
16
|
+
versionTuple,
|
|
17
|
+
revision,
|
|
18
|
+
workflowExecutionToken: crypto.randomUUID(),
|
|
19
|
+
...executionStateOwnerId !== void 0 && { executionStateOwnerId },
|
|
20
|
+
...parentWorkflowId !== void 0 && { parentWorkflowId },
|
|
21
|
+
...parentWorkflowExecutionToken !== void 0 && { parentWorkflowExecutionToken },
|
|
22
|
+
createdAt: now,
|
|
23
|
+
...!delayedStartTimer && { startedAt: now },
|
|
24
|
+
updatedAt: now,
|
|
25
|
+
...tags !== void 0 && { tags }
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function resolveInitialExecutionDeadline(internals, options, delayedStartTimer, now, callbacks) {
|
|
29
|
+
if (options?.executionTimeout === void 0 || delayedStartTimer)
|
|
30
|
+
return;
|
|
31
|
+
const executionTimeoutMilliseconds = parseStartOptionDuration(internals, options.executionTimeout, "options.executionTimeout", callbacks);
|
|
32
|
+
try {
|
|
33
|
+
return normalizeStorageTimestamp(now + executionTimeoutMilliseconds, "options.executionTimeout");
|
|
34
|
+
} catch {
|
|
35
|
+
throw new StartWorkflowValidationError("options.executionTimeout must resolve to a finite, non-negative deadline");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function createInitialWorkflowState(internals, workflowId, type, input, versionTuple, revision, options, tags, executionStateOwnerId, parentWorkflowId, parentWorkflowExecutionToken, delayedStartTimer, callbacks) {
|
|
39
|
+
const now = internals.options.getNow(), state = buildInitialIdentitySlice(workflowId, type, input, versionTuple, revision, executionStateOwnerId, parentWorkflowId, parentWorkflowExecutionToken, delayedStartTimer, now, tags), executionDeadline = resolveInitialExecutionDeadline(internals, options, delayedStartTimer, now, callbacks);
|
|
40
|
+
if (executionDeadline !== void 0)
|
|
41
|
+
state.executionDeadline = executionDeadline;
|
|
42
|
+
return state;
|
|
43
|
+
}
|
|
44
|
+
export function createInitialCheckpoint(internals, workflowId, workflowVersion, options, _callbacks) {
|
|
45
|
+
const checkpoint = createCheckpoint(workflowId, workflowVersion, internals.options.getNow());
|
|
46
|
+
if (options?.searchAttributes)
|
|
47
|
+
checkpoint.searchAttributes = { ...options.searchAttributes };
|
|
48
|
+
return checkpoint;
|
|
49
|
+
}
|
|
50
|
+
export function applyRestartLineage(state, displacedState) {
|
|
51
|
+
if (displacedState === null)
|
|
52
|
+
return;
|
|
53
|
+
state.restartedFrom = {
|
|
54
|
+
workflowId: displacedState.id,
|
|
55
|
+
...displacedState.workflowExecutionToken !== void 0 && {
|
|
56
|
+
workflowExecutionToken: displacedState.workflowExecutionToken
|
|
57
|
+
},
|
|
58
|
+
replacedAt: state.createdAt
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -1,7 +1,65 @@
|
|
|
1
|
-
import type { BatchOperation } from '../../../storage/interface.ts';
|
|
1
|
+
import type { BatchOperation, ConditionalBatchCondition } from '../../../storage/interface.ts';
|
|
2
2
|
import type { StartWorkflowOptions, WorkflowState } from '../../types.ts';
|
|
3
3
|
import type { EngineInternals } from '../internals.ts';
|
|
4
4
|
import { type LifecycleCallbacks } from './shared.ts';
|
|
5
|
+
/**
|
|
6
|
+
* What the duplicate-id read decided about a caller-supplied workflow id.
|
|
7
|
+
*/
|
|
8
|
+
export type StartDuplicateIdDecision = {
|
|
9
|
+
/** A prior terminal run this start will displace, or `null` for a fresh id. */
|
|
10
|
+
terminalRunToPurge: WorkflowState | null;
|
|
11
|
+
/**
|
|
12
|
+
* Compare-and-swap precondition that makes the duplicate-id check atomic with
|
|
13
|
+
* the create commit (WFT-152).
|
|
14
|
+
*
|
|
15
|
+
* The check below reads `KEYS.workflow(workflowId)` and decides whether the id
|
|
16
|
+
* is free, but that read and the create batch are separated by every build step
|
|
17
|
+
* in between. Within ONE engine `pendingStarts` holds the id across that window;
|
|
18
|
+
* two engines sharing a store share no such memory, so both previously committed
|
|
19
|
+
* blind and the second silently overwrote the first — leaving the loser with a
|
|
20
|
+
* run whose terminal transition happens on the other engine and therefore never
|
|
21
|
+
* settles its `result()` waiter. Conditioning the batch on the exact value seen
|
|
22
|
+
* here collapses that window: the loser's batch does not commit, and
|
|
23
|
+
* `buildAndCommitStartBatch` surfaces {@link WorkflowAlreadyExistsError} — the
|
|
24
|
+
* same error the in-engine `pendingStarts` guard already throws for the same
|
|
25
|
+
* collision.
|
|
26
|
+
*
|
|
27
|
+
* `expectedValue` is the RAW observed bytes, deliberately not a re-encoding of
|
|
28
|
+
* the decoded state: re-encoding is not guaranteed to round-trip byte-identically,
|
|
29
|
+
* and a condition built from one would fail against a record nothing had touched.
|
|
30
|
+
* `null` (id absent) and a prior terminal run's bytes (an
|
|
31
|
+
* `onTerminalConflict: 'start-new'` restart) are both valid expected values, so a
|
|
32
|
+
* restart is equally protected against a concurrent engine displacing the same
|
|
33
|
+
* terminal run.
|
|
34
|
+
*
|
|
35
|
+
* No `conditionalBatch` capability gate is needed. Reaching this code means a
|
|
36
|
+
* workflow is registered, and registration drains through
|
|
37
|
+
* `WorkflowCatalog#activateRegistered`, which already hard-requires that
|
|
38
|
+
* capability at `Engine.create()`. A store that cannot honour this condition
|
|
39
|
+
* cannot host an engine that could start a workflow in the first place.
|
|
40
|
+
*
|
|
41
|
+
* KNOWN LIMITATION (PR #959 review). The condition compares a VALUE, so it cannot
|
|
42
|
+
* distinguish "this id was never used" from "a run existed here and was purged".
|
|
43
|
+
* If a racing winner commits, completes, and is purged or swept by retention
|
|
44
|
+
* before this batch commits, `wf:<id>` is absent again, `expectedValue: null`
|
|
45
|
+
* matches, and both starts execute. Closing that needs durable per-id generation
|
|
46
|
+
* or tombstone state a purge cannot restore — a new persisted mechanism, out of
|
|
47
|
+
* scope here. The window is narrow (a winner's whole lifecycle plus a purge inside
|
|
48
|
+
* one loser's read-to-commit gap) and the outcome is a duplicate run rather than
|
|
49
|
+
* the indefinite hang this fence exists to remove.
|
|
50
|
+
*/
|
|
51
|
+
duplicateIdCondition: ConditionalBatchCondition;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* The decision for a start whose id was GENERATED rather than caller-supplied. A
|
|
55
|
+
* v4 UUID is effectively unique, so the duplicate-id read is skipped entirely and
|
|
56
|
+
* there is no observed value to condition on — the start keeps the unconditioned
|
|
57
|
+
* single-write hot path.
|
|
58
|
+
*/
|
|
59
|
+
export declare const GENERATED_ID_START_DECISION: {
|
|
60
|
+
readonly terminalRunToPurge: null;
|
|
61
|
+
readonly duplicateIdCondition: undefined;
|
|
62
|
+
};
|
|
5
63
|
/**
|
|
6
64
|
* Decide what a caller-supplied workflow id that already has a persisted record
|
|
7
65
|
* means, WITHOUT performing any destructive action. Only invoked when the caller
|
|
@@ -23,9 +81,14 @@ import { type LifecycleCallbacks } from './shared.ts';
|
|
|
23
81
|
* delete the finalizer payload before the resource is torn down, leaking it, so
|
|
24
82
|
* the restart is refused until teardown settles (which clears the marker).
|
|
25
83
|
*
|
|
26
|
-
*
|
|
84
|
+
* This read is only a point-in-time observation: another engine sharing the store
|
|
85
|
+
* can commit a create for the same id in the window between it and the create
|
|
86
|
+
* batch. The returned {@link StartDuplicateIdDecision} therefore carries a
|
|
87
|
+
* `duplicateIdCondition` holding the exact bytes seen here, so that batch can be
|
|
88
|
+
* conditioned on them — turning that window into a lost compare-and-swap rather
|
|
89
|
+
* than a blind overwrite (WFT-152).
|
|
27
90
|
*/
|
|
28
|
-
export declare function resolveTerminalConflictForRestart(internals: EngineInternals, workflowId: string, options: StartWorkflowOptions | undefined): Promise<
|
|
91
|
+
export declare function resolveTerminalConflictForRestart(internals: EngineInternals, workflowId: string, options: StartWorkflowOptions | undefined): Promise<StartDuplicateIdDecision>;
|
|
29
92
|
/**
|
|
30
93
|
* Prepare a prior terminal run for displacement by a `'start-new'` restart WITHOUT
|
|
31
94
|
* committing the destructive delete. Returns the storage delete operations (for the
|
|
@@ -6,10 +6,17 @@ import {
|
|
|
6
6
|
import { WorkflowAlreadyExistsError, WorkflowTeardownPendingError } from "../errors.js";
|
|
7
7
|
import { cleanupWaiters } from "../termination/cleanup.js";
|
|
8
8
|
import { decodeWorkflowState, isTerminalWorkflowStatus } from "../validation.js";
|
|
9
|
+
export const GENERATED_ID_START_DECISION = {
|
|
10
|
+
terminalRunToPurge: null,
|
|
11
|
+
duplicateIdCondition: void 0
|
|
12
|
+
};
|
|
9
13
|
export async function resolveTerminalConflictForRestart(internals, workflowId, options) {
|
|
10
|
-
const existingBytes = await internals.storage.get(
|
|
14
|
+
const key = KEYS.workflow(workflowId), existingBytes = await internals.storage.get(key);
|
|
11
15
|
if (existingBytes === null)
|
|
12
|
-
return
|
|
16
|
+
return {
|
|
17
|
+
terminalRunToPurge: null,
|
|
18
|
+
duplicateIdCondition: { key, expectedValue: null }
|
|
19
|
+
};
|
|
13
20
|
if (options?.onTerminalConflict !== "start-new")
|
|
14
21
|
throw new WorkflowAlreadyExistsError(workflowId);
|
|
15
22
|
const existingState = decodeWorkflowState(existingBytes);
|
|
@@ -17,7 +24,10 @@ export async function resolveTerminalConflictForRestart(internals, workflowId, o
|
|
|
17
24
|
throw new WorkflowAlreadyExistsError(workflowId);
|
|
18
25
|
if (await storageHas(internals.storage, KEYS.teardownOwed(workflowId)))
|
|
19
26
|
throw new WorkflowTeardownPendingError(workflowId);
|
|
20
|
-
return
|
|
27
|
+
return {
|
|
28
|
+
terminalRunToPurge: existingState,
|
|
29
|
+
duplicateIdCondition: { key, expectedValue: existingBytes }
|
|
30
|
+
};
|
|
21
31
|
}
|
|
22
32
|
export async function prepareTerminalRunPurge(internals, state, callbacks) {
|
|
23
33
|
const cleanupWaitersForStart = (id) => cleanupWaiters(internals, id, {
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
import type { BatchOperation } from '../../../storage/interface.ts';
|
|
2
|
-
import type {
|
|
3
|
-
import { type WorkflowVersionTuple } from '../../workflow-version-tuple.ts';
|
|
2
|
+
import type { StartOptions, StartWorkflowOptions } from '../../types.ts';
|
|
4
3
|
import { type WorkflowHandle } from '../handles.ts';
|
|
5
4
|
import type { EngineInternals } from '../internals.ts';
|
|
6
5
|
import { type LifecycleCallbacks } from './shared.ts';
|
|
7
6
|
import { type BuildIdempotentStartOperations } from './start-commit.ts';
|
|
8
7
|
export declare function start(internals: EngineInternals, type: string, input: unknown, options: StartWorkflowOptions | undefined, callbacks: LifecycleCallbacks): Promise<WorkflowHandle>;
|
|
9
|
-
export declare function startWorkflow(internals: EngineInternals, type: string, input: unknown, options: StartWorkflowOptions | undefined, additionalStartOperations: BatchOperation[] | undefined, callbacks: LifecycleCallbacks, buildIdempotentStartOperations?: BuildIdempotentStartOperations
|
|
8
|
+
export declare function startWorkflow(internals: EngineInternals, type: string, input: unknown, options: StartWorkflowOptions | undefined, additionalStartOperations: BatchOperation[] | undefined, callbacks: LifecycleCallbacks, buildIdempotentStartOperations?: BuildIdempotentStartOperations,
|
|
9
|
+
/**
|
|
10
|
+
* A pinned schedule's forward-looking revision commitment (WFT-20),
|
|
11
|
+
* threaded from `ScheduleCallbacks.startWorkflow`'s own `revisionOverride`
|
|
12
|
+
* parameter. When supplied, this call resolves and reserves EXACTLY this
|
|
13
|
+
* revision (via {@link resolveAndReservePinnedExecutableRegistration},
|
|
14
|
+
* which enforces an exact-match check for an eager type rather than
|
|
15
|
+
* silently falling back to whatever is active) instead of the ordinary
|
|
16
|
+
* "whatever `resolveExecutableRegistration` currently resolves"
|
|
17
|
+
* admission path, and the persisted `WorkflowState.revision` is this
|
|
18
|
+
* value verbatim — never re-derived from `registeredCatalogRevisions`.
|
|
19
|
+
* `undefined` (the default, every non-schedule start) is byte-for-byte
|
|
20
|
+
* the pre-WFT-20 admission path.
|
|
21
|
+
*/
|
|
22
|
+
revisionOverride?: string): Promise<WorkflowHandle>;
|
|
10
23
|
export declare function resolveScheduledStartAt(internals: EngineInternals, options: StartOptions | undefined, submissionTime: number, callbacks: LifecycleCallbacks): number | undefined;
|
|
11
|
-
export declare function parseStartOptionDuration(_internals: EngineInternals, duration: Duration, fieldName: 'options.executionTimeout' | 'options.startAfter', _callbacks: LifecycleCallbacks): number;
|
|
12
|
-
export declare function createInitialWorkflowState(internals: EngineInternals, workflowId: string, type: string, input: unknown, versionTuple: WorkflowVersionTuple, options: StartOptions | undefined, tags: string[] | undefined, executionStateOwnerId: string | undefined, parentWorkflowId: string | undefined, parentWorkflowExecutionToken: string | undefined, delayedStartTimer: TimerEntry | undefined, callbacks: LifecycleCallbacks): WorkflowState;
|
|
13
|
-
export declare function createInitialCheckpoint(internals: EngineInternals, workflowId: string, workflowVersion: string, options: StartOptions | undefined, _callbacks: LifecycleCallbacks): Checkpoint;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createCheckpoint } from "../../checkpoint.js";
|
|
2
1
|
import { assertPayloadWithinLimit } from "../../payload-size.js";
|
|
3
2
|
import { normalizeStorageTimestamp } from "../../scheduler.js";
|
|
4
3
|
import {
|
|
@@ -6,13 +5,16 @@ import {
|
|
|
6
5
|
assertExclusiveStartWorkflowOptions,
|
|
7
6
|
assertValidOnTerminalConflict,
|
|
8
7
|
coerceStartWorkflowId,
|
|
9
|
-
coerceStartWorkflowTimestamp
|
|
10
|
-
parseStartWorkflowDuration
|
|
8
|
+
coerceStartWorkflowTimestamp
|
|
11
9
|
} from "../../start-workflow-validation.js";
|
|
12
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
releaseInFlightStart,
|
|
12
|
+
resolveAndReserveExecutableRegistration
|
|
13
|
+
} from "../catalog-removal.js";
|
|
13
14
|
import { forgetCommittedCheckpointBytes } from "../checkpoint-commit-snapshots.js";
|
|
14
|
-
import { WorkflowAlreadyExistsError
|
|
15
|
+
import { WorkflowAlreadyExistsError } from "../errors.js";
|
|
15
16
|
import { createDelayedStartTimerEntry } from "../operations-time.js";
|
|
17
|
+
import { resolveAndReservePinnedExecutableRegistration } from "../pinned-schedule-revision.js";
|
|
16
18
|
import { selectPersistedWorkflowStartHeaders } from "../state-utilities.js";
|
|
17
19
|
import { buildWorkflowConcurrencyStartOperations } from "../workflow-concurrency.js";
|
|
18
20
|
import { createWorkflowVersionTuple } from "./persist.js";
|
|
@@ -28,6 +30,17 @@ import {
|
|
|
28
30
|
runWorkflowStartInterceptor
|
|
29
31
|
} from "./start-exec.js";
|
|
30
32
|
import {
|
|
33
|
+
resolveCachedStartRevision,
|
|
34
|
+
resolveStartRevisionUncached
|
|
35
|
+
} from "./start-revision-resolution.js";
|
|
36
|
+
import {
|
|
37
|
+
applyRestartLineage,
|
|
38
|
+
createInitialCheckpoint,
|
|
39
|
+
createInitialWorkflowState,
|
|
40
|
+
parseStartOptionDuration
|
|
41
|
+
} from "./start-state.js";
|
|
42
|
+
import {
|
|
43
|
+
GENERATED_ID_START_DECISION,
|
|
31
44
|
prepareTerminalRunPurge,
|
|
32
45
|
resolveTerminalConflictForRestart
|
|
33
46
|
} from "./start-terminal-conflict-purge.js";
|
|
@@ -70,11 +83,16 @@ function assertServicesSupportedForMode(internals, options) {
|
|
|
70
83
|
if (options?.services !== void 0 && internals.inlineStrategy === null)
|
|
71
84
|
throw Error('options.services is only supported in inline execution mode; it cannot be serialized to a Worker. Remove services or use workflowExecutionMode: "inline".');
|
|
72
85
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
function resolveAndReserveStartRegistration(internals, type, revisionOverride, callbacks) {
|
|
87
|
+
if (revisionOverride === void 0)
|
|
88
|
+
return resolveAndReserveExecutableRegistration(internals, type, callbacks.resolveExecutableRegistration);
|
|
89
|
+
return resolveAndReservePinnedExecutableRegistration(internals.engine, internals, type, revisionOverride).then(({ registration, inFlightRevision }) => ({
|
|
90
|
+
registration,
|
|
91
|
+
inFlightRevision,
|
|
92
|
+
resolvedRevision: revisionOverride
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
export async function startWorkflow(internals, type, input, options, additionalStartOperations, callbacks, buildIdempotentStartOperations, revisionOverride) {
|
|
78
96
|
assertServicesSupportedForMode(internals, options);
|
|
79
97
|
assertValidOnTerminalConflict(options);
|
|
80
98
|
const preparation = prepareStartWorkflow(internals, options, callbacks), {
|
|
@@ -90,12 +108,16 @@ export async function startWorkflow(internals, type, input, options, additionalS
|
|
|
90
108
|
if (internals.pendingStarts.has(workflowId))
|
|
91
109
|
throw new WorkflowAlreadyExistsError(workflowId);
|
|
92
110
|
internals.pendingStarts.add(workflowId);
|
|
93
|
-
let startSucceeded = !1;
|
|
94
|
-
const inFlightRevision = reserveInFlightStart(internals, type);
|
|
111
|
+
let startSucceeded = !1, inFlightRevision;
|
|
95
112
|
try {
|
|
96
|
-
const terminalRunToPurge = callerProvidedId ? await resolveTerminalConflictForRestart(internals, workflowId, options) : null;
|
|
97
113
|
assertPayloadWithinLimit(input, internals.options.payloadSizePolicy.maxBytes, "workflow input");
|
|
98
|
-
const
|
|
114
|
+
const {
|
|
115
|
+
registration,
|
|
116
|
+
inFlightRevision: reservedRevision,
|
|
117
|
+
resolvedRevision
|
|
118
|
+
} = await resolveAndReserveStartRegistration(internals, type, revisionOverride, callbacks);
|
|
119
|
+
inFlightRevision = reservedRevision;
|
|
120
|
+
const workflowConcurrency = registration.concurrency, revision = resolveCachedStartRevision(internals, type, resolvedRevision) ?? await resolveStartRevisionUncached(internals, type), { terminalRunToPurge, duplicateIdCondition } = callerProvidedId ? await resolveTerminalConflictForRestart(internals, workflowId, options) : GENERATED_ID_START_DECISION, versionTuple = createWorkflowVersionTuple(internals, registration, callbacks), state = createInitialWorkflowState(internals, workflowId, type, input, versionTuple, revision, options, preparation.normalizedTags, executionStateOwnerId, parentWorkflowId, parentWorkflowExecutionToken, delayedStartTimer, callbacks);
|
|
99
121
|
applyRestartLineage(state, terminalRunToPurge);
|
|
100
122
|
const checkpoint = createInitialCheckpoint(internals, workflowId, versionTuple.workflowVersion, options, callbacks), workflowStartHeaders = runWorkflowStartInterceptor(internals, workflowId, type, input, parentHeaders, callbacks), persistedWorkflowStartHeaders = selectPersistedWorkflowStartHeaders(workflowStartHeaders), purgeDeleteOperations = terminalRunToPurge !== null ? await prepareTerminalRunPurge(internals, terminalRunToPurge, callbacks) : void 0;
|
|
101
123
|
internals.checkpoints.set(workflowId, checkpoint);
|
|
@@ -113,7 +135,8 @@ export async function startWorkflow(internals, type, input, options, additionalS
|
|
|
113
135
|
additionalStartOperations,
|
|
114
136
|
buildWorkflowConcurrencyStartOperations: workflowConcurrency === void 0 ? void 0 : () => buildWorkflowConcurrencyStartOperations(internals, type, workflowId, input, workflowConcurrency),
|
|
115
137
|
callbacks,
|
|
116
|
-
purgeDeleteOperations
|
|
138
|
+
purgeDeleteOperations,
|
|
139
|
+
duplicateIdCondition
|
|
117
140
|
}, buildIdempotentStartOperations);
|
|
118
141
|
if (options?.services !== void 0) {
|
|
119
142
|
internals.workflowServices.set(workflowId, options.services);
|
|
@@ -140,17 +163,6 @@ export async function startWorkflow(internals, type, input, options, additionalS
|
|
|
140
163
|
rollbackTransientStartState(internals, workflowId);
|
|
141
164
|
}
|
|
142
165
|
}
|
|
143
|
-
function applyRestartLineage(state, displacedState) {
|
|
144
|
-
if (displacedState === null)
|
|
145
|
-
return;
|
|
146
|
-
state.restartedFrom = {
|
|
147
|
-
workflowId: displacedState.id,
|
|
148
|
-
...displacedState.workflowExecutionToken !== void 0 && {
|
|
149
|
-
workflowExecutionToken: displacedState.workflowExecutionToken
|
|
150
|
-
},
|
|
151
|
-
replacedAt: state.createdAt
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
166
|
export function resolveScheduledStartAt(internals, options, submissionTime, callbacks) {
|
|
155
167
|
assertExclusiveStartWorkflowOptions(options?.startAt, options?.startAfter);
|
|
156
168
|
if (options?.startAt !== void 0)
|
|
@@ -165,45 +177,3 @@ export function resolveScheduledStartAt(internals, options, submissionTime, call
|
|
|
165
177
|
}
|
|
166
178
|
return;
|
|
167
179
|
}
|
|
168
|
-
export function parseStartOptionDuration(_internals, duration, fieldName, _callbacks) {
|
|
169
|
-
return parseStartWorkflowDuration(duration, fieldName);
|
|
170
|
-
}
|
|
171
|
-
function buildInitialIdentitySlice(workflowId, type, input, versionTuple, executionStateOwnerId, parentWorkflowId, parentWorkflowExecutionToken, delayedStartTimer, now, tags) {
|
|
172
|
-
return {
|
|
173
|
-
id: workflowId,
|
|
174
|
-
type,
|
|
175
|
-
status: delayedStartTimer ? "pending" : "running",
|
|
176
|
-
input,
|
|
177
|
-
versionTuple,
|
|
178
|
-
workflowExecutionToken: crypto.randomUUID(),
|
|
179
|
-
...executionStateOwnerId !== void 0 && { executionStateOwnerId },
|
|
180
|
-
...parentWorkflowId !== void 0 && { parentWorkflowId },
|
|
181
|
-
...parentWorkflowExecutionToken !== void 0 && { parentWorkflowExecutionToken },
|
|
182
|
-
createdAt: now,
|
|
183
|
-
...!delayedStartTimer && { startedAt: now },
|
|
184
|
-
updatedAt: now,
|
|
185
|
-
...tags !== void 0 && { tags }
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
function resolveInitialExecutionDeadline(internals, options, delayedStartTimer, now, callbacks) {
|
|
189
|
-
if (options?.executionTimeout === void 0 || delayedStartTimer)
|
|
190
|
-
return;
|
|
191
|
-
const executionTimeoutMilliseconds = parseStartOptionDuration(internals, options.executionTimeout, "options.executionTimeout", callbacks);
|
|
192
|
-
try {
|
|
193
|
-
return normalizeStorageTimestamp(now + executionTimeoutMilliseconds, "options.executionTimeout");
|
|
194
|
-
} catch {
|
|
195
|
-
throw new StartWorkflowValidationError("options.executionTimeout must resolve to a finite, non-negative deadline");
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
export function createInitialWorkflowState(internals, workflowId, type, input, versionTuple, options, tags, executionStateOwnerId, parentWorkflowId, parentWorkflowExecutionToken, delayedStartTimer, callbacks) {
|
|
199
|
-
const now = internals.options.getNow(), state = buildInitialIdentitySlice(workflowId, type, input, versionTuple, executionStateOwnerId, parentWorkflowId, parentWorkflowExecutionToken, delayedStartTimer, now, tags), executionDeadline = resolveInitialExecutionDeadline(internals, options, delayedStartTimer, now, callbacks);
|
|
200
|
-
if (executionDeadline !== void 0)
|
|
201
|
-
state.executionDeadline = executionDeadline;
|
|
202
|
-
return state;
|
|
203
|
-
}
|
|
204
|
-
export function createInitialCheckpoint(internals, workflowId, workflowVersion, options, _callbacks) {
|
|
205
|
-
const checkpoint = createCheckpoint(workflowId, workflowVersion, internals.options.getNow());
|
|
206
|
-
if (options?.searchAttributes)
|
|
207
|
-
checkpoint.searchAttributes = { ...options.searchAttributes };
|
|
208
|
-
return checkpoint;
|
|
209
|
-
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type { ForkOptions } from '../../types.ts';
|
|
2
|
+
import type { WorkflowHandle } from '../handles.ts';
|
|
3
3
|
import type { EngineInternals } from '../internals.ts';
|
|
4
|
-
import { type LifecycleCallbacks, type RecoverAllOptions
|
|
4
|
+
import { type LifecycleCallbacks, type RecoverAllOptions } from './shared.ts';
|
|
5
5
|
export declare function recoverAll(internals: EngineInternals, callbacks: LifecycleCallbacks, options?: RecoverAllOptions): Promise<WorkflowHandle[]>;
|
|
6
6
|
/** Options for {@link resume}. */
|
|
7
7
|
export type ResumeOptions = {
|
|
@@ -19,4 +19,3 @@ export type ResumeOptions = {
|
|
|
19
19
|
};
|
|
20
20
|
export declare function resume(internals: EngineInternals, workflowId: string, callbacks: LifecycleCallbacks, onRecoveredWorkflow?: RecoverAllOptions['onRecoveredWorkflow'], options?: ResumeOptions): Promise<WorkflowHandle>;
|
|
21
21
|
export declare function fork(internals: EngineInternals, sourceWorkflowId: string, options: ForkOptions | undefined, callbacks: LifecycleCallbacks): Promise<WorkflowHandle>;
|
|
22
|
-
export declare function launchWorkflowFromCheckpoint(internals: EngineInternals, workflowId: string, state: WorkflowState, checkpoint: Checkpoint, registration: RegistrationEntry, callbacks: LifecycleCallbacks): WorkflowHandle;
|
|
@@ -1,31 +1,35 @@
|
|
|
1
1
|
import { KEYS } from "../../../storage/interface.js";
|
|
2
2
|
import { deserializeCheckpoint, serializeCheckpoint } from "../../checkpoint.js";
|
|
3
3
|
import { RegExpExtensionDecodeError } from "../../codec/extension-codec.js";
|
|
4
|
-
import { Context, setContextWorkflowInterceptor } from "../../context.js";
|
|
5
4
|
import { EMPTY_EVENT_HEAD } from "../../event-log.js";
|
|
6
|
-
import { WorkflowRecoverySkippedEvent
|
|
5
|
+
import { WorkflowRecoverySkippedEvent } from "../../events.js";
|
|
7
6
|
import { VersionMismatchError } from "../../versioning.js";
|
|
8
|
-
import { createCancelHandlerRegistration, resetCancelHandlers } from "../cancel-handlers.js";
|
|
9
7
|
import { forgetCommittedCheckpointBytes } from "../checkpoint-commit-snapshots.js";
|
|
10
8
|
import { hydrateCheckpointReplayState } from "../checkpoint-replay.js";
|
|
9
|
+
import { DynamicWorkflowSourceUnavailableError } from "../dynamic-source-errors.js";
|
|
10
|
+
import { resolveExecutableRegistrationOrRenamedNotFound } from "../dynamic-source-execution.js";
|
|
11
11
|
import { WorkflowTypeNotRegisteredForRecoveryError } from "../errors.js";
|
|
12
12
|
import { commitFencedEngineWrite } from "../fenced-write.js";
|
|
13
|
-
import { getWorkflowExecutionStartedAt } from "../handles.js";
|
|
14
13
|
import { WorkflowClaimUnavailableError } from "../lease-errors.js";
|
|
14
|
+
import { WorkflowRevisionUnavailableError } from "../revision-errors.js";
|
|
15
15
|
import { normalizeForkStep, selectPersistedWorkflowStartHeaders } from "../state-utilities.js";
|
|
16
16
|
import { loadWorkflowState } from "../storage-io.js";
|
|
17
|
-
import { getComposedWorkflowInterceptor } from "../strategy-helpers.js";
|
|
18
17
|
import { decodeWorkflowState } from "../validation.js";
|
|
18
|
+
import { launchWorkflowFromCheckpoint } from "./checkpoint-launch.js";
|
|
19
19
|
import {
|
|
20
20
|
buildForkBatchOperations,
|
|
21
21
|
buildForkSearchAttributes,
|
|
22
22
|
createForkLineage,
|
|
23
23
|
createForkedWorkflowState
|
|
24
24
|
} from "./fork-helpers.js";
|
|
25
|
-
import {
|
|
25
|
+
import { derivePreparedExecutionState } from "./persist.js";
|
|
26
|
+
import {
|
|
27
|
+
buildRecoveryRevisionGroups,
|
|
28
|
+
classifyRevisionGroups,
|
|
29
|
+
createRecoveryScopedRevisionCallbacks
|
|
30
|
+
} from "./recovery-revision-groups.js";
|
|
26
31
|
import { resumeWorkflowFromStorage } from "./resume.js";
|
|
27
32
|
import {
|
|
28
|
-
createWorkflowHandle,
|
|
29
33
|
enforceHistoryPolicyBeforeReplayById,
|
|
30
34
|
loadWorkflowStartHeaders,
|
|
31
35
|
setWorkflowStartHeaders
|
|
@@ -39,9 +43,9 @@ function classifyRecoveryState(internals, callbacks, state) {
|
|
|
39
43
|
return { kind: "local", workflowId: state.id };
|
|
40
44
|
if (state.status !== "running")
|
|
41
45
|
return { kind: "ignored" };
|
|
42
|
-
if (!internals.registrations.has(state.type))
|
|
46
|
+
if (!internals.registrations.has(state.type) && !internals.sources.byName.has(state.type))
|
|
43
47
|
return { kind: "missing", workflow: { type: state.type, workflowId: state.id } };
|
|
44
|
-
return { kind: "recoverable", workflowId: state.id };
|
|
48
|
+
return { kind: "recoverable", workflowId: state.id, type: state.type, revision: state.revision };
|
|
45
49
|
}
|
|
46
50
|
function appendRecoveryClassification(result, classification) {
|
|
47
51
|
if (classification.kind === "ignored")
|
|
@@ -76,6 +80,14 @@ async function recoverEntryOrIsolateFailure(internals, workflowId, callbacks, op
|
|
|
76
80
|
}
|
|
77
81
|
if (error instanceof WorkflowClaimUnavailableError)
|
|
78
82
|
return null;
|
|
83
|
+
if (error instanceof WorkflowRevisionUnavailableError) {
|
|
84
|
+
await callbacks.failWorkflowForRevisionUnavailable(workflowId, error);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
if (error instanceof DynamicWorkflowSourceUnavailableError) {
|
|
88
|
+
await callbacks.failWorkflowForUnavailableDynamicSource(workflowId, error);
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
79
91
|
throw error;
|
|
80
92
|
}
|
|
81
93
|
}
|
|
@@ -83,9 +95,13 @@ export async function recoverAll(internals, callbacks, options) {
|
|
|
83
95
|
const preflight = await preflightRecoverAll(internals, callbacks), handles = [];
|
|
84
96
|
if (preflight.missingWorkflows.length > 0 && options?.acknowledgeUnknownWorkflowTypes !== !0)
|
|
85
97
|
throw new WorkflowTypeNotRegisteredForRecoveryError({
|
|
86
|
-
registeredTypes:
|
|
98
|
+
registeredTypes: new Set([
|
|
99
|
+
...internals.registrations.keys(),
|
|
100
|
+
...internals.sources.byName.keys()
|
|
101
|
+
]),
|
|
87
102
|
missingWorkflows: preflight.missingWorkflows
|
|
88
103
|
});
|
|
104
|
+
const recoverableEntries = preflight.entries.filter((entry) => entry.kind === "recoverable"), revisionGroups = buildRecoveryRevisionGroups(recoverableEntries), revisionClassifications = await classifyRevisionGroups(internals, callbacks, revisionGroups), recoveryScopedCallbacks = createRecoveryScopedRevisionCallbacks(callbacks, revisionClassifications);
|
|
89
105
|
for (const entry of preflight.entries) {
|
|
90
106
|
if (entry.kind === "local") {
|
|
91
107
|
handles.push(callbacks.getHandle(entry.workflowId));
|
|
@@ -95,7 +111,7 @@ export async function recoverAll(internals, callbacks, options) {
|
|
|
95
111
|
callbacks.dispatchEvent(new WorkflowRecoverySkippedEvent(entry.workflow.workflowId, entry.workflow.type, "type-not-registered"));
|
|
96
112
|
continue;
|
|
97
113
|
}
|
|
98
|
-
const handle = await recoverEntryOrIsolateFailure(internals, entry.workflowId,
|
|
114
|
+
const handle = await recoverEntryOrIsolateFailure(internals, entry.workflowId, recoveryScopedCallbacks, options);
|
|
99
115
|
if (handle !== null)
|
|
100
116
|
handles.push(handle);
|
|
101
117
|
}
|
|
@@ -115,10 +131,7 @@ export async function fork(internals, sourceWorkflowId, options, callbacks) {
|
|
|
115
131
|
const sourceState = await loadWorkflowState(internals, sourceWorkflowId);
|
|
116
132
|
if (!sourceState)
|
|
117
133
|
throw Error(`Workflow "${sourceWorkflowId}" not found`);
|
|
118
|
-
const registration =
|
|
119
|
-
if (!registration)
|
|
120
|
-
throw Error(`No workflow registered with name "${sourceState.type}" (needed to fork "${sourceWorkflowId}")`);
|
|
121
|
-
const fromStep = options?.fromStep !== void 0 ? normalizeForkStep(options.fromStep) : void 0, checkpointKey = fromStep !== void 0 ? KEYS.checkpointHistory(sourceWorkflowId, fromStep) : KEYS.checkpoint(sourceWorkflowId), checkpointBytes = await internals.storage.get(checkpointKey);
|
|
134
|
+
const { entry: registration, revision: resolvedRevision } = await resolveExecutableRegistrationOrRenamedNotFound((type) => callbacks.resolveExecutableRegistrationForRevision(type, sourceState.revision), sourceState.type, () => Error(`No workflow registered with name "${sourceState.type}" (needed to fork "${sourceWorkflowId}")`)), fromStep = options?.fromStep !== void 0 ? normalizeForkStep(options.fromStep) : void 0, checkpointKey = fromStep !== void 0 ? KEYS.checkpointHistory(sourceWorkflowId, fromStep) : KEYS.checkpoint(sourceWorkflowId), checkpointBytes = await internals.storage.get(checkpointKey);
|
|
122
135
|
if (!checkpointBytes) {
|
|
123
136
|
if (fromStep !== void 0)
|
|
124
137
|
throw Error(`Checkpoint not found at step ${String(fromStep)} for workflow "${sourceWorkflowId}"`);
|
|
@@ -129,14 +142,14 @@ export async function fork(internals, sourceWorkflowId, options, callbacks) {
|
|
|
129
142
|
createdAt: forkedAt,
|
|
130
143
|
workflowId,
|
|
131
144
|
searchAttributes: buildForkSearchAttributes(internals, preparedExecutionState.checkpoint, lineage, callbacks)
|
|
132
|
-
}, forkState = createForkedWorkflowState(internals, workflowId, preparedExecutionState.state, preparedExecutionState.versionTuple, lineage, forkedAt, callbacks);
|
|
145
|
+
}, forkState = createForkedWorkflowState(internals, workflowId, preparedExecutionState.state, preparedExecutionState.versionTuple, lineage, forkedAt, callbacks, resolvedRevision);
|
|
133
146
|
let forkStarted = !1;
|
|
134
147
|
try {
|
|
135
148
|
const forkCheckpointBytes = serializeCheckpoint(forkCheckpoint);
|
|
136
149
|
await commitFencedEngineWrite(internals, workflowId, buildForkBatchOperations(internals, workflowId, forkState, forkCheckpoint, forkCheckpointBytes, persistedWorkflowStartHeaders, callbacks), [], () => Error(`Fork of workflow "${workflowId}" lost its CAS race.`));
|
|
137
150
|
internals.eventLogHeads.set(workflowId, EMPTY_EVENT_HEAD);
|
|
138
151
|
setWorkflowStartHeaders(internals, workflowId, persistedWorkflowStartHeaders, callbacks);
|
|
139
|
-
const handle = launchWorkflowFromCheckpoint(internals, workflowId, forkState, forkCheckpoint, registration, callbacks);
|
|
152
|
+
const handle = launchWorkflowFromCheckpoint(internals, workflowId, forkState, forkCheckpoint, registration, resolvedRevision, callbacks);
|
|
140
153
|
forkStarted = !0;
|
|
141
154
|
return handle;
|
|
142
155
|
} finally {
|
|
@@ -149,66 +162,3 @@ export async function fork(internals, sourceWorkflowId, options, callbacks) {
|
|
|
149
162
|
}
|
|
150
163
|
}
|
|
151
164
|
}
|
|
152
|
-
function launchInlineWorkflowFromCheckpoint(internals, workflowId, state, checkpoint, registration, callbacks) {
|
|
153
|
-
const inlineStrategy = internals.inlineStrategy;
|
|
154
|
-
if (!inlineStrategy)
|
|
155
|
-
throw Error("Inline workflow launch requested without an inline strategy.");
|
|
156
|
-
const accumulatedResults = new Map(checkpoint.accumulatedResults), workflowAbort = new AbortController;
|
|
157
|
-
resetCancelHandlers(internals, workflowId);
|
|
158
|
-
const context = new Context({
|
|
159
|
-
workflowId,
|
|
160
|
-
...state.workflowExecutionToken !== void 0 && {
|
|
161
|
-
workflowExecutionToken: state.workflowExecutionToken
|
|
162
|
-
},
|
|
163
|
-
workflowType: state.type,
|
|
164
|
-
startedAt: getWorkflowExecutionStartedAt(state),
|
|
165
|
-
abortController: workflowAbort,
|
|
166
|
-
getNow: internals.options.getNow,
|
|
167
|
-
resolveWorkflowType: callbacks.resolveWorkflowTypeTarget,
|
|
168
|
-
executionStateOwnerId: state.executionStateOwnerId ?? workflowId,
|
|
169
|
-
accumulatedResults,
|
|
170
|
-
searchAttributes: checkpoint.searchAttributes,
|
|
171
|
-
registerCancelHandler: createCancelHandlerRegistration(internals, workflowId),
|
|
172
|
-
...registration.searchAttributes && {
|
|
173
|
-
searchAttributeSchema: registration.searchAttributes
|
|
174
|
-
},
|
|
175
|
-
sleepReferenceTime: checkpoint.createdAt,
|
|
176
|
-
...state.executionDeadline !== void 0 && { deadline: state.executionDeadline },
|
|
177
|
-
...internals.options.onLog != null && { logSink: internals.options.onLog }
|
|
178
|
-
});
|
|
179
|
-
setContextWorkflowInterceptor(context, getComposedWorkflowInterceptor(internals));
|
|
180
|
-
if (internals.options.development)
|
|
181
|
-
context.explain(!0);
|
|
182
|
-
const generator = registration.handler(context, state.input);
|
|
183
|
-
inlineStrategy.adoptWorkflow(workflowId, generator, context, workflowAbort);
|
|
184
|
-
inlineStrategy.continueWorkflow(workflowId, void 0);
|
|
185
|
-
callbacks.swallowPromiseRejection(callbacks.processPendingUpdatesAfterInlineAdvance(workflowId));
|
|
186
|
-
}
|
|
187
|
-
function launchWorkerWorkflowFromCheckpoint(internals, workflowId, state, checkpoint) {
|
|
188
|
-
const serialized = serializeCheckpoint(checkpoint);
|
|
189
|
-
internals.strategy.startWorkflow({
|
|
190
|
-
workflowId,
|
|
191
|
-
...state.workflowExecutionToken !== void 0 && {
|
|
192
|
-
workflowExecutionToken: state.workflowExecutionToken
|
|
193
|
-
},
|
|
194
|
-
workflowType: state.type,
|
|
195
|
-
input: state.input,
|
|
196
|
-
checkpoint: serialized,
|
|
197
|
-
executionStateOwnerId: state.executionStateOwnerId ?? workflowId,
|
|
198
|
-
...state.executionDeadline !== void 0 && { deadline: state.executionDeadline },
|
|
199
|
-
...internals.workflowHeaders.has(workflowId) && {
|
|
200
|
-
headers: [...internals.workflowHeaders.get(workflowId)]
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
export function launchWorkflowFromCheckpoint(internals, workflowId, state, checkpoint, registration, callbacks) {
|
|
205
|
-
internals.checkpoints.set(workflowId, checkpoint);
|
|
206
|
-
internals.workflowVersionTuples.set(workflowId, createWorkflowVersionTuple(internals, registration, callbacks));
|
|
207
|
-
const handle = createWorkflowHandle(internals, workflowId, callbacks);
|
|
208
|
-
callbacks.dispatchEvent(new WorkflowStartedEvent(workflowId, state.type, state.input));
|
|
209
|
-
if (internals.inlineStrategy)
|
|
210
|
-
launchInlineWorkflowFromCheckpoint(internals, workflowId, state, checkpoint, registration, callbacks);
|
|
211
|
-
else
|
|
212
|
-
launchWorkerWorkflowFromCheckpoint(internals, workflowId, state, checkpoint);
|
|
213
|
-
return handle;
|
|
214
|
-
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
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
|
-
export {
|
|
3
|
+
export { resolveScheduledStartAt, start, startWorkflow } from './lifecycle/start.ts';
|
|
4
|
+
export { applyRestartLineage, createInitialCheckpoint, createInitialWorkflowState, parseStartOptionDuration, } from './lifecycle/start-state.ts';
|
|
4
5
|
export { startOrSignal, startWithIdempotency, type StartOrSignalCallbacks, type StartOrSignalResult, } from './lifecycle/start-or-signal.ts';
|
|
5
6
|
export { buildInitialSearchAttributeOperations, buildStartBatchOperations, validateSearchAttributes, } from './lifecycle/start-batch.ts';
|
|
6
7
|
export { beginWorkflowExecution, runWorkflowStartInterceptor, startWorkflowExecution, } from './lifecycle/start-exec.ts';
|
|
7
8
|
export { buildForkBatchOperations, buildForkSearchAttributes, createForkLineage, createForkedWorkflowState, } from './lifecycle/fork-helpers.ts';
|
|
8
|
-
export {
|
|
9
|
+
export { launchWorkflowFromCheckpoint } from './lifecycle/checkpoint-launch.ts';
|
|
10
|
+
export { fork, recoverAll, resume } from './lifecycle/transition.ts';
|
|
9
11
|
export { resumeWorkflowFromStorage } from './lifecycle/resume.ts';
|
|
@@ -15,14 +15,13 @@ export {
|
|
|
15
15
|
workflowStateWithVersionTuple,
|
|
16
16
|
workflowVersionTupleFromState
|
|
17
17
|
} from "./lifecycle/persist.js";
|
|
18
|
+
export { resolveScheduledStartAt, start, startWorkflow } from "./lifecycle/start.js";
|
|
18
19
|
export {
|
|
20
|
+
applyRestartLineage,
|
|
19
21
|
createInitialCheckpoint,
|
|
20
22
|
createInitialWorkflowState,
|
|
21
|
-
parseStartOptionDuration
|
|
22
|
-
|
|
23
|
-
start,
|
|
24
|
-
startWorkflow
|
|
25
|
-
} from "./lifecycle/start.js";
|
|
23
|
+
parseStartOptionDuration
|
|
24
|
+
} from "./lifecycle/start-state.js";
|
|
26
25
|
export {
|
|
27
26
|
startOrSignal,
|
|
28
27
|
startWithIdempotency
|
|
@@ -43,5 +42,6 @@ export {
|
|
|
43
42
|
createForkLineage,
|
|
44
43
|
createForkedWorkflowState
|
|
45
44
|
} from "./lifecycle/fork-helpers.js";
|
|
46
|
-
export {
|
|
45
|
+
export { launchWorkflowFromCheckpoint } from "./lifecycle/checkpoint-launch.js";
|
|
46
|
+
export { fork, recoverAll, resume } from "./lifecycle/transition.js";
|
|
47
47
|
export { resumeWorkflowFromStorage } from "./lifecycle/resume.js";
|