@lostgradient/weft 0.24.0 → 0.24.2

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 (283) 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.d.ts +1 -1
  20. package/dist/core/engine/bulk-operations-purge.js +17 -22
  21. package/dist/core/engine/bulk-operations-retry.d.ts +24 -0
  22. package/dist/core/engine/bulk-operations-retry.js +190 -0
  23. package/dist/core/engine/bulk-operations-shared.d.ts +18 -0
  24. package/dist/core/engine/bulk-operations-shared.js +14 -0
  25. package/dist/core/engine/bulk-operations.d.ts +3 -2
  26. package/dist/core/engine/bulk-operations.js +10 -169
  27. package/dist/core/engine/callback-creators-bundles.js +5 -3
  28. package/dist/core/engine/callback-creators-core.js +9 -1
  29. package/dist/core/engine/callback-creators-schedule.js +2 -2
  30. package/dist/core/engine/catalog-readiness.js +2 -0
  31. package/dist/core/engine/catalog-removal.d.ts +107 -3
  32. package/dist/core/engine/catalog-removal.js +66 -13
  33. package/dist/core/engine/catalog-tombstone-recovery.d.ts +56 -0
  34. package/dist/core/engine/catalog-tombstone-recovery.js +39 -0
  35. package/dist/core/engine/child-workflow.d.ts +13 -1
  36. package/dist/core/engine/child-workflow.js +22 -4
  37. package/dist/core/engine/constraints.js +5 -1
  38. package/dist/core/engine/construction.d.ts +1 -1
  39. package/dist/core/engine/decode-revision.d.ts +32 -0
  40. package/dist/core/engine/decode-revision.js +9 -0
  41. package/dist/core/engine/disposal.d.ts +6 -2
  42. package/dist/core/engine/disposal.js +24 -10
  43. package/dist/core/engine/dynamic-source-errors.d.ts +81 -0
  44. package/dist/core/engine/dynamic-source-errors.js +25 -0
  45. package/dist/core/engine/dynamic-source-execution.d.ts +173 -0
  46. package/dist/core/engine/dynamic-source-execution.js +108 -0
  47. package/dist/core/engine/engine-internal-types.d.ts +16 -0
  48. package/dist/core/engine/engine-workflows-namespace.d.ts +10 -0
  49. package/dist/core/engine/engine-workflows-namespace.js +3 -1
  50. package/dist/core/engine/generation-codec.d.ts +22 -0
  51. package/dist/core/engine/generation-codec.js +2 -0
  52. package/dist/core/engine/index.d.ts +18 -10
  53. package/dist/core/engine/index.js +22 -11
  54. package/dist/core/engine/inline-launch-queue.js +1 -1
  55. package/dist/core/engine/internals.d.ts +26 -30
  56. package/dist/core/engine/lifecycle/checkpoint-launch.d.ts +29 -0
  57. package/dist/core/engine/lifecycle/checkpoint-launch.js +75 -0
  58. package/dist/core/engine/lifecycle/delayed-start-registration.d.ts +30 -0
  59. package/dist/core/engine/lifecycle/delayed-start-registration.js +11 -0
  60. package/dist/core/engine/lifecycle/fork-helpers.d.ts +33 -1
  61. package/dist/core/engine/lifecycle/fork-helpers.js +3 -1
  62. package/dist/core/engine/lifecycle/recovery-isolation.d.ts +20 -0
  63. package/dist/core/engine/lifecycle/recovery-isolation.js +26 -0
  64. package/dist/core/engine/lifecycle/recovery-revision-groups.d.ts +75 -0
  65. package/dist/core/engine/lifecycle/recovery-revision-groups.js +64 -0
  66. package/dist/core/engine/lifecycle/resume-body.d.ts +44 -0
  67. package/dist/core/engine/lifecycle/resume-body.js +211 -0
  68. package/dist/core/engine/lifecycle/resume-generation-guard.d.ts +58 -0
  69. package/dist/core/engine/lifecycle/resume-generation-guard.js +11 -0
  70. package/dist/core/engine/lifecycle/resume.d.ts +46 -3
  71. package/dist/core/engine/lifecycle/resume.js +21 -201
  72. package/dist/core/engine/lifecycle/shared.d.ts +43 -0
  73. package/dist/core/engine/lifecycle/standalone-claim-acquire.d.ts +80 -2
  74. package/dist/core/engine/lifecycle/standalone-claim-acquire.js +20 -4
  75. package/dist/core/engine/lifecycle/start-commit-errors.d.ts +12 -0
  76. package/dist/core/engine/lifecycle/start-commit-errors.js +6 -0
  77. package/dist/core/engine/lifecycle/start-commit.d.ts +24 -7
  78. package/dist/core/engine/lifecycle/start-commit.js +57 -15
  79. package/dist/core/engine/lifecycle/start-exec.d.ts +18 -2
  80. package/dist/core/engine/lifecycle/start-exec.js +7 -5
  81. package/dist/core/engine/lifecycle/start-or-signal-create.js +1 -3
  82. package/dist/core/engine/lifecycle/start-or-signal-resolution.d.ts +8 -2
  83. package/dist/core/engine/lifecycle/start-or-signal-resolution.js +11 -3
  84. package/dist/core/engine/lifecycle/start-or-signal.js +1 -1
  85. package/dist/core/engine/lifecycle/start-precondition-attribution.d.ts +55 -0
  86. package/dist/core/engine/lifecycle/start-precondition-attribution.js +13 -0
  87. package/dist/core/engine/lifecycle/start-revision-resolution.d.ts +34 -0
  88. package/dist/core/engine/lifecycle/start-revision-resolution.js +11 -0
  89. package/dist/core/engine/lifecycle/start-schedule-timing.d.ts +11 -0
  90. package/dist/core/engine/lifecycle/start-schedule-timing.js +21 -0
  91. package/dist/core/engine/lifecycle/start-state.d.ts +16 -0
  92. package/dist/core/engine/lifecycle/start-state.js +60 -0
  93. package/dist/core/engine/lifecycle/start-terminal-conflict-purge.d.ts +146 -4
  94. package/dist/core/engine/lifecycle/start-terminal-conflict-purge.js +33 -4
  95. package/dist/core/engine/lifecycle/start.d.ts +54 -7
  96. package/dist/core/engine/lifecycle/start.js +53 -90
  97. package/dist/core/engine/lifecycle/transition.d.ts +12 -4
  98. package/dist/core/engine/lifecycle/transition.js +40 -95
  99. package/dist/core/engine/lifecycle.d.ts +5 -2
  100. package/dist/core/engine/lifecycle.js +7 -6
  101. package/dist/core/engine/listing.js +15 -1
  102. package/dist/core/engine/memo-durable-activity.js +4 -1
  103. package/dist/core/engine/nonterminal-revision-count.d.ts +25 -0
  104. package/dist/core/engine/nonterminal-revision-count.js +16 -0
  105. package/dist/core/engine/operations-time.d.ts +5 -23
  106. package/dist/core/engine/operations-time.js +13 -11
  107. package/dist/core/engine/ownership-bootstrap.d.ts +1 -1
  108. package/dist/core/engine/pinned-schedule-revision-count.d.ts +26 -0
  109. package/dist/core/engine/pinned-schedule-revision-count.js +19 -0
  110. package/dist/core/engine/pinned-schedule-revision.d.ts +76 -0
  111. package/dist/core/engine/pinned-schedule-revision.js +53 -0
  112. package/dist/core/engine/registration.d.ts +30 -0
  113. package/dist/core/engine/registration.js +17 -2
  114. package/dist/core/engine/retention.js +9 -2
  115. package/dist/core/engine/revision-errors.d.ts +71 -0
  116. package/dist/core/engine/revision-errors.js +13 -0
  117. package/dist/core/engine/schedule-overlap.js +2 -1
  118. package/dist/core/engine/schedule-revision-fire.d.ts +17 -0
  119. package/dist/core/engine/schedule-revision-fire.js +3 -0
  120. package/dist/core/engine/schedule-run.d.ts +10 -0
  121. package/dist/core/engine/schedule-run.js +2 -1
  122. package/dist/core/engine/schedules.d.ts +11 -1
  123. package/dist/core/engine/schedules.js +28 -9
  124. package/dist/core/engine/source-diagnostics.d.ts +76 -0
  125. package/dist/core/engine/source-diagnostics.js +105 -0
  126. package/dist/core/engine/source-registration.js +4 -2
  127. package/dist/core/engine/source-resolution.d.ts +25 -9
  128. package/dist/core/engine/source-resolution.js +49 -14
  129. package/dist/core/engine/source-runtime-state.d.ts +92 -0
  130. package/dist/core/engine/source-runtime-state.js +36 -0
  131. package/dist/core/engine/storage-io.d.ts +19 -2
  132. package/dist/core/engine/storage-io.js +21 -2
  133. package/dist/core/engine/termination/complete.js +1 -1
  134. package/dist/core/engine/termination/finalizer-registration.d.ts +18 -0
  135. package/dist/core/engine/termination/finalizer-registration.js +21 -0
  136. package/dist/core/engine/termination/finalizer.d.ts +16 -19
  137. package/dist/core/engine/termination/finalizer.js +3 -3
  138. package/dist/core/engine/termination/suspend.js +3 -0
  139. package/dist/core/engine/time-operation-callbacks.d.ts +32 -0
  140. package/dist/core/engine/time-operation-callbacks.js +0 -0
  141. package/dist/core/engine/validation/schedule-cadence.d.ts +16 -0
  142. package/dist/core/engine/validation/schedule-cadence.js +20 -0
  143. package/dist/core/engine/validation/schedule-options.d.ts +4 -1
  144. package/dist/core/engine/validation/schedule-options.js +14 -0
  145. package/dist/core/engine/validation/schedule-revision.d.ts +24 -0
  146. package/dist/core/engine/validation/schedule-revision.js +21 -0
  147. package/dist/core/engine/validation/schedule-warnings.d.ts +11 -0
  148. package/dist/core/engine/validation/schedule-warnings.js +5 -0
  149. package/dist/core/engine/validation/schedule.d.ts +29 -4
  150. package/dist/core/engine/validation/schedule.js +19 -36
  151. package/dist/core/engine/validation.js +13 -17
  152. package/dist/core/engine/workflow-claim-reclaim-target.d.ts +1 -1
  153. package/dist/core/engine/workflow-claim-reclaim-target.js +22 -8
  154. package/dist/core/engine/workflow-claim-registry.d.ts +79 -75
  155. package/dist/core/engine/workflow-claim-registry.js +15 -2
  156. package/dist/core/engine/workflow-generation-fence.d.ts +74 -0
  157. package/dist/core/engine/workflow-generation-fence.js +19 -0
  158. package/dist/core/engine/workflow-retention-deadline.d.ts +16 -0
  159. package/dist/core/engine/workflow-retention-deadline.js +33 -0
  160. package/dist/core/events/event-map.d.ts +5 -0
  161. package/dist/core/events/index.d.ts +1 -0
  162. package/dist/core/events/index.js +1 -0
  163. package/dist/core/events/workflow-source-events.d.ts +102 -0
  164. package/dist/core/events/workflow-source-events.js +57 -0
  165. package/dist/core/execution-strategy.d.ts +7 -0
  166. package/dist/core/inline-execution-strategy.context-options.d.ts +1 -1
  167. package/dist/core/inline-execution-strategy.js +1 -1
  168. package/dist/core/start-workflow-validation.d.ts +15 -0
  169. package/dist/core/start-workflow-validation.js +11 -1
  170. package/dist/core/types/checkpoint.d.ts +14 -0
  171. package/dist/core/types/schedules.d.ts +52 -1
  172. package/dist/core/types/state.d.ts +16 -0
  173. package/dist/core/weft-error.d.ts +1 -1
  174. package/dist/core/weft-error.js +4 -1
  175. package/dist/core/worker-execution-strategy.d.ts +1 -0
  176. package/dist/core/worker-execution-strategy.js +13 -1
  177. package/dist/core/worker-fault-handling.d.ts +10 -0
  178. package/dist/core/worker-fault-handling.js +1 -0
  179. package/dist/core/worker-inbound-message.d.ts +1 -0
  180. package/dist/core/worker-inbound-message.js +1 -0
  181. package/dist/core/worker-protocol-guard.js +4 -0
  182. package/dist/core/worker-protocol.d.ts +7 -0
  183. package/dist/core/worker-protocol.js +2 -1
  184. package/dist/core/worker-turn-watchdog.d.ts +8 -1
  185. package/dist/core/worker-turn-watchdog.js +3 -2
  186. package/dist/core/workflow-identifiers.d.ts +37 -0
  187. package/dist/core/workflow-identifiers.js +19 -1
  188. package/dist/diagnostics/format.js +5 -0
  189. package/dist/diagnostics/types.d.ts +24 -0
  190. package/dist/diagnostics/version-check.js +8 -2
  191. package/dist/http.js +2 -2
  192. package/dist/index.d.ts +3 -3
  193. package/dist/index.js +7 -0
  194. package/dist/indexeddb.js +1 -1
  195. package/dist/json-schema.js +3 -3
  196. package/dist/server/fault-to-json-rpc.js +2 -1
  197. package/dist/server/fleet-event-feed.js +6 -6
  198. package/dist/server/index.d.ts +11 -0
  199. package/dist/server/operation-fault.d.ts +2 -0
  200. package/dist/server/operation-fault.js +2 -1
  201. package/dist/server/operations/create-schedule.d.ts +2 -0
  202. package/dist/server/operations/create-schedule.js +10 -6
  203. package/dist/server/operations/get-catalog-diagnostics.d.ts +28 -0
  204. package/dist/server/operations/get-catalog-diagnostics.js +10 -2
  205. package/dist/server/operations/get-task-detail-schema.d.ts +8 -0
  206. package/dist/server/operations/get-task-detail-schema.js +1 -0
  207. package/dist/server/operations/get-task-detail.d.ts +8 -0
  208. package/dist/server/operations/get-task-detail.js +1 -0
  209. package/dist/server/operations/get-task-diagnostics.d.ts +4 -4
  210. package/dist/server/operations/preload-workflow-revision.d.ts +28 -0
  211. package/dist/server/operations/preload-workflow-revision.js +60 -0
  212. package/dist/server/operations/schedule-faults.d.ts +1 -0
  213. package/dist/server/operations/schedule-faults.js +32 -4
  214. package/dist/server/operations/schedule-rest-body.d.ts +1 -0
  215. package/dist/server/operations/schedule-rest-body.js +2 -1
  216. package/dist/server/operations/static-registrations.js +6 -0
  217. package/dist/server/operations/update-schedule.d.ts +2 -0
  218. package/dist/server/operations/update-schedule.js +4 -2
  219. package/dist/server/operations/workflow-catalog-operation-helpers.js +27 -0
  220. package/dist/server/rest-binding.d.ts +11 -0
  221. package/dist/server/runtime/task-dispatch-envelope.d.ts +41 -0
  222. package/dist/server/runtime/task-dispatch-envelope.js +54 -0
  223. package/dist/server/runtime/task-dispatch-revision.d.ts +29 -0
  224. package/dist/server/runtime/task-dispatch-revision.js +13 -0
  225. package/dist/server/runtime/task-dispatch.d.ts +3 -1
  226. package/dist/server/runtime/task-dispatch.js +20 -44
  227. package/dist/server/runtime/task-ledger-recovery.js +1 -1
  228. package/dist/server/runtime/task-ledger-runtime.d.ts +14 -2
  229. package/dist/server/runtime/task-ledger-runtime.js +5 -3
  230. package/dist/server/runtime/task-polling.js +9 -3
  231. package/dist/server/runtime/task-reconciliation.js +2 -1
  232. package/dist/server/runtime/websocket-worker.js +9 -0
  233. package/dist/server/task-ledger-codec.d.ts +28 -0
  234. package/dist/server/task-ledger-codec.js +7 -1
  235. package/dist/server/task-ledger-transition-helpers.js +1 -0
  236. package/dist/server/task-ledger-types.d.ts +9 -0
  237. package/dist/server/task-ledger.d.ts +1 -1
  238. package/dist/server/task-ledger.js +3 -1
  239. package/dist/server/task-queue-types.d.ts +2 -0
  240. package/dist/storage/bun-sql.js +135 -100
  241. package/dist/storage/catalog-keys.d.ts +19 -0
  242. package/dist/storage/catalog-keys.js +3 -1
  243. package/dist/storage/compressed-storage.js +1 -1
  244. package/dist/storage/generation-keys.d.ts +52 -0
  245. package/dist/storage/generation-keys.js +4 -0
  246. package/dist/storage/index.d.ts +76 -63
  247. package/dist/storage/interface.d.ts +24 -230
  248. package/dist/storage/interface.js +1 -1
  249. package/dist/storage/key-prefixes.d.ts +1 -1
  250. package/dist/storage/key-prefixes.js +2 -0
  251. package/dist/storage/lease-keys.d.ts +48 -0
  252. package/dist/storage/lease-keys.js +8 -0
  253. package/dist/storage/lmdb.d.ts +30 -2
  254. package/dist/storage/lmdb.js +1 -1
  255. package/dist/storage/memory.js +1 -1
  256. package/dist/storage/neon.js +3 -3
  257. package/dist/storage/node-sqlite.js +135 -100
  258. package/dist/storage/postgres.js +3 -3
  259. package/dist/storage/resolve.js +1 -1
  260. package/dist/storage/scoped-storage.js +1 -1
  261. package/dist/storage/signal-keys.d.ts +23 -0
  262. package/dist/storage/signal-keys.js +9 -0
  263. package/dist/storage/testing.js +1 -1
  264. package/dist/storage/turso.js +2 -2
  265. package/dist/storage/workflow-lifecycle-keys.d.ts +128 -0
  266. package/dist/storage/workflow-lifecycle-keys.js +46 -0
  267. package/dist/storage/workflow-record-keys.d.ts +98 -0
  268. package/dist/storage/workflow-record-keys.js +47 -0
  269. package/dist/version.d.ts +1 -1
  270. package/dist/version.js +1 -1
  271. package/dist/web-extension.js +1 -1
  272. package/dist/worker/protocol-messages.d.ts +17 -0
  273. package/dist/worker/protocol-schemas.d.ts +32 -0
  274. package/dist/worker/protocol-schemas.js +7 -3
  275. package/dist/worker/protocol-task-result.js +35 -3
  276. package/dist/worker/protocol.js +1 -1
  277. package/dist/worker/registry/types.d.ts +2 -0
  278. package/dist/worker/registry.d.ts +1 -1
  279. package/dist/worker/registry.js +3 -1
  280. package/dist/workers/workflow-runner.d.ts +9 -0
  281. package/dist/workers/workflow-runner.js +9 -2
  282. package/dist/workers/workflow-worker-entry.js +9 -4
  283. package/package.json +2 -2
