@lostgradient/weft 0.23.1 → 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 (469) hide show
  1. package/README.md +72 -4
  2. package/dist/cli/codegen-emit-dedup.d.ts +81 -0
  3. package/dist/cli/codegen-emit-dedup.js +45 -0
  4. package/dist/cli/codegen-emit-registry.d.ts +90 -0
  5. package/dist/cli/codegen-emit-registry.js +50 -0
  6. package/dist/cli/codegen-emit.d.ts +26 -39
  7. package/dist/cli/codegen-emit.js +4 -27
  8. package/dist/cli/codegen-validate.d.ts +49 -0
  9. package/dist/cli/codegen-validate.js +186 -0
  10. package/dist/cli/codegen.js +6 -87
  11. package/dist/cli/conformance.js +5 -2
  12. package/dist/cli/generated/operation-catalog.snapshot.json +381 -3
  13. package/dist/cli/generated/operation-client.generated.d.ts +350 -10
  14. package/dist/cli/generated/operation-client.generated.js +14 -0
  15. package/dist/cli/output.js +1 -1
  16. package/dist/cli/parse-schedule-arguments.js +15 -3
  17. package/dist/cli/schedule.js +2 -1
  18. package/dist/cli/storage-factory.js +2 -0
  19. package/dist/cli/types.d.ts +2 -1
  20. package/dist/cli/workflow-commands.js +2 -0
  21. package/dist/cli-main.js +1 -1
  22. package/dist/client/http-client-requests.js +6 -9
  23. package/dist/client/start-body.js +1 -0
  24. package/dist/core/application-payload-digest.d.ts +42 -0
  25. package/dist/core/application-payload-digest.js +76 -0
  26. package/dist/core/application-primitive-abort.d.ts +50 -0
  27. package/dist/core/application-primitive-abort.js +40 -0
  28. package/dist/core/application-primitive-attempt-registry.d.ts +74 -0
  29. package/dist/core/application-primitive-attempt-registry.js +73 -0
  30. package/dist/core/application-primitive-codec.d.ts +36 -0
  31. package/dist/core/application-primitive-codec.js +56 -0
  32. package/dist/core/application-primitive-commit.d.ts +67 -0
  33. package/dist/core/application-primitive-commit.js +66 -0
  34. package/dist/core/application-primitive-guards.d.ts +62 -0
  35. package/dist/core/application-primitive-guards.js +89 -0
  36. package/dist/core/application-primitive-payload.d.ts +29 -0
  37. package/dist/core/application-primitive-payload.js +65 -0
  38. package/dist/core/application-primitive-timing.d.ts +18 -0
  39. package/dist/core/application-primitive-timing.js +22 -0
  40. package/dist/core/atomic-state.js +2 -0
  41. package/dist/core/catalog/codec.d.ts +40 -0
  42. package/dist/core/catalog/codec.js +45 -0
  43. package/dist/core/catalog/errors.d.ts +104 -0
  44. package/dist/core/catalog/errors.js +41 -0
  45. package/dist/core/catalog/index.d.ts +25 -0
  46. package/dist/core/catalog/index.js +22 -0
  47. package/dist/core/catalog/reference-counts.d.ts +120 -0
  48. package/dist/core/catalog/reference-counts.js +28 -0
  49. package/dist/core/catalog/removal.d.ts +105 -0
  50. package/dist/core/catalog/removal.js +30 -0
  51. package/dist/core/catalog/storage-io.d.ts +101 -0
  52. package/dist/core/catalog/storage-io.js +106 -0
  53. package/dist/core/catalog/types.d.ts +108 -0
  54. package/dist/core/catalog/types.js +0 -0
  55. package/dist/core/catalog/workflow-catalog.d.ts +165 -0
  56. package/dist/core/catalog/workflow-catalog.js +198 -0
  57. package/dist/core/compare-codepoint.d.ts +13 -0
  58. package/dist/core/compare-codepoint.js +7 -0
  59. package/dist/core/compression.js +2 -0
  60. package/dist/core/context/attributes.d.ts +1 -1
  61. package/dist/core/context/durable-operations.d.ts +2 -2
  62. package/dist/core/context/index.js +5 -3
  63. package/dist/core/context/state-namespace.d.ts +1 -1
  64. package/dist/core/contract/build.d.ts +64 -0
  65. package/dist/core/contract/build.js +99 -0
  66. package/dist/core/contract/compatibility.d.ts +187 -0
  67. package/dist/core/contract/compatibility.js +37 -0
  68. package/dist/core/contract/failure.d.ts +64 -0
  69. package/dist/core/contract/failure.js +4 -0
  70. package/dist/core/contract/hash.d.ts +92 -0
  71. package/dist/core/contract/hash.js +34 -0
  72. package/dist/core/contract/index.d.ts +21 -0
  73. package/dist/core/contract/index.js +22 -0
  74. package/dist/core/contract/limits.d.ts +74 -0
  75. package/dist/core/contract/limits.js +1 -0
  76. package/dist/core/contract/manifest-parse-schema.d.ts +37 -0
  77. package/dist/core/contract/manifest-parse-schema.js +106 -0
  78. package/dist/core/contract/manifest-parse.d.ts +76 -0
  79. package/dist/core/contract/manifest-parse.js +189 -0
  80. package/dist/core/contract/manifest.d.ts +56 -0
  81. package/dist/core/contract/manifest.js +31 -0
  82. package/dist/core/contract/normalize.d.ts +97 -0
  83. package/dist/core/contract/normalize.js +127 -0
  84. package/dist/core/contract/revision.d.ts +29 -0
  85. package/dist/core/contract/revision.js +5 -0
  86. package/dist/core/contract/types.d.ts +274 -0
  87. package/dist/core/contract/types.js +1 -0
  88. package/dist/core/engine/activity-resolution.js +17 -17
  89. package/dist/core/engine/bulk-operations-purge.js +14 -20
  90. package/dist/core/engine/bulk-operations-retry.d.ts +9 -0
  91. package/dist/core/engine/bulk-operations-retry.js +177 -0
  92. package/dist/core/engine/bulk-operations-shared.d.ts +18 -0
  93. package/dist/core/engine/bulk-operations-shared.js +14 -0
  94. package/dist/core/engine/bulk-operations.js +8 -167
  95. package/dist/core/engine/callback-creators-bundles.js +4 -2
  96. package/dist/core/engine/callback-creators-core.js +9 -1
  97. package/dist/core/engine/callback-creators-schedule.js +2 -2
  98. package/dist/core/engine/catalog-activation.d.ts +40 -0
  99. package/dist/core/engine/catalog-activation.js +17 -0
  100. package/dist/core/engine/catalog-events.d.ts +36 -0
  101. package/dist/core/engine/catalog-events.js +15 -0
  102. package/dist/core/engine/catalog-readiness.d.ts +49 -0
  103. package/dist/core/engine/catalog-readiness.js +61 -0
  104. package/dist/core/engine/catalog-removal.d.ts +236 -0
  105. package/dist/core/engine/catalog-removal.js +123 -0
  106. package/dist/core/engine/catalog-tombstone-recovery.d.ts +56 -0
  107. package/dist/core/engine/catalog-tombstone-recovery.js +39 -0
  108. package/dist/core/engine/constraints.js +5 -1
  109. package/dist/core/engine/construction.d.ts +2 -2
  110. package/dist/core/engine/construction.js +4 -2
  111. package/dist/core/engine/decode-revision.d.ts +32 -0
  112. package/dist/core/engine/decode-revision.js +9 -0
  113. package/dist/core/engine/disposal.d.ts +6 -2
  114. package/dist/core/engine/disposal.js +26 -3
  115. package/dist/core/engine/dynamic-source-errors.d.ts +81 -0
  116. package/dist/core/engine/dynamic-source-errors.js +25 -0
  117. package/dist/core/engine/dynamic-source-execution.d.ts +173 -0
  118. package/dist/core/engine/dynamic-source-execution.js +108 -0
  119. package/dist/core/engine/engine-internal-types.d.ts +16 -0
  120. package/dist/core/engine/engine-workflows-namespace.d.ts +140 -0
  121. package/dist/core/engine/engine-workflows-namespace.js +51 -0
  122. package/dist/core/engine/finalizer-metadata.d.ts +22 -0
  123. package/dist/core/engine/finalizer-metadata.js +12 -0
  124. package/dist/core/engine/handle-result.js +2 -0
  125. package/dist/core/engine/index.d.ts +91 -0
  126. package/dist/core/engine/index.js +78 -6
  127. package/dist/core/engine/inline-launch-queue.js +1 -1
  128. package/dist/core/engine/internals.d.ts +54 -15
  129. package/dist/core/engine/internals.js +6 -0
  130. package/dist/core/engine/lifecycle/checkpoint-launch.d.ts +29 -0
  131. package/dist/core/engine/lifecycle/checkpoint-launch.js +75 -0
  132. package/dist/core/engine/lifecycle/delayed-start-registration.d.ts +30 -0
  133. package/dist/core/engine/lifecycle/delayed-start-registration.js +11 -0
  134. package/dist/core/engine/lifecycle/fork-helpers.d.ts +33 -1
  135. package/dist/core/engine/lifecycle/fork-helpers.js +3 -1
  136. package/dist/core/engine/lifecycle/recovery-revision-groups.d.ts +75 -0
  137. package/dist/core/engine/lifecycle/recovery-revision-groups.js +64 -0
  138. package/dist/core/engine/lifecycle/resume-generation-guard.d.ts +58 -0
  139. package/dist/core/engine/lifecycle/resume-generation-guard.js +11 -0
  140. package/dist/core/engine/lifecycle/resume.js +18 -9
  141. package/dist/core/engine/lifecycle/shared.d.ts +43 -0
  142. package/dist/core/engine/lifecycle/standalone-claim-acquire.d.ts +12 -0
  143. package/dist/core/engine/lifecycle/standalone-claim-acquire.js +6 -0
  144. package/dist/core/engine/lifecycle/start-commit-errors.d.ts +12 -0
  145. package/dist/core/engine/lifecycle/start-commit-errors.js +6 -0
  146. package/dist/core/engine/lifecycle/start-commit.d.ts +22 -7
  147. package/dist/core/engine/lifecycle/start-commit.js +49 -15
  148. package/dist/core/engine/lifecycle/start-exec.d.ts +18 -2
  149. package/dist/core/engine/lifecycle/start-exec.js +7 -5
  150. package/dist/core/engine/lifecycle/start-or-signal-create.js +1 -3
  151. package/dist/core/engine/lifecycle/start-or-signal.js +1 -1
  152. package/dist/core/engine/lifecycle/start-precondition-attribution.d.ts +49 -0
  153. package/dist/core/engine/lifecycle/start-precondition-attribution.js +13 -0
  154. package/dist/core/engine/lifecycle/start-revision-resolution.d.ts +34 -0
  155. package/dist/core/engine/lifecycle/start-revision-resolution.js +11 -0
  156. package/dist/core/engine/lifecycle/start-state.d.ts +16 -0
  157. package/dist/core/engine/lifecycle/start-state.js +60 -0
  158. package/dist/core/engine/lifecycle/start-terminal-conflict-purge.d.ts +66 -3
  159. package/dist/core/engine/lifecycle/start-terminal-conflict-purge.js +13 -3
  160. package/dist/core/engine/lifecycle/start.d.ts +16 -6
  161. package/dist/core/engine/lifecycle/start.js +39 -66
  162. package/dist/core/engine/lifecycle/transition.d.ts +3 -4
  163. package/dist/core/engine/lifecycle/transition.js +30 -80
  164. package/dist/core/engine/lifecycle.d.ts +4 -2
  165. package/dist/core/engine/lifecycle.js +6 -6
  166. package/dist/core/engine/listing.js +15 -1
  167. package/dist/core/engine/memo-durable-activity.js +4 -1
  168. package/dist/core/engine/nonterminal-revision-count.d.ts +25 -0
  169. package/dist/core/engine/nonterminal-revision-count.js +16 -0
  170. package/dist/core/engine/operations-time.d.ts +5 -23
  171. package/dist/core/engine/operations-time.js +13 -11
  172. package/dist/core/engine/ownership-bootstrap.d.ts +1 -1
  173. package/dist/core/engine/pinned-schedule-revision-count.d.ts +26 -0
  174. package/dist/core/engine/pinned-schedule-revision-count.js +19 -0
  175. package/dist/core/engine/pinned-schedule-revision.d.ts +76 -0
  176. package/dist/core/engine/pinned-schedule-revision.js +53 -0
  177. package/dist/core/engine/registration.d.ts +106 -0
  178. package/dist/core/engine/registration.js +28 -7
  179. package/dist/core/engine/retention.js +9 -2
  180. package/dist/core/engine/revision-errors.d.ts +71 -0
  181. package/dist/core/engine/revision-errors.js +13 -0
  182. package/dist/core/engine/schedule-revision-fire.d.ts +17 -0
  183. package/dist/core/engine/schedule-revision-fire.js +3 -0
  184. package/dist/core/engine/schedule-run.js +2 -1
  185. package/dist/core/engine/schedules.d.ts +2 -1
  186. package/dist/core/engine/schedules.js +28 -9
  187. package/dist/core/engine/source-diagnostics.d.ts +76 -0
  188. package/dist/core/engine/source-diagnostics.js +105 -0
  189. package/dist/core/engine/source-registration.d.ts +42 -0
  190. package/dist/core/engine/source-registration.js +38 -0
  191. package/dist/core/engine/source-resolution.d.ts +101 -0
  192. package/dist/core/engine/source-resolution.js +154 -0
  193. package/dist/core/engine/source-runtime-state.d.ts +92 -0
  194. package/dist/core/engine/source-runtime-state.js +36 -0
  195. package/dist/core/engine/storage-io.d.ts +19 -2
  196. package/dist/core/engine/storage-io.js +21 -2
  197. package/dist/core/engine/termination/finalizer-registration.d.ts +18 -0
  198. package/dist/core/engine/termination/finalizer-registration.js +21 -0
  199. package/dist/core/engine/termination/finalizer.d.ts +16 -19
  200. package/dist/core/engine/termination/finalizer.js +3 -3
  201. package/dist/core/engine/time-operation-callbacks.d.ts +32 -0
  202. package/dist/core/engine/time-operation-callbacks.js +0 -0
  203. package/dist/core/engine/validation/schedule-cadence.d.ts +16 -0
  204. package/dist/core/engine/validation/schedule-cadence.js +20 -0
  205. package/dist/core/engine/validation/schedule-options.d.ts +4 -1
  206. package/dist/core/engine/validation/schedule-options.js +14 -0
  207. package/dist/core/engine/validation/schedule-revision.d.ts +24 -0
  208. package/dist/core/engine/validation/schedule-revision.js +21 -0
  209. package/dist/core/engine/validation/schedule-warnings.d.ts +11 -0
  210. package/dist/core/engine/validation/schedule-warnings.js +5 -0
  211. package/dist/core/engine/validation/schedule.d.ts +4 -3
  212. package/dist/core/engine/validation/schedule.js +15 -26
  213. package/dist/core/engine/validation.js +5 -2
  214. package/dist/core/engine/workflow-claim-reclaim-target.d.ts +1 -1
  215. package/dist/core/engine/workflow-claim-reclaim-target.js +16 -2
  216. package/dist/core/engine/workflow-retention-deadline.d.ts +16 -0
  217. package/dist/core/engine/workflow-retention-deadline.js +33 -0
  218. package/dist/core/events/catalog-events.d.ts +117 -0
  219. package/dist/core/events/catalog-events.js +62 -0
  220. package/dist/core/events/event-map.d.ts +11 -0
  221. package/dist/core/events/index.d.ts +2 -0
  222. package/dist/core/events/index.js +2 -0
  223. package/dist/core/events/workflow-source-events.d.ts +102 -0
  224. package/dist/core/events/workflow-source-events.js +57 -0
  225. package/dist/core/execution-strategy.d.ts +7 -0
  226. package/dist/core/inline-execution-strategy.context-options.d.ts +1 -1
  227. package/dist/core/inline-execution-strategy.js +1 -1
  228. package/dist/core/mailbox-admission.d.ts +26 -0
  229. package/dist/core/mailbox-admission.js +130 -0
  230. package/dist/core/mailbox-claims.d.ts +251 -0
  231. package/dist/core/mailbox-claims.js +0 -0
  232. package/dist/core/mailbox-codec.d.ts +32 -0
  233. package/dist/core/mailbox-codec.js +221 -0
  234. package/dist/core/mailbox-contract.d.ts +290 -0
  235. package/dist/core/mailbox-contract.js +0 -0
  236. package/dist/core/mailbox-delivery.d.ts +37 -0
  237. package/dist/core/mailbox-delivery.js +193 -0
  238. package/dist/core/mailbox-guards.d.ts +78 -0
  239. package/dist/core/mailbox-guards.js +58 -0
  240. package/dist/core/mailbox-index-codec.d.ts +41 -0
  241. package/dist/core/mailbox-index-codec.js +71 -0
  242. package/dist/core/mailbox-internals.d.ts +156 -0
  243. package/dist/core/mailbox-internals.js +154 -0
  244. package/dist/core/mailbox-maintenance.d.ts +29 -0
  245. package/dist/core/mailbox-maintenance.js +196 -0
  246. package/dist/core/mailbox-settlement.d.ts +69 -0
  247. package/dist/core/mailbox-settlement.js +206 -0
  248. package/dist/core/mailbox-storage.d.ts +117 -0
  249. package/dist/core/mailbox-storage.js +115 -0
  250. package/dist/core/mailbox-transition-helpers.d.ts +70 -0
  251. package/dist/core/mailbox-transition-helpers.js +43 -0
  252. package/dist/core/mailbox-transitions-recovery.d.ts +28 -0
  253. package/dist/core/mailbox-transitions-recovery.js +64 -0
  254. package/dist/core/mailbox-transitions.d.ts +138 -0
  255. package/dist/core/mailbox-transitions.js +180 -0
  256. package/dist/core/mailbox-types.d.ts +406 -0
  257. package/dist/core/mailbox-types.js +16 -0
  258. package/dist/core/mailbox-validation.d.ts +65 -0
  259. package/dist/core/mailbox-validation.js +73 -0
  260. package/dist/core/mailbox-waits.d.ts +50 -0
  261. package/dist/core/mailbox-waits.js +97 -0
  262. package/dist/core/mailbox.d.ts +163 -0
  263. package/dist/core/mailbox.js +211 -0
  264. package/dist/core/outbox-claims.d.ts +307 -0
  265. package/dist/core/outbox-claims.js +0 -0
  266. package/dist/core/outbox-codec.d.ts +24 -0
  267. package/dist/core/outbox-codec.js +257 -0
  268. package/dist/core/outbox-contract.d.ts +346 -0
  269. package/dist/core/outbox-contract.js +0 -0
  270. package/dist/core/outbox-delivery.d.ts +34 -0
  271. package/dist/core/outbox-delivery.js +149 -0
  272. package/dist/core/outbox-drain.d.ts +29 -0
  273. package/dist/core/outbox-drain.js +158 -0
  274. package/dist/core/outbox-enqueue.d.ts +24 -0
  275. package/dist/core/outbox-enqueue.js +115 -0
  276. package/dist/core/outbox-guards.d.ts +76 -0
  277. package/dist/core/outbox-guards.js +60 -0
  278. package/dist/core/outbox-index-codec.d.ts +33 -0
  279. package/dist/core/outbox-index-codec.js +71 -0
  280. package/dist/core/outbox-internals.d.ts +99 -0
  281. package/dist/core/outbox-internals.js +159 -0
  282. package/dist/core/outbox-listing.d.ts +12 -0
  283. package/dist/core/outbox-listing.js +38 -0
  284. package/dist/core/outbox-maintenance.d.ts +28 -0
  285. package/dist/core/outbox-maintenance.js +191 -0
  286. package/dist/core/outbox-operations.d.ts +18 -0
  287. package/dist/core/outbox-operations.js +46 -0
  288. package/dist/core/outbox-runner.d.ts +30 -0
  289. package/dist/core/outbox-runner.js +217 -0
  290. package/dist/core/outbox-settlement.d.ts +57 -0
  291. package/dist/core/outbox-settlement.js +183 -0
  292. package/dist/core/outbox-storage.d.ts +107 -0
  293. package/dist/core/outbox-storage.js +127 -0
  294. package/dist/core/outbox-transition-helpers.d.ts +83 -0
  295. package/dist/core/outbox-transition-helpers.js +63 -0
  296. package/dist/core/outbox-transitions-recovery.d.ts +30 -0
  297. package/dist/core/outbox-transitions-recovery.js +42 -0
  298. package/dist/core/outbox-transitions.d.ts +145 -0
  299. package/dist/core/outbox-transitions.js +236 -0
  300. package/dist/core/outbox-types.d.ts +400 -0
  301. package/dist/core/outbox-types.js +20 -0
  302. package/dist/core/outbox-validation.d.ts +101 -0
  303. package/dist/core/outbox-validation.js +127 -0
  304. package/dist/core/outbox-waits.d.ts +39 -0
  305. package/dist/core/outbox-waits.js +88 -0
  306. package/dist/core/outbox.d.ts +156 -0
  307. package/dist/core/outbox.js +234 -0
  308. package/dist/core/registry-limits.d.ts +49 -0
  309. package/dist/core/registry-limits.js +10 -0
  310. package/dist/core/registry-schema-conversion.d.ts +20 -0
  311. package/dist/core/registry-schema-conversion.js +22 -0
  312. package/dist/core/registry-snapshot.d.ts +91 -42
  313. package/dist/core/registry-snapshot.js +39 -77
  314. package/dist/core/registry-workflow-contract-draft.d.ts +32 -0
  315. package/dist/core/registry-workflow-contract-draft.js +28 -0
  316. package/dist/core/registry-workflow-manifest.d.ts +122 -0
  317. package/dist/core/registry-workflow-manifest.js +92 -0
  318. package/dist/core/source/errors.d.ts +55 -0
  319. package/dist/core/source/errors.js +13 -0
  320. package/dist/core/source/index.d.ts +19 -0
  321. package/dist/core/source/index.js +4 -0
  322. package/dist/core/source/resolvers.d.ts +45 -0
  323. package/dist/core/source/resolvers.js +9 -0
  324. package/dist/core/source/types.d.ts +211 -0
  325. package/dist/core/source/types.js +0 -0
  326. package/dist/core/source/validate.d.ts +91 -0
  327. package/dist/core/source/validate.js +62 -0
  328. package/dist/core/source/workflow-source.d.ts +43 -0
  329. package/dist/core/source/workflow-source.js +16 -0
  330. package/dist/core/types/checkpoint.d.ts +14 -0
  331. package/dist/core/types/schedules.d.ts +52 -1
  332. package/dist/core/types/state.d.ts +16 -0
  333. package/dist/core/types/workflow-registries.d.ts +7 -0
  334. package/dist/core/types/workflow-registry.d.ts +12 -0
  335. package/dist/core/versioning.js +2 -0
  336. package/dist/core/weft-error.d.ts +1 -1
  337. package/dist/core/weft-error.js +12 -1
  338. package/dist/core/worker-execution-strategy.d.ts +1 -0
  339. package/dist/core/worker-execution-strategy.js +13 -1
  340. package/dist/core/worker-fault-handling.d.ts +10 -0
  341. package/dist/core/worker-fault-handling.js +1 -0
  342. package/dist/core/worker-inbound-message.d.ts +1 -0
  343. package/dist/core/worker-inbound-message.js +1 -0
  344. package/dist/core/worker-protocol-guard.js +4 -0
  345. package/dist/core/worker-protocol.d.ts +7 -0
  346. package/dist/core/worker-protocol.js +2 -1
  347. package/dist/core/worker-turn-watchdog.d.ts +8 -1
  348. package/dist/core/worker-turn-watchdog.js +3 -2
  349. package/dist/diagnostics/format.js +5 -0
  350. package/dist/diagnostics/types.d.ts +24 -0
  351. package/dist/diagnostics/version-check.js +8 -2
  352. package/dist/http.js +2 -2
  353. package/dist/index.d.ts +25 -4
  354. package/dist/index.js +57 -0
  355. package/dist/indexeddb.js +1 -1
  356. package/dist/json-schema.js +3 -3
  357. package/dist/mcp/resources.js +2 -0
  358. package/dist/runtime/portable.d.ts +9 -1
  359. package/dist/server/fault-to-json-rpc.js +4 -1
  360. package/dist/server/handler/auth-context-principal.js +4 -0
  361. package/dist/server/index.d.ts +11 -0
  362. package/dist/server/json-rpc-http.js +2 -0
  363. package/dist/server/json-rpc-websocket.js +6 -4
  364. package/dist/server/operation-catalog/index.d.ts +1 -1
  365. package/dist/server/operation-catalog/pipeline-helpers.js +2 -0
  366. package/dist/server/operation-catalog/types.d.ts +17 -0
  367. package/dist/server/operation-fault.d.ts +5 -0
  368. package/dist/server/operation-fault.js +4 -1
  369. package/dist/server/operations/activate-workflow-revision.d.ts +30 -0
  370. package/dist/server/operations/activate-workflow-revision.js +69 -0
  371. package/dist/server/operations/create-schedule.d.ts +2 -0
  372. package/dist/server/operations/create-schedule.js +10 -6
  373. package/dist/server/operations/fleet-events-subscription.js +1 -1
  374. package/dist/server/operations/fork-workflow.js +1 -1
  375. package/dist/server/operations/get-active-workflow-revision.d.ts +32 -0
  376. package/dist/server/operations/get-active-workflow-revision.js +46 -0
  377. package/dist/server/operations/get-catalog-diagnostics.d.ts +113 -0
  378. package/dist/server/operations/get-catalog-diagnostics.js +65 -0
  379. package/dist/server/operations/get-registry.js +17 -4
  380. package/dist/server/operations/get-task-detail-schema.d.ts +8 -0
  381. package/dist/server/operations/get-task-detail-schema.js +1 -0
  382. package/dist/server/operations/get-task-detail.d.ts +8 -0
  383. package/dist/server/operations/get-task-detail.js +1 -0
  384. package/dist/server/operations/get-task-diagnostics.d.ts +4 -4
  385. package/dist/server/operations/get-workflow-revision.d.ts +25 -0
  386. package/dist/server/operations/get-workflow-revision.js +50 -0
  387. package/dist/server/operations/install-workflow-revision.d.ts +27 -0
  388. package/dist/server/operations/install-workflow-revision.js +46 -0
  389. package/dist/server/operations/list-workflow-revisions.d.ts +20 -0
  390. package/dist/server/operations/list-workflow-revisions.js +40 -0
  391. package/dist/server/operations/preload-workflow-revision.d.ts +28 -0
  392. package/dist/server/operations/preload-workflow-revision.js +60 -0
  393. package/dist/server/operations/schedule-faults.d.ts +1 -0
  394. package/dist/server/operations/schedule-faults.js +32 -4
  395. package/dist/server/operations/schedule-rest-body.d.ts +1 -0
  396. package/dist/server/operations/schedule-rest-body.js +2 -1
  397. package/dist/server/operations/start-or-signal-workflow.js +1 -1
  398. package/dist/server/operations/start-workflow.js +1 -1
  399. package/dist/server/operations/static-registrations.js +42 -0
  400. package/dist/server/operations/storage.js +2 -2
  401. package/dist/server/operations/submit-review-decision.js +1 -1
  402. package/dist/server/operations/update-schedule.d.ts +2 -0
  403. package/dist/server/operations/update-schedule.js +4 -2
  404. package/dist/server/operations/workflow-catalog-operation-helpers.d.ts +59 -0
  405. package/dist/server/operations/workflow-catalog-operation-helpers.js +162 -0
  406. package/dist/server/runtime/task-dispatch-envelope.d.ts +14 -0
  407. package/dist/server/runtime/task-dispatch-envelope.js +32 -0
  408. package/dist/server/runtime/task-dispatch-revision.d.ts +29 -0
  409. package/dist/server/runtime/task-dispatch-revision.js +13 -0
  410. package/dist/server/runtime/task-dispatch.js +12 -37
  411. package/dist/server/runtime/task-ledger-recovery.js +1 -1
  412. package/dist/server/runtime/task-ledger-runtime.d.ts +14 -2
  413. package/dist/server/runtime/task-ledger-runtime.js +5 -3
  414. package/dist/server/runtime/task-polling.js +9 -3
  415. package/dist/server/runtime/task-reconciliation.js +2 -1
  416. package/dist/server/runtime/websocket-upgrade.js +2 -0
  417. package/dist/server/runtime/websocket-worker.js +9 -0
  418. package/dist/server/task-ledger-codec.d.ts +9 -0
  419. package/dist/server/task-ledger-codec.js +4 -1
  420. package/dist/server/task-ledger-transition-helpers.js +1 -0
  421. package/dist/server/task-ledger-types.d.ts +9 -0
  422. package/dist/server/task-ledger.d.ts +1 -1
  423. package/dist/server/task-ledger.js +2 -1
  424. package/dist/server/task-queue-types.d.ts +2 -0
  425. package/dist/storage/bun-sql.js +51 -6
  426. package/dist/storage/catalog-keys.d.ts +64 -0
  427. package/dist/storage/catalog-keys.js +8 -0
  428. package/dist/storage/compressed-storage.js +1 -1
  429. package/dist/storage/derived-operations.js +1 -1
  430. package/dist/storage/index.d.ts +28 -1
  431. package/dist/storage/interface.d.ts +28 -1
  432. package/dist/storage/interface.js +1 -1
  433. package/dist/storage/key-encoding.d.ts +15 -0
  434. package/dist/storage/key-encoding.js +3 -0
  435. package/dist/storage/key-prefixes.d.ts +1 -1
  436. package/dist/storage/key-prefixes.js +16 -0
  437. package/dist/storage/lmdb.d.ts +54 -1
  438. package/dist/storage/lmdb.js +1 -1
  439. package/dist/storage/mailbox-keys.d.ts +80 -0
  440. package/dist/storage/mailbox-keys.js +14 -0
  441. package/dist/storage/memory.js +1 -1
  442. package/dist/storage/neon.js +2 -2
  443. package/dist/storage/node-sqlite-loader.d.ts +1 -1
  444. package/dist/storage/node-sqlite.js +51 -6
  445. package/dist/storage/outbox-keys.d.ts +77 -0
  446. package/dist/storage/outbox-keys.js +14 -0
  447. package/dist/storage/postgres.js +2 -2
  448. package/dist/storage/resolve.js +1 -1
  449. package/dist/storage/scoped-storage.js +1 -1
  450. package/dist/storage/storage-configuration.d.ts +6 -0
  451. package/dist/storage/testing.js +1 -1
  452. package/dist/storage/turso.js +2 -2
  453. package/dist/version.d.ts +1 -1
  454. package/dist/version.js +1 -1
  455. package/dist/web-extension.js +1 -1
  456. package/dist/worker/manifest/registry-contract-builder.d.ts +21 -3
  457. package/dist/worker/manifest/registry-contract-builder.js +40 -31
  458. package/dist/worker/protocol-messages.d.ts +17 -0
  459. package/dist/worker/protocol-schemas.d.ts +32 -0
  460. package/dist/worker/protocol-schemas.js +7 -3
  461. package/dist/worker/protocol-task-result.js +35 -3
  462. package/dist/worker/protocol.js +1 -1
  463. package/dist/worker/registry/types.d.ts +2 -0
  464. package/dist/worker/registry.d.ts +1 -1
  465. package/dist/worker/registry.js +3 -1
  466. package/dist/workers/workflow-runner.d.ts +9 -0
  467. package/dist/workers/workflow-runner.js +9 -2
  468. package/dist/workers/workflow-worker-entry.js +9 -4
  469. package/package.json +2 -2
