@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.
Files changed (243) hide show
  1. package/README.md +2 -2
  2. package/dist/cli/conformance.js +5 -2
  3. package/dist/cli/generated/operation-catalog.snapshot.json +95 -1
  4. package/dist/cli/generated/operation-client.generated.d.ts +28 -2
  5. package/dist/cli/generated/operation-client.generated.js +2 -0
  6. package/dist/cli/parse-schedule-arguments.js +15 -3
  7. package/dist/cli/schedule.js +2 -1
  8. package/dist/cli/types.d.ts +2 -1
  9. package/dist/client/http-client-requests.js +6 -9
  10. package/dist/client/start-body.js +1 -0
  11. package/dist/core/catalog/index.d.ts +2 -2
  12. package/dist/core/catalog/index.js +9 -2
  13. package/dist/core/catalog/reference-counts.d.ts +29 -14
  14. package/dist/core/catalog/removal.d.ts +63 -10
  15. package/dist/core/catalog/removal.js +18 -2
  16. package/dist/core/catalog/storage-io.d.ts +23 -0
  17. package/dist/core/catalog/storage-io.js +1 -1
  18. package/dist/core/engine/activity-resolution.js +17 -17
  19. package/dist/core/engine/bulk-operations-purge.js +14 -20
  20. package/dist/core/engine/bulk-operations-retry.d.ts +9 -0
  21. package/dist/core/engine/bulk-operations-retry.js +177 -0
  22. package/dist/core/engine/bulk-operations-shared.d.ts +18 -0
  23. package/dist/core/engine/bulk-operations-shared.js +14 -0
  24. package/dist/core/engine/bulk-operations.js +8 -167
  25. package/dist/core/engine/callback-creators-bundles.js +4 -2
  26. package/dist/core/engine/callback-creators-core.js +9 -1
  27. package/dist/core/engine/callback-creators-schedule.js +2 -2
  28. package/dist/core/engine/catalog-readiness.js +2 -0
  29. package/dist/core/engine/catalog-removal.d.ts +107 -3
  30. package/dist/core/engine/catalog-removal.js +66 -13
  31. package/dist/core/engine/catalog-tombstone-recovery.d.ts +56 -0
  32. package/dist/core/engine/catalog-tombstone-recovery.js +39 -0
  33. package/dist/core/engine/constraints.js +5 -1
  34. package/dist/core/engine/construction.d.ts +1 -1
  35. package/dist/core/engine/decode-revision.d.ts +32 -0
  36. package/dist/core/engine/decode-revision.js +9 -0
  37. package/dist/core/engine/disposal.d.ts +6 -2
  38. package/dist/core/engine/disposal.js +24 -10
  39. package/dist/core/engine/dynamic-source-errors.d.ts +81 -0
  40. package/dist/core/engine/dynamic-source-errors.js +25 -0
  41. package/dist/core/engine/dynamic-source-execution.d.ts +173 -0
  42. package/dist/core/engine/dynamic-source-execution.js +108 -0
  43. package/dist/core/engine/engine-internal-types.d.ts +16 -0
  44. package/dist/core/engine/engine-workflows-namespace.d.ts +10 -0
  45. package/dist/core/engine/engine-workflows-namespace.js +3 -1
  46. package/dist/core/engine/index.d.ts +18 -10
  47. package/dist/core/engine/index.js +21 -10
  48. package/dist/core/engine/inline-launch-queue.js +1 -1
  49. package/dist/core/engine/internals.d.ts +26 -30
  50. package/dist/core/engine/lifecycle/checkpoint-launch.d.ts +29 -0
  51. package/dist/core/engine/lifecycle/checkpoint-launch.js +75 -0
  52. package/dist/core/engine/lifecycle/delayed-start-registration.d.ts +30 -0
  53. package/dist/core/engine/lifecycle/delayed-start-registration.js +11 -0
  54. package/dist/core/engine/lifecycle/fork-helpers.d.ts +33 -1
  55. package/dist/core/engine/lifecycle/fork-helpers.js +3 -1
  56. package/dist/core/engine/lifecycle/recovery-revision-groups.d.ts +75 -0
  57. package/dist/core/engine/lifecycle/recovery-revision-groups.js +64 -0
  58. package/dist/core/engine/lifecycle/resume-generation-guard.d.ts +58 -0
  59. package/dist/core/engine/lifecycle/resume-generation-guard.js +11 -0
  60. package/dist/core/engine/lifecycle/resume.js +18 -9
  61. package/dist/core/engine/lifecycle/shared.d.ts +43 -0
  62. package/dist/core/engine/lifecycle/standalone-claim-acquire.d.ts +12 -0
  63. package/dist/core/engine/lifecycle/standalone-claim-acquire.js +6 -0
  64. package/dist/core/engine/lifecycle/start-commit-errors.d.ts +12 -0
  65. package/dist/core/engine/lifecycle/start-commit-errors.js +6 -0
  66. package/dist/core/engine/lifecycle/start-commit.d.ts +22 -7
  67. package/dist/core/engine/lifecycle/start-commit.js +49 -15
  68. package/dist/core/engine/lifecycle/start-exec.d.ts +18 -2
  69. package/dist/core/engine/lifecycle/start-exec.js +7 -5
  70. package/dist/core/engine/lifecycle/start-or-signal-create.js +1 -3
  71. package/dist/core/engine/lifecycle/start-or-signal.js +1 -1
  72. package/dist/core/engine/lifecycle/start-precondition-attribution.d.ts +49 -0
  73. package/dist/core/engine/lifecycle/start-precondition-attribution.js +13 -0
  74. package/dist/core/engine/lifecycle/start-revision-resolution.d.ts +34 -0
  75. package/dist/core/engine/lifecycle/start-revision-resolution.js +11 -0
  76. package/dist/core/engine/lifecycle/start-state.d.ts +16 -0
  77. package/dist/core/engine/lifecycle/start-state.js +60 -0
  78. package/dist/core/engine/lifecycle/start-terminal-conflict-purge.d.ts +66 -3
  79. package/dist/core/engine/lifecycle/start-terminal-conflict-purge.js +13 -3
  80. package/dist/core/engine/lifecycle/start.d.ts +16 -6
  81. package/dist/core/engine/lifecycle/start.js +38 -68
  82. package/dist/core/engine/lifecycle/transition.d.ts +3 -4
  83. package/dist/core/engine/lifecycle/transition.js +30 -80
  84. package/dist/core/engine/lifecycle.d.ts +4 -2
  85. package/dist/core/engine/lifecycle.js +6 -6
  86. package/dist/core/engine/listing.js +15 -1
  87. package/dist/core/engine/memo-durable-activity.js +4 -1
  88. package/dist/core/engine/nonterminal-revision-count.d.ts +25 -0
  89. package/dist/core/engine/nonterminal-revision-count.js +16 -0
  90. package/dist/core/engine/operations-time.d.ts +5 -23
  91. package/dist/core/engine/operations-time.js +13 -11
  92. package/dist/core/engine/ownership-bootstrap.d.ts +1 -1
  93. package/dist/core/engine/pinned-schedule-revision-count.d.ts +26 -0
  94. package/dist/core/engine/pinned-schedule-revision-count.js +19 -0
  95. package/dist/core/engine/pinned-schedule-revision.d.ts +76 -0
  96. package/dist/core/engine/pinned-schedule-revision.js +53 -0
  97. package/dist/core/engine/registration.d.ts +30 -0
  98. package/dist/core/engine/registration.js +17 -2
  99. package/dist/core/engine/retention.js +9 -2
  100. package/dist/core/engine/revision-errors.d.ts +71 -0
  101. package/dist/core/engine/revision-errors.js +13 -0
  102. package/dist/core/engine/schedule-revision-fire.d.ts +17 -0
  103. package/dist/core/engine/schedule-revision-fire.js +3 -0
  104. package/dist/core/engine/schedule-run.js +2 -1
  105. package/dist/core/engine/schedules.d.ts +2 -1
  106. package/dist/core/engine/schedules.js +28 -9
  107. package/dist/core/engine/source-diagnostics.d.ts +76 -0
  108. package/dist/core/engine/source-diagnostics.js +105 -0
  109. package/dist/core/engine/source-registration.js +4 -2
  110. package/dist/core/engine/source-resolution.d.ts +25 -9
  111. package/dist/core/engine/source-resolution.js +49 -14
  112. package/dist/core/engine/source-runtime-state.d.ts +92 -0
  113. package/dist/core/engine/source-runtime-state.js +36 -0
  114. package/dist/core/engine/storage-io.d.ts +19 -2
  115. package/dist/core/engine/storage-io.js +21 -2
  116. package/dist/core/engine/termination/finalizer-registration.d.ts +18 -0
  117. package/dist/core/engine/termination/finalizer-registration.js +21 -0
  118. package/dist/core/engine/termination/finalizer.d.ts +16 -19
  119. package/dist/core/engine/termination/finalizer.js +3 -3
  120. package/dist/core/engine/time-operation-callbacks.d.ts +32 -0
  121. package/dist/core/engine/time-operation-callbacks.js +0 -0
  122. package/dist/core/engine/validation/schedule-cadence.d.ts +16 -0
  123. package/dist/core/engine/validation/schedule-cadence.js +20 -0
  124. package/dist/core/engine/validation/schedule-options.d.ts +4 -1
  125. package/dist/core/engine/validation/schedule-options.js +14 -0
  126. package/dist/core/engine/validation/schedule-revision.d.ts +24 -0
  127. package/dist/core/engine/validation/schedule-revision.js +21 -0
  128. package/dist/core/engine/validation/schedule-warnings.d.ts +11 -0
  129. package/dist/core/engine/validation/schedule-warnings.js +5 -0
  130. package/dist/core/engine/validation/schedule.d.ts +4 -3
  131. package/dist/core/engine/validation/schedule.js +15 -26
  132. package/dist/core/engine/validation.js +3 -0
  133. package/dist/core/engine/workflow-claim-reclaim-target.d.ts +1 -1
  134. package/dist/core/engine/workflow-claim-reclaim-target.js +16 -2
  135. package/dist/core/engine/workflow-retention-deadline.d.ts +16 -0
  136. package/dist/core/engine/workflow-retention-deadline.js +33 -0
  137. package/dist/core/events/event-map.d.ts +5 -0
  138. package/dist/core/events/index.d.ts +1 -0
  139. package/dist/core/events/index.js +1 -0
  140. package/dist/core/events/workflow-source-events.d.ts +102 -0
  141. package/dist/core/events/workflow-source-events.js +57 -0
  142. package/dist/core/execution-strategy.d.ts +7 -0
  143. package/dist/core/inline-execution-strategy.context-options.d.ts +1 -1
  144. package/dist/core/inline-execution-strategy.js +1 -1
  145. package/dist/core/types/checkpoint.d.ts +14 -0
  146. package/dist/core/types/schedules.d.ts +52 -1
  147. package/dist/core/types/state.d.ts +16 -0
  148. package/dist/core/weft-error.d.ts +1 -1
  149. package/dist/core/weft-error.js +4 -1
  150. package/dist/core/worker-execution-strategy.d.ts +1 -0
  151. package/dist/core/worker-execution-strategy.js +13 -1
  152. package/dist/core/worker-fault-handling.d.ts +10 -0
  153. package/dist/core/worker-fault-handling.js +1 -0
  154. package/dist/core/worker-inbound-message.d.ts +1 -0
  155. package/dist/core/worker-inbound-message.js +1 -0
  156. package/dist/core/worker-protocol-guard.js +4 -0
  157. package/dist/core/worker-protocol.d.ts +7 -0
  158. package/dist/core/worker-protocol.js +2 -1
  159. package/dist/core/worker-turn-watchdog.d.ts +8 -1
  160. package/dist/core/worker-turn-watchdog.js +3 -2
  161. package/dist/diagnostics/format.js +5 -0
  162. package/dist/diagnostics/types.d.ts +24 -0
  163. package/dist/diagnostics/version-check.js +8 -2
  164. package/dist/http.js +1 -1
  165. package/dist/index.d.ts +3 -3
  166. package/dist/index.js +7 -0
  167. package/dist/indexeddb.js +1 -1
  168. package/dist/json-schema.js +3 -3
  169. package/dist/server/fault-to-json-rpc.js +2 -1
  170. package/dist/server/index.d.ts +11 -0
  171. package/dist/server/operation-fault.d.ts +2 -0
  172. package/dist/server/operation-fault.js +2 -1
  173. package/dist/server/operations/create-schedule.d.ts +2 -0
  174. package/dist/server/operations/create-schedule.js +10 -6
  175. package/dist/server/operations/get-catalog-diagnostics.d.ts +28 -0
  176. package/dist/server/operations/get-catalog-diagnostics.js +10 -2
  177. package/dist/server/operations/get-task-detail-schema.d.ts +8 -0
  178. package/dist/server/operations/get-task-detail-schema.js +1 -0
  179. package/dist/server/operations/get-task-detail.d.ts +8 -0
  180. package/dist/server/operations/get-task-detail.js +1 -0
  181. package/dist/server/operations/get-task-diagnostics.d.ts +4 -4
  182. package/dist/server/operations/preload-workflow-revision.d.ts +28 -0
  183. package/dist/server/operations/preload-workflow-revision.js +60 -0
  184. package/dist/server/operations/schedule-faults.d.ts +1 -0
  185. package/dist/server/operations/schedule-faults.js +32 -4
  186. package/dist/server/operations/schedule-rest-body.d.ts +1 -0
  187. package/dist/server/operations/schedule-rest-body.js +2 -1
  188. package/dist/server/operations/static-registrations.js +6 -0
  189. package/dist/server/operations/update-schedule.d.ts +2 -0
  190. package/dist/server/operations/update-schedule.js +4 -2
  191. package/dist/server/operations/workflow-catalog-operation-helpers.js +27 -0
  192. package/dist/server/runtime/task-dispatch-envelope.d.ts +14 -0
  193. package/dist/server/runtime/task-dispatch-envelope.js +32 -0
  194. package/dist/server/runtime/task-dispatch-revision.d.ts +29 -0
  195. package/dist/server/runtime/task-dispatch-revision.js +13 -0
  196. package/dist/server/runtime/task-dispatch.js +12 -37
  197. package/dist/server/runtime/task-ledger-recovery.js +1 -1
  198. package/dist/server/runtime/task-ledger-runtime.d.ts +14 -2
  199. package/dist/server/runtime/task-ledger-runtime.js +5 -3
  200. package/dist/server/runtime/task-polling.js +9 -3
  201. package/dist/server/runtime/task-reconciliation.js +2 -1
  202. package/dist/server/runtime/websocket-worker.js +9 -0
  203. package/dist/server/task-ledger-codec.d.ts +9 -0
  204. package/dist/server/task-ledger-codec.js +4 -1
  205. package/dist/server/task-ledger-transition-helpers.js +1 -0
  206. package/dist/server/task-ledger-types.d.ts +9 -0
  207. package/dist/server/task-ledger.d.ts +1 -1
  208. package/dist/server/task-ledger.js +2 -1
  209. package/dist/server/task-queue-types.d.ts +2 -0
  210. package/dist/storage/bun-sql.js +3 -1
  211. package/dist/storage/catalog-keys.d.ts +19 -0
  212. package/dist/storage/catalog-keys.js +3 -1
  213. package/dist/storage/compressed-storage.js +1 -1
  214. package/dist/storage/index.d.ts +3 -1
  215. package/dist/storage/interface.d.ts +2 -0
  216. package/dist/storage/interface.js +1 -1
  217. package/dist/storage/key-prefixes.d.ts +1 -1
  218. package/dist/storage/key-prefixes.js +1 -0
  219. package/dist/storage/lmdb.d.ts +30 -2
  220. package/dist/storage/lmdb.js +1 -1
  221. package/dist/storage/memory.js +1 -1
  222. package/dist/storage/neon.js +2 -2
  223. package/dist/storage/node-sqlite.js +3 -1
  224. package/dist/storage/postgres.js +2 -2
  225. package/dist/storage/resolve.js +1 -1
  226. package/dist/storage/scoped-storage.js +1 -1
  227. package/dist/storage/testing.js +1 -1
  228. package/dist/storage/turso.js +1 -1
  229. package/dist/version.d.ts +1 -1
  230. package/dist/version.js +1 -1
  231. package/dist/web-extension.js +1 -1
  232. package/dist/worker/protocol-messages.d.ts +17 -0
  233. package/dist/worker/protocol-schemas.d.ts +32 -0
  234. package/dist/worker/protocol-schemas.js +7 -3
  235. package/dist/worker/protocol-task-result.js +35 -3
  236. package/dist/worker/protocol.js +1 -1
  237. package/dist/worker/registry/types.d.ts +2 -0
  238. package/dist/worker/registry.d.ts +1 -1
  239. package/dist/worker/registry.js +3 -1
  240. package/dist/workers/workflow-runner.d.ts +9 -0
  241. package/dist/workers/workflow-runner.js +9 -2
  242. package/dist/workers/workflow-worker-entry.js +9 -4
  243. 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