@@ -1,13 +1,60 @@
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 { 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>;
10
- 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;
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,
23
+ /**
24
+ * Internal-only, never part of the public `StartOptions` type (WFT-95).
25
+ * When truthy, `options.id` skips strict fresh-admission validation
26
+ * (`assertValidWorkflowId`'s `.`/`..` rejection) and is instead validated
27
+ * with the decode-compatible `assertDecodableWorkflowId`. This exists
28
+ * ONLY to replay an id that was already durably accepted before strict
29
+ * admission existed — never to let a genuinely fresh caller admit `.`/`..`.
30
+ *
31
+ * Set from exactly three internal call sites, all replaying an
32
+ * already-persisted id rather than admitting a new one:
33
+ * - `drainQueuedScheduleRun()` (via `ScheduledRunStartOptions.skipAdmissionIdCheck`,
34
+ * threaded through `startScheduledRun()`), which restarts a schedule's
35
+ * persisted `queuedRuns[].workflowId` — safe unconditionally (`true`),
36
+ * since a queued run created after this fix was already validated as
37
+ * non-`.`/`..` at schedule-admission time, so relaxing the check here
38
+ * is a no-op for it and only matters for a historical pre-WFT-95 queued run.
39
+ * - `retryFailedWorkflow()`'s checkpoint-absent fallback (`bulk-operations-retry.ts`),
40
+ * which rebuilds an already-persisted, already-validated-at-the-time
41
+ * `workflowId` from its stored input via `onTerminalConflict: 'start-new'`.
42
+ * Passes the literal `'bulk-retry-only'` rather than `true` — see
43
+ * {@link enforceReplayOnlyIdFence}'s doc comment for the TOCTOU race that
44
+ * value fences: the retry's own confirmation read and
45
+ * `resolveTerminalConflictForRestart`'s atomic read are not the same
46
+ * read, so a concurrent purge under `ownership: 'workflow-lease'` can
47
+ * leave nothing to purge-and-replace by the time this runs.
48
+ * - `dispatchChildWorkflowStart()`'s crash-reattach retry, which passes
49
+ * the literal `'reattach-only'` rather than `true` — see
50
+ * {@link enforceReplayOnlyIdFence}'s doc comment for the same TOCTOU
51
+ * race (this one only *speculatively* matched an existing record
52
+ * before this call, unlike the schedule-drain site above).
53
+ *
54
+ * Every other caller (REST, JSON-RPC, direct `engine.start()`,
55
+ * `engine.startOrSignal()`, a fresh `ctx.startChild()`) omits this
56
+ * parameter and gets the strict check, because it is not part of
57
+ * `StartOptions`/`StartWorkflowOptions` and therefore cannot be set from
58
+ * any public surface.
59
+ */
60
+ skipAdmissionIdCheck?: boolean | 'reattach-only' | 'bulk-retry-only'): Promise<WorkflowHandle>;
@@ -1,18 +1,17 @@
1
- import { createCheckpoint } from "../../checkpoint.js";
2
1
  import { assertPayloadWithinLimit } from "../../payload-size.js";