@@ -1,23 +1,39 @@
1
- import { requireStorageCapability, storageValuesEqual } from "../../../storage/interface.js";
1
+ import {
2
+ KEYS,
3
+ requireStorageCapability,
4
+ storageValuesEqual
5
+ } from "../../../storage/interface.js";
2
6
  import { AtomicStateConflictError } from "../../atomic-state.js";
7
+ import { WorkflowAlreadyExistsError } from "../errors.js";
3
8
  import {
4
9
  commitFencedEngineWrite,
5
10
  commitFencedEngineWriteAllowingPreconditionFailure
6
11
  } from "../fenced-write.js";
12
+ import { WorkflowRevisionUnavailableError } from "../revision-errors.js";
7
13
  import {
8
14
  commitWithWorkflowClaimFold,
9
15
  prepareWorkflowClaimFold,
10
16
  throwWorkflowClaimUnavailable
11
17
  } from "../workflow-claim-fold.js";
12
18
  import { buildStartBatchOperations } from "./start-batch.js";
13
-
14
- export class StartIdempotencyRaceLostError extends Error {
15
- constructor() {
16
- super("start idempotency compare-and-swap lost to a concurrent caller");
17
- this.name = "StartIdempotencyRaceLostError";
18
- }
19
- }
19
+ import { StartIdempotencyRaceLostError } from "./start-commit-errors.js";
20
+ import { attributeLostStartPreconditionOrRetry } from "./start-precondition-attribution.js";
20
21
  const WORKFLOW_CONCURRENCY_ADMISSION_MAX_ATTEMPTS = 5;