- * Returns `null` when there is no existing record (the create proceeds normally).
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<WorkflowState | null>;
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(KEYS.workflow(workflowId));
14
+ const key = KEYS.workflow(workflowId), existingBytes = await internals.storage.get(key);
11
15
  if (existingBytes === null)
12
- return null;
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 existingState;
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 { Checkpoint, Duration, StartOptions, StartWorkflowOptions, TimerEntry, WorkflowState } from '../../types.ts';
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): Promise<WorkflowHandle>;
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 { releaseInFlightStart, reserveInFlightStart } from "../catalog-removal.js";
10
+ import {
11
+ releaseInFlightStart,
12
+ resolveAndReserveExecutableRegistration
13
+ } from "../catalog-removal.js";
13
14
  import { forgetCommittedCheckpointBytes } from "../checkpoint-commit-snapshots.js";
14
- import { WorkflowAlreadyExistsError, WorkflowNotRegisteredError } from "../errors.js";
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
- export async function startWorkflow(internals, type, input, options, additionalStartOperations, callbacks, buildIdempotentStartOperations) {
74
- const registration = internals.registrations.get(type);
75
- if (!registration)
76
- throw new WorkflowNotRegisteredError(type);
77
- const workflowConcurrency = registration.concurrency;
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 versionTuple = createWorkflowVersionTuple(internals, registration, callbacks), state = createInitialWorkflowState(internals, workflowId, type, input, versionTuple, options, preparation.normalizedTags, executionStateOwnerId, parentWorkflowId, parentWorkflowExecutionToken, delayedStartTimer, callbacks);
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 { Checkpoint, ForkOptions, WorkflowState } from '../../types.ts';
2
- import { type WorkflowHandle } from '../handles.ts';
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, type RegistrationEntry } from './shared.ts';
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, WorkflowStartedEvent } from "../../events.js";
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 { createWorkflowVersionTuple, derivePreparedExecutionState } from "./persist.js";
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: internals.registrations.keys(),
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, callbacks, options);
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 = internals.registrations.get(sourceState.type);
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 { createInitialCheckpoint, createInitialWorkflowState, parseStartOptionDuration, resolveScheduledStartAt, start, startWorkflow, } from './lifecycle/start.ts';
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 { fork, launchWorkflowFromCheckpoint, recoverAll, resume } from './lifecycle/transition.ts';
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
- resolveScheduledStartAt,
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 { fork, launchWorkflowFromCheckpoint, recoverAll, resume } from "./lifecycle/transition.js";
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";