3
- import { normalizeStorageTimestamp } from "../../scheduler.js";
4
2
  import {
5
- StartWorkflowValidationError,
6
- assertExclusiveStartWorkflowOptions,
7
3
  assertValidOnTerminalConflict,
8
- coerceStartWorkflowId,
9
- coerceStartWorkflowTimestamp,
10
- parseStartWorkflowDuration
4
+ coerceReplayWorkflowId,
5
+ coerceStartWorkflowId
11
6
  } from "../../start-workflow-validation.js";
12
- import { releaseInFlightStart, reserveInFlightStart } from "../catalog-removal.js";
7
+ import {
8
+ releaseInFlightStart,
9
+ resolveAndReserveExecutableRegistration
10
+ } from "../catalog-removal.js";
13
11
  import { forgetCommittedCheckpointBytes } from "../checkpoint-commit-snapshots.js";
14
- import { WorkflowAlreadyExistsError, WorkflowNotRegisteredError } from "../errors.js";
12
+ import { WorkflowAlreadyExistsError } from "../errors.js";
15
13
  import { createDelayedStartTimerEntry } from "../operations-time.js";
14
+ import { resolveAndReservePinnedExecutableRegistration } from "../pinned-schedule-revision.js";
16
15
  import { selectPersistedWorkflowStartHeaders } from "../state-utilities.js";