22
+ function needsCatalogEntryStartPrecondition(internals, state) {
23
+ return internals.options.ownershipMode !== "none" && state.revision !== void 0;
24
+ }
25
+ async function buildCatalogEntryStartPrecondition(internals, state) {
26
+ return {
27
+ source: "catalog-entry",
28
+ condition: await buildCatalogEntryRevisionCondition(internals, state.type, state.revision)
29
+ };
30
+ }
31
+ export async function buildCatalogEntryRevisionCondition(internals, type, revision) {
32
+ const key = KEYS.catalogEntry(type, revision), entryBytes = await internals.storage.get(key);
33
+ if (entryBytes === null)
34
+ throw new WorkflowRevisionUnavailableError(type, revision, "not-installed");
35
+ return { key, expectedValue: entryBytes };
36
+ }
21
37
  async function persistStartBatch(internals, workflowId, startOperations, conditions, claimFold, isDelayedStart) {
22
38
  if (claimFold) {
23
39
  const result = await commitWithWorkflowClaimFold(internals, claimFold, startOperations, conditions.map((entry) => entry.condition), "workflow claim acquisition");
@@ -33,9 +49,19 @@ async function persistStartBatch(internals, workflowId, startOperations, conditi
33
49
  requireStorageCapability(internals.storage, "conditionalBatch", "start preconditions");
34
50
  return await commitFencedEngineWriteAllowingPreconditionFailure(internals, fenceWorkflowId, startOperations, conditions.map((entry) => entry.condition)) ? "committed" : "precondition-lost";
35
51
  }
36
- async function hasStartPreconditionConflict(internals, conditions) {
52
+ async function hasStartConditionConflict(internals, conditions, source) {
53
+ for (const entry of conditions) {
54
+ if (entry.source !== source)
55
+ continue;
56
+ const currentValue = await internals.storage.get(entry.condition.key);
57
+ if (!storageValuesEqual(currentValue, entry.condition.expectedValue))
58
+ return !0;
59
+ }
60
+ return !1;
61
+ }
62
+ async function hasCatalogEntryConflict(internals, conditions) {
37
63
  for (const entry of conditions) {
38
- if (entry.source !== "start-precondition")
64
+ if (entry.source !== "catalog-entry")
39
65
  continue;
40
66
  const currentValue = await internals.storage.get(entry.condition.key);
41
67
  if (!storageValuesEqual(currentValue, entry.condition.expectedValue))
@@ -49,6 +75,9 @@ function tagStartPreconditions(conditions) {
49
75
  condition
50
76
  }));
51
77
  }
78
+ function tagDuplicateIdCondition(condition) {
79
+ return condition === void 0 ? [] : [{ source: "duplicate-id", condition }];
80
+ }
52
81
  function tagWorkflowConcurrencyConditions(conditions) {
53
82
  return conditions.map((condition) => ({
54
83
  source: "workflow-concurrency",
@@ -66,18 +95,23 @@ export async function buildAndCommitStartBatch(context, buildIdempotentStartOper
66
95
  for (let attempt = 0;attempt < maxAttempts; attempt += 1) {
67
96
  const idempotent = buildIdempotentStartOperations?.(workflowId), workflowConcurrency = await context.buildWorkflowConcurrencyStartOperations?.();
68
97
  lastWorkflowConcurrencyStateKey = workflowConcurrency?.stateKey ?? lastWorkflowConcurrencyStateKey;
69
- const startOperations = buildStartBatchOperations(internals, workflowId, state, checkpoint, registration, options, state.executionDeadline, context.delayedStartTimer, context.persistedWorkflowStartHeaders, mergeAdditionalStartOperations(context.additionalStartOperations, mergeAdditionalStartOperations(idempotent?.operations, workflowConcurrency?.operations)), context.callbacks, context.purgeDeleteOperations), conditions = [
98
+ const catalogEntryPrecondition = needsCatalogEntryStartPrecondition(internals, state) ? await buildCatalogEntryStartPrecondition(internals, state) : void 0, startOperations = buildStartBatchOperations(internals, workflowId, state, checkpoint, registration, options, state.executionDeadline, context.delayedStartTimer, context.persistedWorkflowStartHeaders, mergeAdditionalStartOperations(context.additionalStartOperations, mergeAdditionalStartOperations(idempotent?.operations, workflowConcurrency?.operations)), context.callbacks, context.purgeDeleteOperations), conditions = [
70
99
  ...tagStartPreconditions(idempotent?.conditions),
71
- ...tagWorkflowConcurrencyConditions(workflowConcurrency?.conditions ?? [])
100
+ ...tagDuplicateIdCondition(context.duplicateIdCondition),
101
+ ...tagWorkflowConcurrencyConditions(workflowConcurrency?.conditions ?? []),
102
+ ...catalogEntryPrecondition === void 0 ? [] : [catalogEntryPrecondition]
72
103
  ], isDelayedStart = context.delayedStartTimer !== void 0, claimFold = isDelayedStart ? void 0 : await prepareWorkflowClaimFold(internals, workflowId), outcome = await persistStartBatch(internals, workflowId, startOperations, conditions, claimFold, isDelayedStart);
73
104
  if (outcome === "committed")
74
105
  return;
75
- if (await hasStartPreconditionConflict(internals, conditions))
106
+ if (outcome !== "claim-lost" && await hasStartConditionConflict(internals, conditions, "duplicate-id"))
107
+ throw new WorkflowAlreadyExistsError(workflowId);
108
+ if (await hasStartConditionConflict(internals, conditions, "start-precondition"))
76
109
  throw new StartIdempotencyRaceLostError;
110
+ if (await hasCatalogEntryConflict(internals, conditions))
111
+ throw new WorkflowRevisionUnavailableError(state.type, state.revision, "not-installed");
77
112
  if (outcome === "claim-lost")
78
113
  return throwWorkflowClaimUnavailable(internals, workflowId);
79
- if (workflowConcurrency === void 0)
80
- throw new StartIdempotencyRaceLostError;
114
+ await attributeLostStartPreconditionOrRetry(workflowId, context.duplicateIdCondition !== void 0, workflowConcurrency !== void 0, () => hasStartConditionConflict(internals, conditions, "workflow-concurrency"));
81
115
  }
82
116
  throw new AtomicStateConflictError(lastWorkflowConcurrencyStateKey ?? "workflow concurrency admission", WORKFLOW_CONCURRENCY_ADMISSION_MAX_ATTEMPTS);
83
117
  }
@@ -2,8 +2,24 @@ import type { Checkpoint, StartOptions, WorkflowState } from '../../types.ts';
2
2
  import type { EngineInternals } from '../internals.ts';
3
3
  import { type LifecycleCallbacks, type RegistrationEntry } from './shared.ts';
4
4
  export declare function runWorkflowStartInterceptor(_internals: EngineInternals, workflowId: string, workflowType: string, input: unknown, parentHeaders: Map<string, string> | undefined, callbacks: LifecycleCallbacks): Map<string, string> | undefined;
5
- export declare function startWorkflowExecution(internals: EngineInternals, workflowId: string, workflowExecutionToken: string | undefined, workflowType: string, input: unknown, checkpoint: Checkpoint, nestingDepth: number, executionDeadline: number | undefined, executionStateOwnerId: string, _callbacks?: LifecycleCallbacks): void;
6
- export declare function beginWorkflowExecution(internals: EngineInternals, workflowId: string, workflowExecutionToken: string | undefined, workflowType: string, input: unknown, checkpoint: Checkpoint, executionDeadline: number | undefined, executionStateOwnerId: string, _registration: RegistrationEntry, callbacks: LifecycleCallbacks, onStarted?: () => void): void;
5
+ export declare function startWorkflowExecution(internals: EngineInternals, workflowId: string, workflowExecutionToken: string | undefined, workflowType: string,
6
+ /**
7
+ * The starting run's persisted `WorkflowState.revision` (WFT-19/WFT-20):
8
+ * populates the per-instance identity cache readers use for revision-scoped
9
+ * routing, and is echoed to the strategy's `startWorkflow` for worker-protocol
10
+ * revision validation.
11
+ */
12
+ revision: string | undefined, input: unknown, checkpoint: Checkpoint, nestingDepth: number, executionDeadline: number | undefined, executionStateOwnerId: string, _callbacks?: LifecycleCallbacks): void;
13
+ export declare function beginWorkflowExecution(internals: EngineInternals, workflowId: string, workflowExecutionToken: string | undefined, workflowType: string,
14
+ /**
15
+ * The starting run's persisted `WorkflowState.revision` (WFT-19/WFT-20).
16
+ * Threaded into both the queued-inline path below (which also re-reads
17
+ * `revision` off the RELOADED `WorkflowState` at flush time —
18
+ * `inline-launch-queue.ts` — exactly like it already does for
19
+ * `workflowExecutionToken`) and the non-inline `startWorkflowExecution`
20
+ * branch, which populates the per-instance identity cache with it directly.
21
+ */
22
+ revision: string | undefined, input: unknown, checkpoint: Checkpoint, executionDeadline: number | undefined, executionStateOwnerId: string, _registration: RegistrationEntry, callbacks: LifecycleCallbacks, onStarted?: () => void): void;
7
23
  /**
8
24
  * `defer: false` is an inline-only liveness gate: it awaits the moment the
9
25
  * generator is first driven. A worker-mode start queues to the Worker transport
@@ -20,13 +20,14 @@ export function runWorkflowStartInterceptor(_internals, workflowId, workflowType
20
20
  });
21
21
  return capturedHeaders;
22
22
  }
23
- export function startWorkflowExecution(internals, workflowId, workflowExecutionToken, workflowType, input, checkpoint, nestingDepth, executionDeadline, executionStateOwnerId, _callbacks) {
23
+ export function startWorkflowExecution(internals, workflowId, workflowExecutionToken, workflowType, revision, input, checkpoint, nestingDepth, executionDeadline, executionStateOwnerId, _callbacks) {
24
24
  if (nestingDepth !== 0)
25
25
  internals.workflowNestingDepths.set(workflowId, nestingDepth);
26
- internals.workflowTypeByWorkflowId.set(workflowId, workflowType);
26
+ internals.workflowTypeByWorkflowId.set(workflowId, { type: workflowType, revision });
27
27
  internals.strategy.startWorkflow({
28
28
  workflowId,
29
29
  ...workflowExecutionToken !== void 0 && { workflowExecutionToken },
30
+ ...revision !== void 0 && { revision },
30
31
  workflowType,
31
32
  input,
32
33
  checkpoint: serializeCheckpoint(checkpoint),
@@ -40,7 +41,7 @@ export function startWorkflowExecution(internals, workflowId, workflowExecutionT
40
41
  }
41
42
  });
42
43
  }
43
- export function beginWorkflowExecution(internals, workflowId, workflowExecutionToken, workflowType, input, checkpoint, executionDeadline, executionStateOwnerId, _registration, callbacks, onStarted) {
44
+ export function beginWorkflowExecution(internals, workflowId, workflowExecutionToken, workflowType, revision, input, checkpoint, executionDeadline, executionStateOwnerId, _registration, callbacks, onStarted) {
44
45
  const nestingDepth = internals.pendingNestingDepth ?? 0;
45
46
  internals.pendingNestingDepth = void 0;
46
47
  if (internals.inlineStrategy !== null) {
@@ -48,6 +49,7 @@ export function beginWorkflowExecution(internals, workflowId, workflowExecutionT
48
49
  workflowId,
49
50
  ...workflowExecutionToken !== void 0 && { workflowExecutionToken },
50
51
  workflowType,
52
+ revision,
51
53
  input,
52
54
  checkpoint,
53
55
  nestingDepth,
@@ -58,7 +60,7 @@ export function beginWorkflowExecution(internals, workflowId, workflowExecutionT
58
60
  return;
59
61
  }
60
62
  callbacks.dispatchEvent(new WorkflowStartedEvent(workflowId, workflowType, input));
61
- startWorkflowExecution(internals, workflowId, workflowExecutionToken, workflowType, input, checkpoint, nestingDepth, executionDeadline, executionStateOwnerId, callbacks);
63
+ startWorkflowExecution(internals, workflowId, workflowExecutionToken, workflowType, revision, input, checkpoint, nestingDepth, executionDeadline, executionStateOwnerId, callbacks);
62
64
  onStarted?.();
63
65
  }
64
66
  export function assertDeferSupported(internals, options, isDelayedStart) {
@@ -73,7 +75,7 @@ export async function beginExecutionAwaitingLiveness(internals, params, workflow
73
75
  if (params.isDelayed)
74
76
  return;
75
77
  const liveness = params.options?.defer === !1 ? Promise.withResolvers() : void 0;
76
- beginWorkflowExecution(internals, workflowId, params.state.workflowExecutionToken, params.type, params.input, params.checkpoint, params.state.executionDeadline, params.state.executionStateOwnerId ?? workflowId, params.registration, callbacks, liveness ? () => liveness.resolve() : void 0);
78
+ beginWorkflowExecution(internals, workflowId, params.state.workflowExecutionToken, params.type, params.state.revision, params.input, params.checkpoint, params.state.executionDeadline, params.state.executionStateOwnerId ?? workflowId, params.registration, callbacks, liveness ? () => liveness.resolve() : void 0);
77
79
  if (liveness)
78
80
  await liveness.promise;
79
81
  }
@@ -2,9 +2,7 @@ import { KEYS } from "../../../storage/interface.js";
2
2
  import { encode } from "../../codec.js";
3
3
  import { WorkflowAlreadyExistsError } from "../errors.js";
4
4
  import { buildCreateBatchSignalOperations } from "../signals.js";
5
- import {
6
- StartIdempotencyRaceLostError
7
- } from "./start-commit.js";
5
+ import { StartIdempotencyRaceLostError } from "./start-commit-errors.js";
8
6
  import {
9
7
  requireWinnerId,
10
8
  resolveCallerIdWinnerOrRetry,
@@ -6,7 +6,7 @@ import {
6
6
  StartWorkflowValidationError
7
7
  } from "../../start-workflow-validation.js";
8
8
  import { IdempotencyKeyPurgedError, StartOrSignalConflictError } from "../errors.js";
9
- import { StartIdempotencyRaceLostError } from "./start-commit.js";
9
+ import { StartIdempotencyRaceLostError } from "./start-commit-errors.js";
10
10
  import {
11
11
  createWithSignalOrFallback,
12
12
  idempotentStartOperationsFor
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Decide what a lost start compare-and-swap MEANS, once every positively-detectable
3
+ * cause has already been ruled out by the caller — an idempotency precondition, a
4
+ * catalog-entry removal, and a lost workflow claim are each re-checked and raise
5
+ * their own error before this runs.
6
+ *
7
+ * Throws when the loss is terminal; returns normally when the caller should retry
8
+ * workflow-concurrency admission.
9
+ *
10
+ * WFT-152: a caller-supplied id that lost its duplicate-id condition means another
11
+ * engine sharing this store committed a create for the same id after this start's
12
+ * duplicate-id read. It raises the SAME {@link WorkflowAlreadyExistsError} the
13
+ * in-engine `pendingStarts` guard raises for the identical collision, so a
14
+ * cross-engine duplicate id is indistinguishable from an in-engine one at the call
15
+ * site.
16
+ *
17
+ * Attribution is by ELIMINATION, never by re-reading the duplicate-id key. A
18
+ * re-read is unsound: the winning run can complete and be purged (or swept by
19
+ * retention) between the failed compare-and-swap and the check, restoring the
20
+ * workflow record to the very value the condition expected, so the conflict reads
21
+ * back as "no conflict". With no workflow-concurrency conditions in the batch,
22
+ * nothing else is left for the lost outcome to mean.
23
+ *
24
+ * When concurrency conditions ARE present, retry only on positive evidence that the
25
+ * retryable one is what missed — hence `hasWorkflowConcurrencyConflict`, invoked
26
+ * lazily so the extra storage read happens only on that path.
27
+ *
28
+ * That evidence is NOT proof the duplicate id was fine, and deliberately is not
29
+ * treated as such. The concurrency precondition is a monotonic atomic-state VERSION
30
+ * key (`buildWorkflowConcurrencyStartOperations` conditions on `snapshot.version`
31
+ * and writes `version + 1`; releasing the slot increments again rather than
32
+ * restoring), so once a same-id winner acquires and releases, that condition stays
33
+ * mismatched forever and reports a conflict regardless of what else missed. What
34
+ * makes retrying safe here is the caller's own earlier check: it re-reads the
35
+ * duplicate-id key positively and raises `WorkflowAlreadyExistsError` before this
36
+ * runs, so reaching this point means the workflow record currently matches the
37
+ * expectation — the id is free right now, and a retry re-conditions on that same
38
+ * still-matching value.
39
+ *
40
+ * The residual is the pre-compare-and-swap purge ABA (WFT-153): a winner purged
41
+ * between the read and the commit makes an absent record look never-used, which no
42
+ * value-comparing condition can detect. That is tracked separately and is the reason
43
+ * this function does not claim exclusive attribution.
44
+ *
45
+ * With no concurrency evidence at all, fail closed: something missed, nothing
46
+ * retryable explains it, and a spurious `WorkflowAlreadyExistsError` is public,
47
+ * non-destructive, and retryable by the caller.
48
+ */
49
+ export declare function attributeLostStartPreconditionOrRetry(workflowId: string, hasDuplicateIdCondition: boolean, hasWorkflowConcurrency: boolean, hasWorkflowConcurrencyConflict: () => Promise<boolean>): Promise<void>;
@@ -0,0 +1,13 @@
1
+ import { WorkflowAlreadyExistsError } from "../errors.js";
2
+ import { StartIdempotencyRaceLostError } from "./start-commit-errors.js";
3
+ export async function attributeLostStartPreconditionOrRetry(workflowId, hasDuplicateIdCondition, hasWorkflowConcurrency, hasWorkflowConcurrencyConflict) {
4
+ if (!hasDuplicateIdCondition) {
5
+ if (!hasWorkflowConcurrency)
6
+ throw new StartIdempotencyRaceLostError;
7
+ return;
8
+ }
9
+ if (!hasWorkflowConcurrency)
10
+ throw new WorkflowAlreadyExistsError(workflowId);
11
+ if (!await hasWorkflowConcurrencyConflict())
12
+ throw new WorkflowAlreadyExistsError(workflowId);
13
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Resolve the exact revision a fresh `startWorkflow()` call — or a schedule's
3
+ * pin capture (WFT-20, `pinned-schedule-revision.ts`) — is about to commit
4
+ * to. Extracted out of `lifecycle/start.ts` so both that module and the
5
+ * schedule pin-capture path import the SAME logic instead of duplicating the
6
+ * `registeredCatalogRevisions`-fallback + `ensureWorkflowCatalogReady()`
7
+ * recheck rule; `start.ts` imports these back unchanged.
8
+ *
9
+ * @module core/engine/lifecycle/start-revision-resolution
10
+ */
11
+ import type { EngineInternals } from '../internals.ts';
12
+ /**
13
+ * The exact executable artifact a start is about to run: the resolved
14
+ * dynamic-source candidate revision, or — for an eager registration, which
15
+ * never populates `resolvedRevision` — this process's own
16
+ * `registeredCatalogRevisions` entry for `type` (the revision of the code
17
+ * actually loaded here, NOT `inFlightRevision`, which for an eager type
18
+ * falls back to the catalog's cached ACTIVE pointer and can name a revision
19
+ * this process never loaded under a multi-engine deployment). Synchronous,
20
+ * on purpose: every top-level engine.* method already awaits
21
+ * `ensureWorkflowCatalogReady()` before reaching a call site that needs this,
22
+ * so this map is populated by the time the overwhelmingly common case gets
23
+ * here. `undefined` means "genuinely not cached yet"; the caller falls back
24
+ * to {@link resolveStartRevisionUncached}.
25
+ */
26
+ export declare function resolveCachedStartRevision(internals: EngineInternals, type: string, resolvedRevision: string | undefined): string | undefined;
27
+ /**
28
+ * The rare fallback {@link resolveCachedStartRevision} defers to: a fired
29
+ * schedule occurrence or a delayed-start timer calls `startWorkflow`
30
+ * directly from background scheduler code, with no top-level
31
+ * `ensureWorkflowCatalogReady()` gate already awaited. Re-checks catalog
32
+ * readiness once, then re-reads the cache.
33
+ */
34
+ export declare function resolveStartRevisionUncached(internals: EngineInternals, type: string): Promise<string>;
@@ -0,0 +1,11 @@
1
+ import { ensureWorkflowCatalogReady } from "../catalog-readiness.js";
2
+ export function resolveCachedStartRevision(internals, type, resolvedRevision) {
3
+ return resolvedRevision ?? internals.registeredCatalogRevisions.get(type);
4
+ }
5
+ export async function resolveStartRevisionUncached(internals, type) {
6
+ await ensureWorkflowCatalogReady(internals.engine);
7
+ const afterReadiness = internals.registeredCatalogRevisions.get(type);
8
+ if (afterReadiness !== void 0)
9
+ return afterReadiness;
10
+ throw Error(`Cannot start workflow "${type}": no catalog revision is registered for this eagerly-registered type, even after re-checking catalog readiness. This should be unreachable.`);
11
+ }
@@ -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;