17
16
  import { buildWorkflowConcurrencyStartOperations } from "../workflow-concurrency.js";
18
17
  import { createWorkflowVersionTuple } from "./persist.js";
@@ -28,14 +27,27 @@ import {
28
27
  runWorkflowStartInterceptor
29
28
  } from "./start-exec.js";
30
29
  import {
30
+ resolveCachedStartRevision,
31
+ resolveStartRevisionUncached
32
+ } from "./start-revision-resolution.js";
33
+ import { resolveScheduledStartAt } from "./start-schedule-timing.js";
34
+ import {
35
+ applyRestartLineage,
36
+ createInitialCheckpoint,
37
+ createInitialWorkflowState,
38
+ parseStartOptionDuration
39
+ } from "./start-state.js";
40
+ import {
41
+ enforceReplayOnlyIdFence,
42
+ GENERATED_ID_START_DECISION,
31
43
  prepareTerminalRunPurge,
32
44
  resolveTerminalConflictForRestart
33
45
  } from "./start-terminal-conflict-purge.js";
34
46
  export async function start(internals, type, input, options, callbacks) {
35
47
  return startWorkflow(internals, type, input, options, void 0, callbacks);
36
48
  }
37
- function prepareStartWorkflow(internals, options, callbacks) {
38
- const callerProvidedId = options?.id !== void 0, workflowId = options?.id !== void 0 ? coerceStartWorkflowId(options.id, "options.id") : crypto.randomUUID(), parentHeaders = internals.pendingParentHeaders;
49
+ function prepareStartWorkflow(internals, options, callbacks, skipAdmissionIdCheck) {
50
+ const callerProvidedId = options?.id !== void 0, workflowId = options?.id !== void 0 ? skipAdmissionIdCheck ? coerceReplayWorkflowId(options.id, "options.id") : coerceStartWorkflowId(options.id, "options.id") : crypto.randomUUID(), parentHeaders = internals.pendingParentHeaders;
39
51
  internals.pendingParentHeaders = void 0;
40
52
  const pendingExecutionStateOwnerId = internals.pendingExecutionStateOwnerId, executionStateOwnerId = pendingExecutionStateOwnerId === null ? void 0 : pendingExecutionStateOwnerId ?? workflowId;
41
53
  internals.pendingExecutionStateOwnerId = void 0;
@@ -70,14 +82,19 @@ function assertServicesSupportedForMode(internals, options) {
70
82
  if (options?.services !== void 0 && internals.inlineStrategy === null)
71
83
  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
84
  }
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;
85
+ function resolveAndReserveStartRegistration(internals, type, revisionOverride, callbacks) {
86
+ if (revisionOverride === void 0)
87
+ return resolveAndReserveExecutableRegistration(internals, type, callbacks.resolveExecutableRegistration);
88
+ return resolveAndReservePinnedExecutableRegistration(internals.engine, internals, type, revisionOverride).then(({ registration, inFlightRevision }) => ({
89
+ registration,
90
+ inFlightRevision,
91
+ resolvedRevision: revisionOverride
92
+ }));
93
+ }
94
+ export async function startWorkflow(internals, type, input, options, additionalStartOperations, callbacks, buildIdempotentStartOperations, revisionOverride, skipAdmissionIdCheck) {
78
95
  assertServicesSupportedForMode(internals, options);
79
96
  assertValidOnTerminalConflict(options);
80
- const preparation = prepareStartWorkflow(internals, options, callbacks), {
97
+ const preparation = prepareStartWorkflow(internals, options, callbacks, Boolean(skipAdmissionIdCheck)), {
81
98
  workflowId,
82
99
  callerProvidedId,
83
100
  parentHeaders,
@@ -90,14 +107,25 @@ export async function startWorkflow(internals, type, input, options, additionalS
90
107
  if (internals.pendingStarts.has(workflowId))
91
108
  throw new WorkflowAlreadyExistsError(workflowId);
92
109
  internals.pendingStarts.add(workflowId);
93
- let startSucceeded = !1;
94
- const inFlightRevision = reserveInFlightStart(internals, type);
110
+ let startSucceeded = !1, inFlightRevision;
95
111
  try {
96
- const terminalRunToPurge = callerProvidedId ? await resolveTerminalConflictForRestart(internals, workflowId, options) : null;
97
112
  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);
113
+ const {
114
+ registration,
115
+ inFlightRevision: reservedRevision,
116
+ resolvedRevision
117
+ } = await resolveAndReserveStartRegistration(internals, type, revisionOverride, callbacks);
118
+ inFlightRevision = reservedRevision;
119
+ const workflowConcurrency = registration.concurrency, revision = resolveCachedStartRevision(internals, type, resolvedRevision) ?? await resolveStartRevisionUncached(internals, type), {
120
+ terminalRunToPurge,
121
+ duplicateIdCondition,
122
+ duplicateIdGenerationCondition,
123
+ observedGenerationBytes
124
+ } = callerProvidedId ? await resolveTerminalConflictForRestart(internals, workflowId, options) : GENERATED_ID_START_DECISION;
125
+ enforceReplayOnlyIdFence(skipAdmissionIdCheck, workflowId, terminalRunToPurge);
126
+ const versionTuple = createWorkflowVersionTuple(internals, registration, callbacks), state = createInitialWorkflowState(internals, workflowId, type, input, versionTuple, revision, options, preparation.normalizedTags, executionStateOwnerId, parentWorkflowId, parentWorkflowExecutionToken, delayedStartTimer, callbacks);
99
127
  applyRestartLineage(state, terminalRunToPurge);
100
- 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;
128
+ 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, observedGenerationBytes) : void 0;
101
129
  internals.checkpoints.set(workflowId, checkpoint);
102
130
  setWorkflowStartHeaders(internals, workflowId, workflowStartHeaders, callbacks);
103
131
  internals.workflowVersionTuples.set(workflowId, versionTuple);
@@ -113,7 +141,9 @@ export async function startWorkflow(internals, type, input, options, additionalS
113
141
  additionalStartOperations,
114
142
  buildWorkflowConcurrencyStartOperations: workflowConcurrency === void 0 ? void 0 : () => buildWorkflowConcurrencyStartOperations(internals, type, workflowId, input, workflowConcurrency),
115
143
  callbacks,
116
- purgeDeleteOperations
144
+ purgeDeleteOperations,
145
+ duplicateIdCondition,
146
+ duplicateIdGenerationCondition
117
147
  }, buildIdempotentStartOperations);
118
148
  if (options?.services !== void 0) {
119
149
  internals.workflowServices.set(workflowId, options.services);
@@ -140,70 +170,3 @@ export async function startWorkflow(internals, type, input, options, additionalS
140
170
  rollbackTransientStartState(internals, workflowId);
141
171
  }
142
172
  }
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
- export function resolveScheduledStartAt(internals, options, submissionTime, callbacks) {
155
- assertExclusiveStartWorkflowOptions(options?.startAt, options?.startAfter);
156
- if (options?.startAt !== void 0)
157
- return coerceStartWorkflowTimestamp(options.startAt, "options.startAt");
158
- if (options?.startAfter !== void 0) {
159
- const startAfterMilliseconds = parseStartOptionDuration(internals, options.startAfter, "options.startAfter", callbacks);
160
- try {
161
- return normalizeStorageTimestamp(submissionTime + startAfterMilliseconds, "options.startAfter");
162
- } catch {
163
- throw new StartWorkflowValidationError("options.startAfter must resolve to a finite, non-negative start time");
164
- }
165
- }
166
- return;
167
- }
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,8 @@
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 FreshResumeClaimTracker } from './resume.ts';
5
+ import { type LifecycleCallbacks, type RecoverAllOptions } from './shared.ts';
5
6
  export declare function recoverAll(internals: EngineInternals, callbacks: LifecycleCallbacks, options?: RecoverAllOptions): Promise<WorkflowHandle[]>;
6
7
  /** Options for {@link resume}. */
7
8
  export type ResumeOptions = {
@@ -16,7 +17,14 @@ export type ResumeOptions = {
16
17
  * `engine.resume()` leaves it `false` — nothing was deposed there.
17
18
  */
18
19
  readonly forceReplayFromStorage?: boolean;
20
+ /**
21
+ * Forwarded to `resumeWorkflowFromStorage()`'s matching options — see
22
+ * `ResumeFromStorageOptions` (WFT-134 issue D). Set by
23
+ * `recoverEntryOrIsolateFailure` only; unset for the local-ownership fast
24
+ * path above, which never reaches that function.
25
+ */
26
+ readonly deferClaimReleaseOnRejection?: boolean;
27
+ readonly freshClaimTracker?: FreshResumeClaimTracker;
19
28
  };
20
29
  export declare function resume(internals: EngineInternals, workflowId: string, callbacks: LifecycleCallbacks, onRecoveredWorkflow?: RecoverAllOptions['onRecoveredWorkflow'], options?: ResumeOptions): Promise<WorkflowHandle>;
21
30
  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,35 +1,36 @@
1
1
  import { KEYS } from "../../../storage/interface.js";
2
2
  import { deserializeCheckpoint, serializeCheckpoint } from "../../checkpoint.js";
3
- import { RegExpExtensionDecodeError } from "../../codec/extension-codec.js";
4
- import { Context, setContextWorkflowInterceptor } from "../../context.js";
5
3
  import { EMPTY_EVENT_HEAD } from "../../event-log.js";
6
- import { WorkflowRecoverySkippedEvent, WorkflowStartedEvent } from "../../events.js";
7
- import { VersionMismatchError } from "../../versioning.js";
8
- import { createCancelHandlerRegistration, resetCancelHandlers } from "../cancel-handlers.js";
4
+ import { WorkflowRecoverySkippedEvent } from "../../events.js";
9
5
  import { forgetCommittedCheckpointBytes } from "../checkpoint-commit-snapshots.js";
10
6
  import { hydrateCheckpointReplayState } from "../checkpoint-replay.js";
7
+ import { resolveExecutableRegistrationOrRenamedNotFound } from "../dynamic-source-execution.js";
11
8
  import { WorkflowTypeNotRegisteredForRecoveryError } from "../errors.js";
12
9
  import { commitFencedEngineWrite } from "../fenced-write.js";
13
- import { getWorkflowExecutionStartedAt } from "../handles.js";
14
- import { WorkflowClaimUnavailableError } from "../lease-errors.js";
15
10
  import { normalizeForkStep, selectPersistedWorkflowStartHeaders } from "../state-utilities.js";
16
11
  import { loadWorkflowState } from "../storage-io.js";
17
- import { getComposedWorkflowInterceptor } from "../strategy-helpers.js";
18
12
  import { decodeWorkflowState } from "../validation.js";
13
+ import { launchWorkflowFromCheckpoint } from "./checkpoint-launch.js";
19
14
  import {
20
15
  buildForkBatchOperations,
21
16
  buildForkSearchAttributes,
22
17
  createForkLineage,
23
18
  createForkedWorkflowState
24
19
  } from "./fork-helpers.js";
25
- import { createWorkflowVersionTuple, derivePreparedExecutionState } from "./persist.js";
20
+ import { derivePreparedExecutionState } from "./persist.js";
21
+ import { isolateRecoveryFailure } from "./recovery-isolation.js";
22
+ import {
23
+ buildRecoveryRevisionGroups,
24
+ classifyRevisionGroups,
25
+ createRecoveryScopedRevisionCallbacks
26
+ } from "./recovery-revision-groups.js";
26
27
  import { resumeWorkflowFromStorage } from "./resume.js";
27
28
  import {
28
- createWorkflowHandle,
29
29
  enforceHistoryPolicyBeforeReplayById,
30
30
  loadWorkflowStartHeaders,
31
31
  setWorkflowStartHeaders
32
32
  } from "./shared.js";
33
+ import { releaseFreshlyAcquiredResumeClaim } from "./standalone-claim-acquire.js";
33
34
  function isWorkflowSideRecordKey(key) {
34
35
  return key.includes(":ckpt") || key.includes(":offload") || key.includes(":archive") || key.includes(":timeline:");
35
36
  }
@@ -39,9 +40,9 @@ function classifyRecoveryState(internals, callbacks, state) {
39
40
  return { kind: "local", workflowId: state.id };
40
41
  if (state.status !== "running")
41
42
  return { kind: "ignored" };
42
- if (!internals.registrations.has(state.type))
43
+ if (!internals.registrations.has(state.type) && !internals.sources.byName.has(state.type))
43
44
  return { kind: "missing", workflow: { type: state.type, workflowId: state.id } };
44
- return { kind: "recoverable", workflowId: state.id };
45
+ return { kind: "recoverable", workflowId: state.id, type: state.type, revision: state.revision };
45
46
  }
46
47
  function appendRecoveryClassification(result, classification) {
47
48
  if (classification.kind === "ignored")
@@ -63,29 +64,32 @@ async function preflightRecoverAll(internals, callbacks) {
63
64
  return result;
64
65
  }
65
66
  async function recoverEntryOrIsolateFailure(internals, workflowId, callbacks, options) {
67
+ const freshClaimTracker = { epoch: null };
66
68
  try {
67
- return await resume(internals, workflowId, callbacks, options?.onRecoveredWorkflow);
69
+ return await resume(internals, workflowId, callbacks, options?.onRecoveredWorkflow, {
70
+ deferClaimReleaseOnRejection: !0,
71
+ freshClaimTracker
72
+ });
68
73
  } catch (error) {
69
- if (error instanceof RegExpExtensionDecodeError) {
70
- await callbacks.failWorkflowForCheckpointDecodeError(workflowId, error);
71
- return null;
72
- }
73
- if (error instanceof VersionMismatchError && options?.versionMismatchPolicy !== "throw") {
74
- await callbacks.failWorkflowForVersionMismatch(workflowId, error);
75
- return null;
74
+ try {
75
+ return await isolateRecoveryFailure(workflowId, callbacks, options, error);
76
+ } finally {
77
+ if (freshClaimTracker.epoch !== null)
78
+ await releaseFreshlyAcquiredResumeClaim(internals, workflowId, freshClaimTracker.epoch);
76
79
  }
77
- if (error instanceof WorkflowClaimUnavailableError)
78
- return null;
79
- throw error;
80
80
  }
81
81
  }
82
82
  export async function recoverAll(internals, callbacks, options) {
83
83
  const preflight = await preflightRecoverAll(internals, callbacks), handles = [];
84
84
  if (preflight.missingWorkflows.length > 0 && options?.acknowledgeUnknownWorkflowTypes !== !0)
85
85
  throw new WorkflowTypeNotRegisteredForRecoveryError({
86
- registeredTypes: internals.registrations.keys(),
86
+ registeredTypes: new Set([
87
+ ...internals.registrations.keys(),
88
+ ...internals.sources.byName.keys()
89
+ ]),
87
90
  missingWorkflows: preflight.missingWorkflows
88
91
  });
92
+ const recoverableEntries = preflight.entries.filter((entry) => entry.kind === "recoverable"), revisionGroups = buildRecoveryRevisionGroups(recoverableEntries), revisionClassifications = await classifyRevisionGroups(internals, callbacks, revisionGroups), recoveryScopedCallbacks = createRecoveryScopedRevisionCallbacks(callbacks, revisionClassifications);
89
93
  for (const entry of preflight.entries) {
90
94
  if (entry.kind === "local") {
91
95
  handles.push(callbacks.getHandle(entry.workflowId));
@@ -95,7 +99,7 @@ export async function recoverAll(internals, callbacks, options) {
95
99
  callbacks.dispatchEvent(new WorkflowRecoverySkippedEvent(entry.workflow.workflowId, entry.workflow.type, "type-not-registered"));
96
100
  continue;
97
101
  }
98
- const handle = await recoverEntryOrIsolateFailure(internals, entry.workflowId, callbacks, options);
102
+ const handle = await recoverEntryOrIsolateFailure(internals, entry.workflowId, recoveryScopedCallbacks, options);
99
103
  if (handle !== null)
100
104
  handles.push(handle);
101
105
  }
@@ -109,16 +113,20 @@ export async function resume(internals, workflowId, callbacks, onRecoveredWorkfl
109
113
  return callbacks.getHandle(workflowId);
110
114
  }
111
115
  }
112
- return resumeWorkflowFromStorage(internals, workflowId, !0, callbacks, onRecoveredWorkflow);
116
+ return resumeWorkflowFromStorage(internals, workflowId, !0, callbacks, onRecoveredWorkflow, {
117
+ ...options?.deferClaimReleaseOnRejection !== void 0 && {
118
+ deferClaimReleaseOnRejection: options.deferClaimReleaseOnRejection
119
+ },
120
+ ...options?.freshClaimTracker !== void 0 && {
121
+ freshClaimTracker: options.freshClaimTracker
122
+ }
123
+ });
113
124
  }
114
125
  export async function fork(internals, sourceWorkflowId, options, callbacks) {
115
126
  const sourceState = await loadWorkflowState(internals, sourceWorkflowId);
116
127
  if (!sourceState)
117
128
  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);
129
+ 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
130
  if (!checkpointBytes) {
123
131
  if (fromStep !== void 0)
124
132
  throw Error(`Checkpoint not found at step ${String(fromStep)} for workflow "${sourceWorkflowId}"`);
@@ -129,14 +137,14 @@ export async function fork(internals, sourceWorkflowId, options, callbacks) {
129
137
  createdAt: forkedAt,
130
138
  workflowId,
131
139
  searchAttributes: buildForkSearchAttributes(internals, preparedExecutionState.checkpoint, lineage, callbacks)
132
- }, forkState = createForkedWorkflowState(internals, workflowId, preparedExecutionState.state, preparedExecutionState.versionTuple, lineage, forkedAt, callbacks);
140
+ }, forkState = createForkedWorkflowState(internals, workflowId, preparedExecutionState.state, preparedExecutionState.versionTuple, lineage, forkedAt, callbacks, resolvedRevision);
133
141
  let forkStarted = !1;
134
142
  try {
135
143
  const forkCheckpointBytes = serializeCheckpoint(forkCheckpoint);
136
144
  await commitFencedEngineWrite(internals, workflowId, buildForkBatchOperations(internals, workflowId, forkState, forkCheckpoint, forkCheckpointBytes, persistedWorkflowStartHeaders, callbacks), [], () => Error(`Fork of workflow "${workflowId}" lost its CAS race.`));
137
145
  internals.eventLogHeads.set(workflowId, EMPTY_EVENT_HEAD);
138
146
  setWorkflowStartHeaders(internals, workflowId, persistedWorkflowStartHeaders, callbacks);
139
- const handle = launchWorkflowFromCheckpoint(internals, workflowId, forkState, forkCheckpoint, registration, callbacks);
147
+ const handle = launchWorkflowFromCheckpoint(internals, workflowId, forkState, forkCheckpoint, registration, resolvedRevision, callbacks);
140
148
  forkStarted = !0;
141
149
  return handle;
142
150
  } finally {
@@ -149,66 +157,3 @@ export async function fork(internals, sourceWorkflowId, options, callbacks) {
149
157
  }
150
158
  }
151
159
  }
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,12 @@
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 } from './lifecycle/start-schedule-timing.ts';
4
+ export { start, startWorkflow } from './lifecycle/start.ts';
5
+ export { applyRestartLineage, createInitialCheckpoint, createInitialWorkflowState, parseStartOptionDuration, } from './lifecycle/start-state.ts';
4
6
  export { startOrSignal, startWithIdempotency, type StartOrSignalCallbacks, type StartOrSignalResult, } from './lifecycle/start-or-signal.ts';
5
7
  export { buildInitialSearchAttributeOperations, buildStartBatchOperations, validateSearchAttributes, } from './lifecycle/start-batch.ts';
6
8
  export { beginWorkflowExecution, runWorkflowStartInterceptor, startWorkflowExecution, } from './lifecycle/start-exec.ts';
7
9
  export { buildForkBatchOperations, buildForkSearchAttributes, createForkLineage, createForkedWorkflowState, } from './lifecycle/fork-helpers.ts';
8
- export { fork, launchWorkflowFromCheckpoint, recoverAll, resume } from './lifecycle/transition.ts';
10
+ export { launchWorkflowFromCheckpoint } from './lifecycle/checkpoint-launch.ts';
11
+ export { fork, recoverAll, resume } from './lifecycle/transition.ts';
9
12
  export { resumeWorkflowFromStorage } from './lifecycle/resume.ts';
@@ -15,14 +15,14 @@ export {
15
15
  workflowStateWithVersionTuple,
16
16
  workflowVersionTupleFromState
17
17
  } from "./lifecycle/persist.js";
18
+ export { resolveScheduledStartAt } from "./lifecycle/start-schedule-timing.js";
19
+ export { start, startWorkflow } from "./lifecycle/start.js";
18
20
  export {
21
+ applyRestartLineage,
19
22
  createInitialCheckpoint,
20
23
  createInitialWorkflowState,
21
- parseStartOptionDuration,
22
- resolveScheduledStartAt,
23
- start,
24
- startWorkflow
25
- } from "./lifecycle/start.js";
24
+ parseStartOptionDuration
25
+ } from "./lifecycle/start-state.js";
26
26
  export {
27
27
  startOrSignal,
28
28
  startWithIdempotency
@@ -43,5 +43,6 @@ export {
43
43
  createForkLineage,
44
44
  createForkedWorkflowState
45
45
  } from "./lifecycle/fork-helpers.js";
46
- export { fork, launchWorkflowFromCheckpoint, recoverAll, resume } from "./lifecycle/transition.js";
46
+ export { launchWorkflowFromCheckpoint } from "./lifecycle/checkpoint-launch.js";
47
+ export { fork, recoverAll, resume } from "./lifecycle/transition.js";
47
48
  export { resumeWorkflowFromStorage } from "./lifecycle/resume.js";
@@ -6,6 +6,10 @@ import { buildIndexOperations, validateAttributeType } from "../search-attribute
6
6
  import { normalizeWorkflowTags } from "../workflow-tags.js";
7
7
  import { mutateWorkflowTags, validateAttributeValueSizes } from "./attributes-tags.js";
8
8
  import { CONSTRAINED_ID_CHUNK_SIZE } from "./candidate-read-batching.js";
9
+ import {
10
+ getResolvedDynamicRegistration,
11
+ resolveExecutableRegistrationForRevision
12
+ } from "./dynamic-source-execution.js";
9
13
  import { resolveListCandidateIds } from "./list-candidate-resolution.js";
10
14
  import {
11
15
  readSearchAttributesForFilter,
@@ -95,6 +99,7 @@ function summaryFromState(state, attributeFailureCategory) {
95
99
  status: state.status,
96
100
  ...state.tags !== void 0 && { tags: state.tags },
97
101
  version: state.versionTuple.workflowVersion,
102
+ ...state.revision !== void 0 && { revision: state.revision },
98
103
  createdAt: state.createdAt,
99
104
  updatedAt: state.updatedAt,
100
105
  ...state.executionDeadline !== void 0 && { executionDeadline: state.executionDeadline },
@@ -164,10 +169,19 @@ export async function getAttributes(internals, workflowId) {
164
169
  return null;
165
170
  return decoded;
166
171
  }
172
+ async function resolveAttributeSchemaRegistration(internals, state) {
173
+ const eagerOrAlreadyResolved = getResolvedDynamicRegistration(internals, state.type, state.revision);
174
+ if (eagerOrAlreadyResolved !== void 0)
175
+ return eagerOrAlreadyResolved;
176
+ if (!internals.sources.byName.has(state.type))
177
+ return;
178
+ const { entry } = await resolveExecutableRegistrationForRevision(internals.engine, internals, state.type, state.revision);
179
+ return entry;
180
+ }
167
181
  export async function setAttributes(internals, workflowId, attributes) {
168
182
  const stateBytes = await internals.storage.get(KEYS.workflow(workflowId));
169
183
  if (stateBytes) {
170
- const state = decodeWorkflowState(stateBytes), registration = internals.registrations.get(state.type);
184
+ const state = decodeWorkflowState(stateBytes), registration = await resolveAttributeSchemaRegistration(internals, state);
171
185
  if (registration?.searchAttributes) {
172
186
  const schema = registration.searchAttributes;
173
187
  for (const [key, value] of Object.entries(attributes)) {
@@ -45,7 +45,10 @@ class MemoDurableActivityScope {
45
45
  this.#activityCallbacks = activityCallbacks;
46
46
  this.#persistCheckpoint = persistCheckpoint;
47
47
  this.#identityPrefix = `memo:${operation.step}:${hashString(operation.key)}`;
48
- this.#internals.workflowTypeByWorkflowId.set(workflowId, context.workflowType);
48
+ this.#internals.workflowTypeByWorkflowId.set(workflowId, {
49
+ type: context.workflowType,
50
+ revision: this.#internals.workflowTypeByWorkflowId.get(workflowId)?.revision
51
+ });
49
52
  }
50
53
  async run(execute) {
51
54
  const cleanupAbortForwarding = this.#forwardAbortSignals();
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Count non-terminal (`running`, `pending`, or `suspended`) workflow runs
3
+ * pinned to an exact `(type, revision)` (WFT-17). Wired into
4
+ * `catalog-removal.ts`'s `countWorkflowRevisionReferences()`, replacing the
5
+ * `nonTerminalRuns: 0` stub WFT-12 left in place pending this batch (see
6
+ * that changelog entry: "stays 0 until run-level revision pinning (WFT-17)
7
+ * exists to count against").
8
+ *
9
+ * Mirrors `diagnostics/version-check.ts`'s `groupActiveWorkflowsByType()`
10
+ * bounded-scan shape: only top-level `wf:{id}` records are workflow states
11
+ * (`wf:{id}:ckpt`, `:offload`, `:archive`, `:timeline:`, and index keys all
12
+ * share the `wf:` prefix but are not).
13
+ *
14
+ * @module core/engine/nonterminal-revision-count
15
+ */
16
+ import type { Storage } from '../../storage/interface.ts';
17
+ /**
18
+ * Scan `storage` for non-terminal workflow states whose `type` and
19
+ * `revision` match exactly, returning the count. A legacy record with no
20
+ * persisted `revision` never matches (a defined `revision` argument cannot
21
+ * equal `undefined`), consistent with recovery's own "a legacy record is a
22
+ * distinct, unresolved pin" treatment — it is not silently folded into
23
+ * whichever revision happens to be active.
24
+ */
25
+ export declare function countNonTerminalRunsForRevision(storage: Storage, type: string, revision: string): Promise<number>;