@lostgradient/weft 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (579) hide show
  1. package/README.md +123 -43
  2. package/dist/cli/conformance.js +7 -1
  3. package/dist/cli/generated/operation-client.generated.d.ts +55 -13
  4. package/dist/cli/generated/operation-client.generated.js +3 -0
  5. package/dist/cli/help-text.d.ts +1 -1
  6. package/dist/cli/help-text.js +1 -0
  7. package/dist/cli/operation-catalog-snapshot.d.ts +8 -0
  8. package/dist/cli/operation-catalog-snapshot.js +11 -0
  9. package/dist/cli/parse-schedule-arguments.js +3 -1
  10. package/dist/cli/schedule.js +2 -1
  11. package/dist/cli/types.d.ts +1 -0
  12. package/dist/cli-main.js +80 -79
  13. package/dist/client/handle-delegation.d.ts +7 -2
  14. package/dist/client/handle-delegation.js +9 -1
  15. package/dist/client/http-client-requests.d.ts +4 -1
  16. package/dist/client/http-client-requests.js +10 -0
  17. package/dist/client/http-client.d.ts +10 -4
  18. package/dist/client/http-client.js +18 -1
  19. package/dist/client/http-request.d.ts +11 -1
  20. package/dist/client/http-request.js +17 -7
  21. package/dist/client/index.d.ts +1 -1
  22. package/dist/client/interface.d.ts +100 -4
  23. package/dist/client/local.d.ts +7 -1
  24. package/dist/client/local.js +17 -2
  25. package/dist/client/start-body.d.ts +9 -2
  26. package/dist/client/start-body.js +13 -4
  27. package/dist/core/byte-arrays.d.ts +5 -0
  28. package/dist/core/byte-arrays.js +5 -0
  29. package/dist/core/checkpoint/lifecycle.d.ts +3 -0
  30. package/dist/core/checkpoint/lifecycle.js +9 -4
  31. package/dist/core/checkpoint/serialization.js +12 -5
  32. package/dist/core/codec/extension-codec.d.ts +16 -0
  33. package/dist/core/codec/extension-codec.js +104 -8
  34. package/dist/core/codec/index.d.ts +1 -0
  35. package/dist/core/codec/index.js +1 -0
  36. package/dist/core/codec/serializer-registry.d.ts +122 -0
  37. package/dist/core/codec/serializer-registry.js +51 -0
  38. package/dist/core/context/activity-retry-state.d.ts +38 -0
  39. package/dist/core/context/activity-retry-state.js +89 -0
  40. package/dist/core/context/activity-schedule-to-close.d.ts +159 -0
  41. package/dist/core/context/activity-schedule-to-close.js +69 -0
  42. package/dist/core/context/child-workflow-pipe.d.ts +5 -5
  43. package/dist/core/context/child-workflow-pipe.js +15 -2
  44. package/dist/core/context/context-presence.d.ts +19 -0
  45. package/dist/core/context/context-presence.js +13 -0
  46. package/dist/core/context/durable-operations.d.ts +25 -0
  47. package/dist/core/context/durable-operations.js +64 -0
  48. package/dist/core/context/index.d.ts +18 -5
  49. package/dist/core/context/index.js +17 -12
  50. package/dist/core/context/internals.d.ts +29 -0
  51. package/dist/core/context/internals.js +6 -1
  52. package/dist/core/context/operation-request.d.ts +35 -0
  53. package/dist/core/context/parallel-cache-entry.d.ts +14 -5
  54. package/dist/core/context/run-operation.d.ts +21 -3
  55. package/dist/core/context/run-operation.js +56 -93
  56. package/dist/core/context/session-state.js +7 -8
  57. package/dist/core/context/speculative-child.js +2 -0
  58. package/dist/core/context/types.d.ts +15 -0
  59. package/dist/core/context/version-patching.d.ts +8 -0
  60. package/dist/core/context/version-patching.js +40 -0
  61. package/dist/core/context/workflow-logger.d.ts +98 -0
  62. package/dist/core/context/workflow-logger.js +68 -0
  63. package/dist/core/engine/activity-heartbeat-tracking.d.ts +64 -0
  64. package/dist/core/engine/activity-heartbeat-tracking.js +42 -0
  65. package/dist/core/engine/activity-per-attempt-timeout.d.ts +41 -0
  66. package/dist/core/engine/activity-per-attempt-timeout.js +36 -0
  67. package/dist/core/engine/activity-reconciliation.d.ts +1 -0
  68. package/dist/core/engine/activity-reconciliation.js +12 -1
  69. package/dist/core/engine/activity-resolution.d.ts +23 -0
  70. package/dist/core/engine/activity-resolution.js +37 -0
  71. package/dist/core/engine/aggregate.js +66 -30
  72. package/dist/core/engine/async-activity-completion.d.ts +23 -6
  73. package/dist/core/engine/async-activity-completion.js +53 -12
  74. package/dist/core/engine/attributes-tags.js +9 -4
  75. package/dist/core/engine/bulk-operations-purge.d.ts +26 -0
  76. package/dist/core/engine/bulk-operations-purge.js +38 -3
  77. package/dist/core/engine/bulk-operations-shared.d.ts +2 -0
  78. package/dist/core/engine/bulk-operations-shared.js +8 -2
  79. package/dist/core/engine/bulk-operations.d.ts +4 -1
  80. package/dist/core/engine/bulk-operations.js +235 -24
  81. package/dist/core/engine/callback-creators-bundles.d.ts +9 -0
  82. package/dist/core/engine/callback-creators-bundles.js +30 -2
  83. package/dist/core/engine/callback-creators-core.js +6 -4
  84. package/dist/core/engine/callback-creators-router.js +10 -0
  85. package/dist/core/engine/callback-creators-schedule.d.ts +2 -2
  86. package/dist/core/engine/callback-creators-schedule.js +12 -8
  87. package/dist/core/engine/candidate-read-batching.d.ts +6 -0
  88. package/dist/core/engine/candidate-read-batching.js +1 -0
  89. package/dist/core/engine/checkpoint-io.js +61 -25
  90. package/dist/core/engine/checkpoint-reads.js +3 -2
  91. package/dist/core/engine/checkpoint-replay.d.ts +20 -0
  92. package/dist/core/engine/checkpoint-replay.js +242 -0
  93. package/dist/core/engine/checkpoint-side-effects.d.ts +23 -0
  94. package/dist/core/engine/checkpoint-side-effects.js +35 -0
  95. package/dist/core/engine/child-workflow-cancellation.d.ts +10 -0
  96. package/dist/core/engine/child-workflow-cancellation.js +40 -0
  97. package/dist/core/engine/child-workflow.js +27 -4
  98. package/dist/core/engine/condition-waiters.d.ts +14 -0
  99. package/dist/core/engine/condition-waiters.js +5 -0
  100. package/dist/core/engine/construction.d.ts +3 -1
  101. package/dist/core/engine/construction.js +30 -8
  102. package/dist/core/engine/coordination-branch-executors.d.ts +70 -0
  103. package/dist/core/engine/coordination-branch-executors.js +107 -0
  104. package/dist/core/engine/deferred-consume-envelope.d.ts +59 -0
  105. package/dist/core/engine/deferred-consume-envelope.js +14 -0
  106. package/dist/core/engine/disposal.js +25 -0
  107. package/dist/core/engine/engine-create-types.d.ts +14 -18
  108. package/dist/core/engine/engine-internal-types.d.ts +38 -3
  109. package/dist/core/engine/engine-leak-warnings.d.ts +6 -0
  110. package/dist/core/engine/engine-leak-warnings.js +4 -0
  111. package/dist/core/engine/engine-runtime-helpers.d.ts +17 -0
  112. package/dist/core/engine/engine-runtime-helpers.js +26 -5
  113. package/dist/core/engine/errors.d.ts +121 -0
  114. package/dist/core/engine/errors.js +45 -1
  115. package/dist/core/engine/event-log-compaction.d.ts +8 -0
  116. package/dist/core/engine/event-log-compaction.js +20 -2
  117. package/dist/core/engine/fenced-write.d.ts +65 -0
  118. package/dist/core/engine/fenced-write.js +71 -0
  119. package/dist/core/engine/finalizer-state.d.ts +28 -0
  120. package/dist/core/engine/finalizer-state.js +16 -0
  121. package/dist/core/engine/handle-result.js +1 -1
  122. package/dist/core/engine/handles.d.ts +97 -40
  123. package/dist/core/engine/handles.js +25 -27
  124. package/dist/core/engine/index.d.ts +125 -15
  125. package/dist/core/engine/index.js +207 -11
  126. package/dist/core/engine/inline-launch-queue.d.ts +14 -0
  127. package/dist/core/engine/inline-launch-queue.js +32 -7
  128. package/dist/core/engine/inline-parking.js +3 -2
  129. package/dist/core/engine/internals.d.ts +125 -13
  130. package/dist/core/engine/lease-codec.d.ts +35 -0
  131. package/dist/core/engine/lease-codec.js +32 -0
  132. package/dist/core/engine/lease-deposition.d.ts +66 -0
  133. package/dist/core/engine/lease-deposition.js +10 -0
  134. package/dist/core/engine/lease-errors.d.ts +99 -0
  135. package/dist/core/engine/lease-errors.js +30 -0
  136. package/dist/core/engine/lease-manager.d.ts +114 -0
  137. package/dist/core/engine/lease-manager.js +135 -0
  138. package/dist/core/engine/lifecycle/fork-helpers.js +1 -7
  139. package/dist/core/engine/lifecycle/persist.js +15 -52
  140. package/dist/core/engine/lifecycle/recovered-services.d.ts +8 -6
  141. package/dist/core/engine/lifecycle/recovered-services.js +18 -3
  142. package/dist/core/engine/lifecycle/resume.js +39 -11
  143. package/dist/core/engine/lifecycle/shared.d.ts +8 -1
  144. package/dist/core/engine/lifecycle/start-batch.d.ts +1 -1
  145. package/dist/core/engine/lifecycle/start-batch.js +2 -1
  146. package/dist/core/engine/lifecycle/start-commit.d.ts +56 -0
  147. package/dist/core/engine/lifecycle/start-commit.js +69 -0
  148. package/dist/core/engine/lifecycle/start-exec.d.ts +30 -2
  149. package/dist/core/engine/lifecycle/start-exec.js +38 -0
  150. package/dist/core/engine/lifecycle/start-or-signal-create.d.ts +58 -0
  151. package/dist/core/engine/lifecycle/start-or-signal-create.js +95 -0
  152. package/dist/core/engine/lifecycle/start-or-signal-resolution.d.ts +79 -0
  153. package/dist/core/engine/lifecycle/start-or-signal-resolution.js +60 -0
  154. package/dist/core/engine/lifecycle/start-or-signal.d.ts +46 -0
  155. package/dist/core/engine/lifecycle/start-or-signal.js +71 -0
  156. package/dist/core/engine/lifecycle/start-terminal-conflict-purge.d.ts +39 -0
  157. package/dist/core/engine/lifecycle/start-terminal-conflict-purge.js +28 -0
  158. package/dist/core/engine/lifecycle/start.d.ts +6 -6
  159. package/dist/core/engine/lifecycle/start.js +47 -45
  160. package/dist/core/engine/lifecycle/transition.js +17 -5
  161. package/dist/core/engine/lifecycle.d.ts +3 -2
  162. package/dist/core/engine/lifecycle.js +9 -2
  163. package/dist/core/engine/list-candidate-resolution.js +2 -2
  164. package/dist/core/engine/listing.js +10 -6
  165. package/dist/core/engine/operations-activity.d.ts +4 -11
  166. package/dist/core/engine/operations-activity.js +22 -49
  167. package/dist/core/engine/operations-coordination.d.ts +10 -0
  168. package/dist/core/engine/operations-coordination.js +43 -8
  169. package/dist/core/engine/operations-router.d.ts +6 -0
  170. package/dist/core/engine/operations-router.js +2 -0
  171. package/dist/core/engine/operations-speculate.js +6 -2
  172. package/dist/core/engine/operations-stream.js +12 -2
  173. package/dist/core/engine/operations-time.d.ts +4 -2
  174. package/dist/core/engine/operations-time.js +14 -2
  175. package/dist/core/engine/operations-wait-condition.d.ts +23 -0
  176. package/dist/core/engine/operations-wait-condition.js +62 -0
  177. package/dist/core/engine/ownership-options.d.ts +23 -0
  178. package/dist/core/engine/ownership-options.js +26 -0
  179. package/dist/core/engine/payload-size-policy.d.ts +3 -0
  180. package/dist/core/engine/payload-size-policy.js +4 -0
  181. package/dist/core/engine/pending-updates.d.ts +17 -0
  182. package/dist/core/engine/pending-updates.js +33 -11
  183. package/dist/core/engine/persisted-data-version.d.ts +5 -9
  184. package/dist/core/engine/persisted-data-version.js +4 -5
  185. package/dist/core/engine/queries.js +1 -1
  186. package/dist/core/engine/registration.d.ts +1 -0
  187. package/dist/core/engine/registration.js +18 -3
  188. package/dist/core/engine/schedule-handle.d.ts +45 -0
  189. package/dist/core/engine/schedule-handle.js +26 -0
  190. package/dist/core/engine/schedule-jitter.d.ts +5 -0
  191. package/dist/core/engine/schedule-jitter.js +10 -0
  192. package/dist/core/engine/schedule-run.d.ts +17 -0
  193. package/dist/core/engine/schedule-run.js +38 -0
  194. package/dist/core/engine/schedule-timer.js +81 -17
  195. package/dist/core/engine/schedules.d.ts +8 -5
  196. package/dist/core/engine/schedules.js +44 -19
  197. package/dist/core/engine/search-attribute-records.d.ts +4 -0
  198. package/dist/core/engine/search-attribute-records.js +17 -0
  199. package/dist/core/engine/second-instance-detector.d.ts +96 -0
  200. package/dist/core/engine/second-instance-detector.js +108 -0
  201. package/dist/core/engine/signals.d.ts +38 -0
  202. package/dist/core/engine/signals.js +55 -19
  203. package/dist/core/engine/state-utilities.d.ts +49 -2
  204. package/dist/core/engine/state-utilities.js +114 -3
  205. package/dist/core/engine/storage-io.d.ts +16 -2
  206. package/dist/core/engine/storage-io.js +26 -5
  207. package/dist/core/engine/stream-chunk-loading.d.ts +2 -0
  208. package/dist/core/engine/stream-chunk-loading.js +24 -1
  209. package/dist/core/engine/sub-operation.js +22 -3
  210. package/dist/core/engine/termination/cleanup.d.ts +28 -1
  211. package/dist/core/engine/termination/cleanup.js +44 -2
  212. package/dist/core/engine/termination/complete.js +48 -9
  213. package/dist/core/engine/termination/finalizer-activity.d.ts +53 -0
  214. package/dist/core/engine/termination/finalizer-activity.js +30 -0
  215. package/dist/core/engine/termination/finalizer-claim.d.ts +110 -0
  216. package/dist/core/engine/termination/finalizer-claim.js +80 -0
  217. package/dist/core/engine/termination/finalizer.d.ts +48 -0
  218. package/dist/core/engine/termination/finalizer.js +159 -0
  219. package/dist/core/engine/termination/suspend.d.ts +70 -0
  220. package/dist/core/engine/termination/suspend.js +41 -0
  221. package/dist/core/engine/termination.d.ts +5 -2
  222. package/dist/core/engine/termination.js +6 -0
  223. package/dist/core/engine/updates.js +5 -25
  224. package/dist/core/engine/validation/schedule.d.ts +2 -1
  225. package/dist/core/engine/validation/schedule.js +51 -2
  226. package/dist/core/engine/validation.js +54 -3
  227. package/dist/core/engine/waiting-update-response.d.ts +8 -0
  228. package/dist/core/engine/waiting-update-response.js +22 -0
  229. package/dist/core/engine/workflow-concurrency.d.ts +10 -0
  230. package/dist/core/engine/workflow-concurrency.js +136 -0
  231. package/dist/core/engine/workflow-feed.d.ts +9 -8
  232. package/dist/core/engine/workflow-feed.js +4 -1
  233. package/dist/core/engine/workflow-indexes.d.ts +9 -0
  234. package/dist/core/engine/workflow-indexes.js +10 -0
  235. package/dist/core/engine/workflow-state-stream.js +33 -13
  236. package/dist/core/engine/workflow-visibility-queries.d.ts +2 -2
  237. package/dist/core/engine-helpers.js +1 -0
  238. package/dist/core/events/activity-events.d.ts +43 -12
  239. package/dist/core/events/activity-events.js +28 -0
  240. package/dist/core/events/attribute-events.d.ts +2 -3
  241. package/dist/core/events/event-map.d.ts +19 -12
  242. package/dist/core/events/index.d.ts +1 -0
  243. package/dist/core/events/index.js +1 -0
  244. package/dist/core/events/schedule-events.d.ts +71 -0
  245. package/dist/core/events/schedule-events.js +29 -0
  246. package/dist/core/events/signal-events.d.ts +4 -6
  247. package/dist/core/events/system-events.d.ts +29 -19
  248. package/dist/core/events/system-events.js +26 -0
  249. package/dist/core/events/update-events.d.ts +6 -8
  250. package/dist/core/events/workflow-events.d.ts +108 -20
  251. package/dist/core/events/workflow-events.js +35 -0
  252. package/dist/core/failure-categories.js +5 -17
  253. package/dist/core/fault-code.d.ts +9 -3
  254. package/dist/core/fault-code.js +1 -0
  255. package/dist/core/inline-execution-strategy.context-options.d.ts +69 -0
  256. package/dist/core/inline-execution-strategy.context-options.js +56 -0
  257. package/dist/core/inline-execution-strategy.d.ts +31 -36
  258. package/dist/core/inline-execution-strategy.js +16 -27
  259. package/dist/core/list-filter-validation.d.ts +1 -1
  260. package/dist/core/list-filter-validation.js +2 -1
  261. package/dist/core/payload-size.d.ts +1 -1
  262. package/dist/core/persisted-data-incompatible-error.d.ts +9 -7
  263. package/dist/core/review/events.d.ts +6 -8
  264. package/dist/core/scheduler/scheduler-class.d.ts +10 -1
  265. package/dist/core/scheduler/scheduler-class.js +38 -11
  266. package/dist/core/scheduler/timer-batch.js +9 -1
  267. package/dist/core/scheduler/timer-sources.d.ts +1 -0
  268. package/dist/core/scheduler/timer-sources.js +11 -5
  269. package/dist/core/session-state.d.ts +0 -1
  270. package/dist/core/session-state.js +2 -2
  271. package/dist/core/start-workflow-validation.d.ts +56 -0
  272. package/dist/core/start-workflow-validation.js +30 -1
  273. package/dist/core/step-context.d.ts +10 -6
  274. package/dist/core/step-context.js +7 -15
  275. package/dist/core/types/activity-verification.d.ts +67 -0
  276. package/dist/core/types/activity-verification.js +0 -0
  277. package/dist/core/types/activity.d.ts +86 -64
  278. package/dist/core/types/bulk.d.ts +50 -9
  279. package/dist/core/types/checkpoint.d.ts +44 -8
  280. package/dist/core/types/identity.d.ts +8 -1
  281. package/dist/core/types/launch-metadata.d.ts +33 -0
  282. package/dist/core/types/launch-metadata.js +0 -0
  283. package/dist/core/types/list-options.d.ts +146 -0
  284. package/dist/core/types/list-options.js +0 -0
  285. package/dist/core/types/message-handles.d.ts +25 -0
  286. package/dist/core/types/options.d.ts +165 -215
  287. package/dist/core/types/reviews.d.ts +2 -1
  288. package/dist/core/types/schedules.d.ts +39 -3
  289. package/dist/core/types/services-resolution.d.ts +47 -0
  290. package/dist/core/types/services-resolution.js +0 -0
  291. package/dist/core/types/state.d.ts +11 -11
  292. package/dist/core/types/workflow-builder-runtime.d.ts +12 -3
  293. package/dist/core/types/workflow-builder-runtime.js +11 -2
  294. package/dist/core/types/workflow-builder.d.ts +47 -15
  295. package/dist/core/types/workflow-concurrency.d.ts +39 -0
  296. package/dist/core/types/workflow-concurrency.js +0 -0
  297. package/dist/core/types/workflow-context.d.ts +157 -14
  298. package/dist/core/types/workflow-definition.d.ts +53 -10
  299. package/dist/core/types/workflow-function.d.ts +101 -9
  300. package/dist/core/types/workflow-log.d.ts +98 -0
  301. package/dist/core/types/workflow-log.js +0 -0
  302. package/dist/core/types/workflow-registries.d.ts +14 -3
  303. package/dist/core/types/workflow-registry.d.ts +3 -0
  304. package/dist/core/types/workflow-snapshot.d.ts +29 -0
  305. package/dist/core/types/workflow-snapshot.js +0 -0
  306. package/dist/core/types.d.ts +7 -0
  307. package/dist/core/types.js +7 -0
  308. package/dist/core/versioning.d.ts +9 -39
  309. package/dist/core/versioning.js +2 -12
  310. package/dist/core/weft-error.d.ts +44 -1
  311. package/dist/core/weft-error.js +19 -1
  312. package/dist/core/worker-execution-strategy-options.d.ts +26 -0
  313. package/dist/core/worker-execution-strategy.js +40 -36
  314. package/dist/core/worker-inbound-message.d.ts +38 -0
  315. package/dist/core/worker-inbound-message.js +37 -0
  316. package/dist/core/worker-log-abuse-counter.d.ts +175 -0
  317. package/dist/core/worker-log-abuse-counter.js +107 -0
  318. package/dist/core/worker-message-helpers.d.ts +47 -0
  319. package/dist/core/worker-message-helpers.js +26 -0
  320. package/dist/core/worker-protocol-guard.js +1 -1
  321. package/dist/core/worker-protocol-log.d.ts +56 -0
  322. package/dist/core/worker-protocol-log.js +18 -0
  323. package/dist/core/worker-protocol.d.ts +23 -0
  324. package/dist/core/worker-protocol.js +12 -2
  325. package/dist/core/workflow-version-tuple.d.ts +3 -9
  326. package/dist/core/workflow-version-tuple.js +2 -3
  327. package/dist/diagnostics/doctor.js +6 -3
  328. package/dist/diagnostics/format.js +3 -8
  329. package/dist/diagnostics/types.d.ts +5 -5
  330. package/dist/diagnostics/validate.d.ts +0 -1
  331. package/dist/diagnostics/version-check.d.ts +1 -1
  332. package/dist/diagnostics/version-check.js +10 -13
  333. package/dist/index.d.ts +13 -11
  334. package/dist/index.js +39 -4
  335. package/dist/json-schema.js +3 -3
  336. package/dist/mcp/cli.js +35 -35
  337. package/dist/mcp/dispatcher.js +1 -1
  338. package/dist/mcp/http.js +16 -6
  339. package/dist/mcp/list-filter.js +2 -1
  340. package/dist/mcp/protocol.d.ts +2 -0
  341. package/dist/mcp/protocol.js +1 -1
  342. package/dist/mcp/session.d.ts +16 -0
  343. package/dist/mcp/session.js +60 -6
  344. package/dist/mcp/tools.js +73 -6
  345. package/dist/observability/index.js +2 -2
  346. package/dist/server/asyncapi-channels.js +5 -2
  347. package/dist/server/attribute-filters.d.ts +8 -3
  348. package/dist/server/attribute-filters.js +6 -1
  349. package/dist/server/authentication/api-key.d.ts +2 -1
  350. package/dist/server/authentication/api-key.js +5 -2
  351. package/dist/server/authentication/constant-time-api-key.d.ts +15 -0
  352. package/dist/server/authentication/constant-time-api-key.js +24 -0
  353. package/dist/server/authentication/index.d.ts +1 -1
  354. package/dist/server/authentication/index.js +7 -4
  355. package/dist/server/authentication/rotating-api-key-store.d.ts +5 -3
  356. package/dist/server/authentication/rotating-api-key-store.js +19 -11
  357. package/dist/server/engine-event-feed-backend.d.ts +4 -5
  358. package/dist/server/fault-to-http.js +8 -1
  359. package/dist/server/fault-to-json-rpc.js +1 -0
  360. package/dist/server/fleet-event-feed.d.ts +31 -0
  361. package/dist/server/fleet-event-feed.js +157 -0
  362. package/dist/server/handler/index.js +1 -1
  363. package/dist/server/handler/response-helpers.js +2 -1
  364. package/dist/server/handler/route-dispatch.d.ts +6 -3
  365. package/dist/server/handler/route-dispatch.js +2 -2
  366. package/dist/server/handler.js +31 -31
  367. package/dist/server/index.d.ts +32 -2
  368. package/dist/server/index.js +35 -35
  369. package/dist/server/interactive-operations.js +1 -0
  370. package/dist/server/json-rpc-transport-helpers.d.ts +1 -0
  371. package/dist/server/json-rpc-transport-helpers.js +2 -1
  372. package/dist/server/json-rpc-websocket-runtime.d.ts +11 -2
  373. package/dist/server/json-rpc-websocket-runtime.js +1 -0
  374. package/dist/server/json-rpc-websocket-subscriptions.d.ts +10 -0
  375. package/dist/server/json-rpc-websocket-subscriptions.js +29 -0
  376. package/dist/server/json-rpc-websocket-types.d.ts +22 -0
  377. package/dist/server/json-rpc-websocket-types.js +0 -0
  378. package/dist/server/json-rpc-websocket-validation.js +20 -4
  379. package/dist/server/json-rpc-websocket.d.ts +2 -43
  380. package/dist/server/json-rpc-websocket.js +57 -74
  381. package/dist/server/openapi.d.ts +2 -1
  382. package/dist/server/openapi.js +1 -3
  383. package/dist/server/openrpc-document-schema.d.ts +78 -0
  384. package/dist/server/openrpc-document-schema.js +27 -1
  385. package/dist/server/openrpc-errors.d.ts +3 -0
  386. package/dist/server/openrpc-errors.js +2 -1
  387. package/dist/server/openrpc.js +32 -0
  388. package/dist/server/operation-catalog/index.d.ts +1 -1
  389. package/dist/server/operation-catalog/pipeline-helpers.js +1 -0
  390. package/dist/server/operation-catalog/pipeline-stages.js +26 -12
  391. package/dist/server/operation-catalog/pipeline.js +8 -1
  392. package/dist/server/operation-catalog/registry.js +13 -0
  393. package/dist/server/operation-catalog/types.d.ts +20 -9
  394. package/dist/server/operation-fault.d.ts +10 -0
  395. package/dist/server/operation-fault.js +2 -0
  396. package/dist/server/operation-registry.d.ts +12 -11
  397. package/dist/server/operation-registry.js +12 -3
  398. package/dist/server/operations/aggregate-workflows.d.ts +15 -15
  399. package/dist/server/operations/async-activity.d.ts +2 -2
  400. package/dist/server/operations/async-activity.js +12 -7
  401. package/dist/server/operations/bulk-cancel-workflows.d.ts +1 -1
  402. package/dist/server/operations/bulk-cancel-workflows.js +2 -2
  403. package/dist/server/operations/bulk-delete-workflows.d.ts +1 -1
  404. package/dist/server/operations/bulk-delete-workflows.js +2 -2
  405. package/dist/server/operations/bulk-filter-helpers.d.ts +8 -6
  406. package/dist/server/operations/bulk-filter-helpers.js +54 -24
  407. package/dist/server/operations/bulk-mutate-workflow-tags.d.ts +11 -9
  408. package/dist/server/operations/bulk-mutate-workflow-tags.js +2 -2
  409. package/dist/server/operations/bulk-operation-helpers.d.ts +2 -2
  410. package/dist/server/operations/bulk-retry-failed-workflows.d.ts +7 -0
  411. package/dist/server/operations/bulk-retry-failed-workflows.js +64 -0
  412. package/dist/server/operations/bulk-signal-workflows.d.ts +11 -9
  413. package/dist/server/operations/bulk-signal-workflows.js +2 -2
  414. package/dist/server/operations/cancel-schedule.d.ts +1 -1
  415. package/dist/server/operations/create-schedule.d.ts +3 -1
  416. package/dist/server/operations/create-schedule.js +29 -11
  417. package/dist/server/operations/fleet-events-subscription.d.ts +54 -0
  418. package/dist/server/operations/fleet-events-subscription.js +99 -0
  419. package/dist/server/operations/fork-workflow.d.ts +1 -1
  420. package/dist/server/operations/fork-workflow.js +3 -2
  421. package/dist/server/operations/get-checkpoint-at.d.ts +1 -1
  422. package/dist/server/operations/get-registry.d.ts +1 -1
  423. package/dist/server/operations/get-retention-overview.d.ts +1 -1
  424. package/dist/server/operations/get-review.d.ts +1 -1
  425. package/dist/server/operations/get-schedule.d.ts +1 -1
  426. package/dist/server/operations/get-stream-chunks.d.ts +1 -1
  427. package/dist/server/operations/get-system-metrics.d.ts +2 -2
  428. package/dist/server/operations/get-task-diagnostics.d.ts +45 -6
  429. package/dist/server/operations/get-task-diagnostics.js +101 -7
  430. package/dist/server/operations/get-update-result.d.ts +1 -1
  431. package/dist/server/operations/get-workflow-attributes.d.ts +1 -1
  432. package/dist/server/operations/get-workflow-events.d.ts +1 -1
  433. package/dist/server/operations/get-workflow-result.d.ts +1 -1
  434. package/dist/server/operations/get-workflow-timeline.d.ts +1 -1
  435. package/dist/server/operations/get-workflow.d.ts +1 -1
  436. package/dist/server/operations/list-checkpoints.d.ts +1 -1
  437. package/dist/server/operations/list-reviews.d.ts +1 -1
  438. package/dist/server/operations/list-schedules.d.ts +1 -1
  439. package/dist/server/operations/list-task-queues.d.ts +2 -2
  440. package/dist/server/operations/list-workers.d.ts +2 -2
  441. package/dist/server/operations/list-workflows.d.ts +1 -1
  442. package/dist/server/operations/operation-helpers.d.ts +16 -1
  443. package/dist/server/operations/operation-helpers.js +15 -3
  444. package/dist/server/operations/pause-schedule.d.ts +1 -1
  445. package/dist/server/operations/purge-workflows.d.ts +5 -5
  446. package/dist/server/operations/purge-workflows.js +2 -2
  447. package/dist/server/operations/query-workflow.d.ts +1 -1
  448. package/dist/server/operations/query-workflow.js +3 -2
  449. package/dist/server/operations/recover-all.d.ts +1 -1
  450. package/dist/server/operations/replay-workflow.d.ts +1 -1
  451. package/dist/server/operations/resume-schedule.d.ts +1 -1
  452. package/dist/server/operations/resume-workflow.js +2 -2
  453. package/dist/server/operations/set-workflow-attributes.d.ts +1 -1
  454. package/dist/server/operations/set-workflow-attributes.js +6 -3
  455. package/dist/server/operations/signal-workflow.js +8 -3
  456. package/dist/server/operations/single-workflow-tag-mutation.js +13 -3
  457. package/dist/server/operations/start-or-signal-workflow.d.ts +44 -0
  458. package/dist/server/operations/start-or-signal-workflow.js +130 -0
  459. package/dist/server/operations/start-workflow-options.d.ts +38 -0
  460. package/dist/server/operations/start-workflow-options.js +63 -0
  461. package/dist/server/operations/start-workflow-rest-input.d.ts +14 -0
  462. package/dist/server/operations/start-workflow-rest-input.js +31 -0
  463. package/dist/server/operations/start-workflow.d.ts +1 -1
  464. package/dist/server/operations/start-workflow.js +15 -92
  465. package/dist/server/operations/storage.d.ts +6 -6
  466. package/dist/server/operations/storage.js +24 -14
  467. package/dist/server/operations/stream-workflow-sse.d.ts +1 -1
  468. package/dist/server/operations/submit-review-decision.d.ts +1 -1
  469. package/dist/server/operations/submit-review-decision.js +6 -3
  470. package/dist/server/operations/suspend-workflow.d.ts +13 -0
  471. package/dist/server/operations/suspend-workflow.js +36 -0
  472. package/dist/server/operations/update-schedule.d.ts +1 -1
  473. package/dist/server/operations/update-schedule.js +7 -4
  474. package/dist/server/operations/update-workflow.d.ts +1 -1
  475. package/dist/server/operations/update-workflow.js +7 -3
  476. package/dist/server/operations/worker-drain.d.ts +8 -8
  477. package/dist/server/operations/worker-drain.js +11 -8
  478. package/dist/server/operations/workflow-events-subscription.d.ts +7 -8
  479. package/dist/server/operations/workflow-events-subscription.js +43 -8
  480. package/dist/server/rest-binding.d.ts +21 -8
  481. package/dist/server/rest-bindings.js +24 -0
  482. package/dist/server/rest-body.d.ts +10 -0
  483. package/dist/server/rest-body.js +80 -0
  484. package/dist/server/runtime/authentication-bridge.d.ts +1 -0
  485. package/dist/server/runtime/authentication-bridge.js +33 -6
  486. package/dist/server/runtime/client-visible-events.d.ts +3 -0
  487. package/dist/server/runtime/client-visible-events.js +65 -0
  488. package/dist/server/runtime/context.d.ts +7 -0
  489. package/dist/server/runtime/event-broadcasting.d.ts +6 -0
  490. package/dist/server/runtime/event-broadcasting.js +72 -38
  491. package/dist/server/runtime/retry.d.ts +1 -0
  492. package/dist/server/runtime/retry.js +14 -0
  493. package/dist/server/runtime/shutdown.d.ts +7 -6
  494. package/dist/server/runtime/shutdown.js +6 -1
  495. package/dist/server/runtime/task-dispatch.js +5 -3
  496. package/dist/server/runtime/task-polling.d.ts +16 -2
  497. package/dist/server/runtime/task-polling.js +80 -24
  498. package/dist/server/runtime/task-reconciliation.js +9 -1
  499. package/dist/server/runtime/task-result-resolution.d.ts +15 -0
  500. package/dist/server/runtime/task-result-resolution.js +81 -0
  501. package/dist/server/runtime/websocket-stream.d.ts +20 -1
  502. package/dist/server/runtime/websocket-stream.js +173 -7
  503. package/dist/server/runtime/websocket-upgrade.js +22 -3
  504. package/dist/server/runtime/websocket-worker.d.ts +1 -1
  505. package/dist/server/runtime/websocket-worker.js +46 -30
  506. package/dist/server/serve-internals.d.ts +11 -1
  507. package/dist/server/serve-internals.js +20 -2
  508. package/dist/server/task-state.d.ts +38 -0
  509. package/dist/server/task-state.js +28 -0
  510. package/dist/server/workflow-event-feed.d.ts +36 -115
  511. package/dist/server/workflow-event-feed.js +92 -27
  512. package/dist/service-worker/index.js +31 -31
  513. package/dist/storage/auto.js +1 -1
  514. package/dist/storage/bun-sql.js +24 -0
  515. package/dist/storage/capabilities.d.ts +10 -2
  516. package/dist/storage/capabilities.js +2 -2
  517. package/dist/storage/compressed-storage.js +1 -1
  518. package/dist/storage/default-scope.d.ts +7 -4
  519. package/dist/storage/http.js +2 -2
  520. package/dist/storage/index.d.ts +81 -4
  521. package/dist/storage/indexeddb.js +1 -1
  522. package/dist/storage/interface.d.ts +189 -0
  523. package/dist/storage/interface.js +1 -1
  524. package/dist/storage/key-prefixes.d.ts +1 -1
  525. package/dist/storage/key-prefixes.js +12 -0
  526. package/dist/storage/lmdb.d.ts +4 -3
  527. package/dist/storage/lmdb.js +1 -1
  528. package/dist/storage/memory.js +1 -1
  529. package/dist/storage/neon-batch.d.ts +30 -0
  530. package/dist/storage/neon-batch.js +30 -0
  531. package/dist/storage/neon-value-mapping.d.ts +74 -0
  532. package/dist/storage/neon-value-mapping.js +23 -0
  533. package/dist/storage/neon.d.ts +124 -0
  534. package/dist/storage/neon.js +15 -0
  535. package/dist/storage/node-sqlite-loader.d.ts +71 -0
  536. package/dist/storage/node-sqlite-loader.js +41 -0
  537. package/dist/storage/node-sqlite.d.ts +1 -19
  538. package/dist/storage/node-sqlite.js +61 -32
  539. package/dist/storage/postgres-key-value-queries.d.ts +104 -0
  540. package/dist/storage/postgres-key-value-queries.js +101 -0
  541. package/dist/storage/resolve.d.ts +2 -165
  542. package/dist/storage/resolve.js +1 -1
  543. package/dist/storage/scoped-storage.js +1 -1
  544. package/dist/storage/storage-configuration.d.ts +209 -0
  545. package/dist/storage/storage-configuration.js +0 -0
  546. package/dist/storage/testing.d.ts +168 -0
  547. package/dist/storage/testing.js +2 -0
  548. package/dist/storage/text-value-store.d.ts +9 -9
  549. package/dist/storage/turso.d.ts +8 -0
  550. package/dist/storage/turso.js +2 -2
  551. package/dist/storage/typed-storage.js +1 -1
  552. package/dist/storage/web-extension.js +1 -1
  553. package/dist/testing/index.js +35 -35
  554. package/dist/version.d.ts +1 -1
  555. package/dist/version.js +1 -1
  556. package/dist/worker/index.js +9 -5
  557. package/dist/worker/long-poll.js +4 -0
  558. package/dist/worker/protocol-messages.d.ts +20 -0
  559. package/dist/worker/protocol-schemas.d.ts +32 -0
  560. package/dist/worker/protocol-schemas.js +8 -4
  561. package/dist/worker/protocol-task-result.d.ts +28 -0
  562. package/dist/worker/protocol-task-result.js +76 -0
  563. package/dist/worker/protocol.d.ts +4 -15
  564. package/dist/worker/protocol.js +1 -1
  565. package/dist/worker/registry/fair-share.d.ts +29 -0
  566. package/dist/worker/registry/fair-share.js +30 -0
  567. package/dist/worker/registry/routing.d.ts +18 -0
  568. package/dist/worker/registry/routing.js +14 -0
  569. package/dist/worker/registry/types.d.ts +7 -0
  570. package/dist/worker/registry.d.ts +16 -1
  571. package/dist/worker/registry.js +24 -36
  572. package/dist/workers/worker-replay-state.d.ts +32 -0
  573. package/dist/workers/worker-replay-state.js +35 -0
  574. package/dist/workers/worker-state-namespace.d.ts +21 -0
  575. package/dist/workers/worker-state-namespace.js +13 -0
  576. package/dist/workers/workflow-runner.d.ts +19 -15
  577. package/dist/workers/workflow-runner.js +72 -60
  578. package/dist/workers/workflow-worker-entry.js +5 -2
  579. package/package.json +21 -4
@@ -0,0 +1,38 @@
1
+ import { KEYS } from "../../storage/interface.js";
2
+ import { encode } from "../codec.js";
3
+ import { ScheduleFiredEvent } from "../events.js";
4
+ import { unavailableServicesError } from "./lifecycle/recovered-services.js";
5
+ import { EMPTY_STORAGE_VALUE } from "./lifecycle/shared.js";
6
+ export async function startScheduledRun(internals, state, callbacks, occurrence) {
7
+ const workflowId = crypto.randomUUID(), scheduleRunOperations = state.overlap === "allow" ? [] : [{ type: "put", key: KEYS.scheduleRun(workflowId), value: encode(state.id) }], resolution = await resolveScheduledRunServices(internals, workflowId, state);
8
+ if (resolution !== null) {
9
+ scheduleRunOperations.push({ type: "put", key: KEYS.workflowHasServices(workflowId), value: EMPTY_STORAGE_VALUE }, { type: "put", key: KEYS.terminalCleanupNeeded(workflowId), value: EMPTY_STORAGE_VALUE });
10
+ internals.workflowsNeedingTerminalCleanup.add(workflowId);
11
+ if (resolution.status === "available")
12
+ internals.workflowServices.set(workflowId, resolution.services);
13
+ }
14
+ await callbacks.startWorkflow(state.workflowType, state.input, { id: workflowId }, scheduleRunOperations);
15
+ internals.engine.dispatchEvent(new ScheduleFiredEvent(state.id, workflowId, internals.options.getNow(), occurrence));
16
+ if (resolution !== null && resolution.status === "unavailable") {
17
+ const unavailableError = unavailableServicesError(workflowId, resolution.reason);
18
+ try {
19
+ await callbacks.failWorkflow(workflowId, unavailableError);
20
+ } catch (error) {
21
+ callbacks.handleCleanupError("startScheduledRun", error, workflowId);
22
+ }
23
+ }
24
+ return workflowId;
25
+ }
26
+ async function resolveScheduledRunServices(internals, workflowId, state) {
27
+ const resolver = internals.options.resolveWorkflowServices;
28
+ if (internals.inlineStrategy === null || !resolver)
29
+ return null;
30
+ try {
31
+ return await resolver({ workflowId, workflowType: state.workflowType, input: state.input });
32
+ } catch (error) {
33
+ return {
34
+ status: "unavailable",
35
+ reason: error instanceof Error ? error.message : String(error)
36
+ };
37
+ }
38
+ }
@@ -1,6 +1,16 @@
1
+ import { ScheduleMissedFireEvent } from "../events/schedule-events.js";
2
+ import { resolveEffectiveScheduleFireAt } from "./schedule-jitter.js";
1
3
  import { collectDueScheduleOccurrences, getNextScheduleOccurrence } from "./schedule-occurrence.js";
2
4
  import { loadScheduleState, writeScheduleState } from "./storage-io.js";
3
- const SCHEDULE_LATE_GRACE_MILLISECONDS = 1000, MAX_SCHEDULE_BACKFILL_OCCURRENCES_PER_TICK = 256;
5
+ const SCHEDULE_LATE_GRACE_MILLISECONDS = 1000, MAX_SCHEDULE_BACKFILL_OCCURRENCES_PER_TICK = 256, MAX_MISSED_FIRE_COUNT_PER_TICK = MAX_SCHEDULE_BACKFILL_OCCURRENCES_PER_TICK;
6
+
7
+ class ScheduleStatePersistenceError extends Error {
8
+ scheduleId;
9
+ constructor(scheduleId, cause) {
10
+ super(`Failed to persist schedule "${scheduleId}" while processing its timer`, { cause });
11
+ this.scheduleId = scheduleId;
12
+ }
13
+ }
4
14
  export async function handleScheduleTimer(internals, entry, callbacks) {
5
15
  const state = await loadScheduleState(internals, entry.workflowId);
6
16
  if (!isCurrentActiveScheduleTimer(state, entry))
@@ -10,28 +20,43 @@ export async function handleScheduleTimer(internals, entry, callbacks) {
10
20
  const now = internals.options.getNow(), work = planScheduleTimerWork(state, entry, now);
11
21
  if (!work)
12
22
  return;
13
- nextState = await processScheduleTimerOccurrences(internals, state, work.occurrencesToProcess, now, callbacks, (processedState) => {
14
- nextState = processedState;
15
- });
16
- await writeScheduleState(internals, {
17
- ...nextState,
18
- updatedAt: now,
19
- nextFireAt: resolveNextScheduleFireAt(nextState, work, now)
23
+ if (work.occurrencesToProcess.length === 0) {
24
+ nextState = {
25
+ ...nextState,
26
+ updatedAt: now,
27
+ nextFireAt: resolveNextScheduleFireAt(nextState, work, now),
28
+ ...work.missedOccurrences && {
29
+ lastMissedFireAt: work.missedOccurrences.lastMissedFireAt,
30
+ missedFireCount: nextState.missedFireCount + work.missedOccurrences.count
31
+ }
32
+ };
33
+ await persistScheduleTimerState(internals, nextState);
34
+ if (work.missedOccurrences)
35
+ internals.engine.dispatchEvent(new ScheduleMissedFireEvent(nextState.id, work.missedOccurrences.count, work.missedOccurrences.windowStart, work.missedOccurrences.windowEnd));
36
+ return;
37
+ }
38
+ nextState = await processScheduleTimerOccurrences(internals, state, work, now, callbacks, {
39
+ onProcessedState: (processedState) => {
40
+ nextState = processedState;
41
+ }
20
42
  });
21
43
  } catch (error) {
44
+ if (error instanceof ScheduleStatePersistenceError)
45
+ throw error;
22
46
  await pauseScheduleAfterTimerFailure(internals, nextState, error);
23
47
  }
24
48
  }
25
49
  function isCurrentActiveScheduleTimer(state, entry) {
26
- return state?.status === "active" && state.nextFireAt === entry.fireAt;
50
+ return state?.status === "active" && state.nextFireAt !== null && resolveEffectiveScheduleFireAt(state, state.nextFireAt) === entry.fireAt;
27
51
  }
28
52
  function planScheduleTimerWork(state, entry, now) {
29
53
  const dueOccurrences = collectDueScheduleOccurrences(state, state.nextFireAt, Math.max(now, entry.fireAt), state.backfill ? MAX_SCHEDULE_BACKFILL_OCCURRENCES_PER_TICK : 2);
30
54
  if (dueOccurrences.length === 0)
31
55
  return null;
32
- if (!state.backfill && now - state.nextFireAt > SCHEDULE_LATE_GRACE_MILLISECONDS)
56
+ if (!state.backfill && now - entry.fireAt > SCHEDULE_LATE_GRACE_MILLISECONDS)
33
57
  return {
34
58
  dueOccurrences,
59
+ missedOccurrences: countMissedScheduleOccurrences(state, state.nextFireAt, now),
35
60
  occurrencesToProcess: [],
36
61
  skipMissedOccurrences: !0
37
62
  };
@@ -42,29 +67,68 @@ function planScheduleTimerWork(state, entry, now) {
42
67
  skipMissedOccurrences: !1
43
68
  };
44
69
  }
70
+ function countMissedScheduleOccurrences(state, firstDueAt, throughTimestamp) {
71
+ let count = 0, occurrence = firstDueAt, lastMissedFireAt = firstDueAt;
72
+ while (occurrence <= throughTimestamp && count < MAX_MISSED_FIRE_COUNT_PER_TICK) {
73
+ count += 1;
74
+ lastMissedFireAt = occurrence;
75
+ const nextOccurrence = getNextScheduleOccurrence(state, occurrence);
76
+ if (nextOccurrence <= occurrence)
77
+ throw Error(`Schedule "${state.id}" produced a non-advancing occurrence`);
78
+ occurrence = nextOccurrence;
79
+ }
80
+ const cappedBeforeThroughTimestamp = occurrence <= throughTimestamp;
81
+ return {
82
+ count,
83
+ lastMissedFireAt,
84
+ windowStart: firstDueAt,
85
+ windowEnd: cappedBeforeThroughTimestamp ? lastMissedFireAt : throughTimestamp
86
+ };
87
+ }
45
88
  function resolveNextScheduleFireAt(state, work, now) {
46
89
  if (work.skipMissedOccurrences)
47
- return getNextScheduleOccurrence(state, now);
90
+ return getNextScheduleOccurrence(state, work.missedOccurrences?.lastMissedFireAt ?? now);
48
91
  const anchorOccurrence = work.occurrencesToProcess.at(-1) ?? work.dueOccurrences.at(-1);
49
92
  return getNextScheduleOccurrence(state, anchorOccurrence);
50
93
  }
51
- async function processScheduleTimerOccurrences(internals, state, occurrencesToProcess, now, callbacks, onProcessedState) {
94
+ async function processScheduleTimerOccurrences(internals, state, work, now, callbacks, options) {
52
95
  let nextState = state;
53
- for (const occurrence of occurrencesToProcess) {
96
+ for (const [index, occurrence] of work.occurrencesToProcess.entries()) {
97
+ const finalOccurrence = index === work.occurrencesToProcess.length - 1;
54
98
  nextState = {
55
- ...await callbacks.applyScheduleOccurrence(nextState),
99
+ ...await callbacks.applyScheduleOccurrence(nextState, occurrence),
56
100
  lastFireAt: occurrence,
57
101
  updatedAt: now
58
102
  };
59
- onProcessedState(nextState);
60
- await writeScheduleState(internals, nextState, { includeTimer: !1 });
103
+ if (finalOccurrence)
104
+ nextState = {
105
+ ...nextState,
106
+ nextFireAt: resolveNextScheduleFireAt(nextState, work, now)
107
+ };
108
+ options.onProcessedState(nextState);
109
+ await persistScheduleTimerState(internals, nextState, { includeTimer: finalOccurrence });
61
110
  if (state.backfill && nextState.currentWorkflowId !== void 0) {
62
111
  nextState = await callbacks.settleBackfillScheduleState(nextState);
63
- onProcessedState(nextState);
112
+ if (finalOccurrence) {
113
+ nextState = {
114
+ ...nextState,
115
+ updatedAt: now,
116
+ nextFireAt: resolveNextScheduleFireAt(nextState, work, now)
117
+ };
118
+ await persistScheduleTimerState(internals, nextState);
119
+ }
120
+ options.onProcessedState(nextState);
64
121
  }
65
122
  }
66
123
  return nextState;
67
124
  }
125
+ async function persistScheduleTimerState(internals, state, options) {
126
+ try {
127
+ await writeScheduleState(internals, state, options);
128
+ } catch (error) {
129
+ throw new ScheduleStatePersistenceError(state.id, error);
130
+ }
131
+ }
68
132
  async function pauseScheduleAfterTimerFailure(internals, state, error) {
69
133
  const errorNow = internals.options.getNow(), pausedState = {
70
134
  ...state,
@@ -1,7 +1,8 @@
1
1
  import type { BatchOperation } from '../../storage/interface.ts';
2
2
  import type { PaginatedResult, ScheduleFilter, ScheduleOptions, ScheduleSpec, ScheduleState, ScheduleSummary, WorkflowState } from '../types.ts';
3
- import { ScheduleHandle } from './handles.ts';
4
3
  import type { EngineInternals } from './internals.ts';
4
+ import { ScheduleHandle } from './schedule-handle.ts';
5
+ export { startScheduledRun } from './schedule-run.ts';
5
6
  export { handleScheduleTimer } from './schedule-timer.ts';
6
7
  export type RefreshedScheduleState = {
7
8
  state: ScheduleState;
@@ -15,20 +16,22 @@ export type ScheduleCallbacks = {
15
16
  cancelWorkflow: (workflowId: string) => Promise<void>;
16
17
  getWorkflowResult: (workflowId: string) => Promise<unknown>;
17
18
  refreshScheduledWorkflowState: (state: ScheduleState) => Promise<RefreshedScheduleState>;
18
- startScheduledRun: (state: ScheduleState) => Promise<string>;
19
- applyScheduleOccurrence: (state: ScheduleState) => Promise<ScheduleState>;
19
+ startScheduledRun: (state: ScheduleState, occurrence?: number) => Promise<string>;
20
+ applyScheduleOccurrence: (state: ScheduleState, occurrence?: number) => Promise<ScheduleState>;
20
21
  settleBackfillScheduleState: (state: ScheduleState) => Promise<ScheduleState>;
21
22
  flushQueuedInlineWorkflowStartsDirectly: () => Promise<void>;
23
+ failWorkflow: (workflowId: string, error: Error) => Promise<void>;
24
+ handleCleanupError: (source: string, error: unknown, workflowId: string) => void;
22
25
  };
23
26
  export declare function schedule(internals: EngineInternals, type: string, input: unknown, spec: string | ScheduleSpec, options?: ScheduleOptions): Promise<ScheduleHandle>;
24
27
  export declare function listSchedules(internals: EngineInternals, filter?: ScheduleFilter): Promise<PaginatedResult<ScheduleSummary>>;
28
+ export declare function recoverOrphanedScheduleTimers(internals: EngineInternals): Promise<void>;
25
29
  export declare function toScheduleSummary(state: ScheduleState): ScheduleSummary;
26
30
  export declare function pauseSchedule(internals: EngineInternals, scheduleId: string): Promise<void>;
27
31
  export declare function resumeSchedule(internals: EngineInternals, scheduleId: string): Promise<void>;
28
32
  export declare function cancelSchedule(internals: EngineInternals, scheduleId: string): Promise<void>;
29
33
  export declare function updateSchedule(internals: EngineInternals, scheduleId: string, newSpec: string | ScheduleSpec): Promise<void>;
30
34
  export declare function refreshScheduledWorkflowState(internals: EngineInternals, state: ScheduleState, callbacks: Pick<ScheduleCallbacks, 'loadWorkflowState'>): Promise<RefreshedScheduleState>;
31
- export declare function startScheduledRun(_internals: EngineInternals, state: ScheduleState, callbacks: Pick<ScheduleCallbacks, 'startWorkflow'>): Promise<string>;
32
- export declare function applyScheduleOccurrence(_internals: EngineInternals, state: ScheduleState, callbacks: ScheduleCallbacks): Promise<ScheduleState>;
35
+ export declare function applyScheduleOccurrence(_internals: EngineInternals, state: ScheduleState, callbacks: ScheduleCallbacks, occurrence?: number): Promise<ScheduleState>;
33
36
  export declare function settleBackfillScheduleState(internals: EngineInternals, state: ScheduleState, callbacks: Pick<ScheduleCallbacks, 'flushQueuedInlineWorkflowStartsDirectly' | 'refreshScheduledWorkflowState'>): Promise<ScheduleState>;
34
37
  export declare function handleScheduledWorkflowTerminal(internals: EngineInternals, workflowId: string, callbacks: ScheduleCallbacks): Promise<void>;
@@ -1,7 +1,8 @@
1
1
  import { KEYS } from "../../storage/interface.js";
2
- import { decode, encode } from "../codec.js";
2
+ import { decode } from "../codec.js";
3
3
  import { WorkflowNotRegisteredError } from "./errors.js";
4
- import { ScheduleHandle } from "./handles.js";
4
+ import { ScheduleHandle } from "./schedule-handle.js";
5
+ import { resolveEffectiveScheduleFireAt } from "./schedule-jitter.js";
5
6
  import { getNextScheduleOccurrence } from "./schedule-occurrence.js";
6
7
  import {
7
8
  clearScheduleCurrentWorkflow,
@@ -18,6 +19,7 @@ import {
18
19
  normalizeScheduleOptions,
19
20
  normalizeScheduleSpec
20
21
  } from "./validation/schedule.js";
22
+ export { startScheduledRun } from "./schedule-run.js";
21
23
  export { handleScheduleTimer } from "./schedule-timer.js";
22
24
  export async function schedule(internals, type, input, spec, options) {
23
25
  if (!internals.registrations.has(type))
@@ -37,9 +39,11 @@ export async function schedule(internals, type, input, spec, options) {
37
39
  status: "active",
38
40
  overlap: normalizedOptions.overlap,
39
41
  backfill: normalizedOptions.backfill,
42
+ ...normalizedOptions.jitterMs !== void 0 && { jitterMs: normalizedOptions.jitterMs },
40
43
  createdAt: now,
41
44
  updatedAt: now,
42
45
  nextFireAt: getNextScheduleOccurrence({ ...cadenceFields, createdAt: now }, now),
46
+ missedFireCount: 0,
43
47
  queuedRuns: 0
44
48
  };
45
49
  await writeScheduleState(internals, state);
@@ -61,6 +65,28 @@ export async function listSchedules(internals, filter) {
61
65
  }
62
66
  return paginateScheduleSummaries(items, normalizedFilter);
63
67
  }
68
+ export async function recoverOrphanedScheduleTimers(internals) {
69
+ for await (const [key, value] of internals.storage.scan("schedule:")) {
70
+ if (key.slice(9).includes(":"))
71
+ continue;
72
+ const state = decodeScheduleState(value);
73
+ if (!isActiveScheduleWithTimer(state))
74
+ continue;
75
+ if (await hasCurrentScheduleTimer(internals, state))
76
+ continue;
77
+ await writeScheduleState(internals, state);
78
+ }
79
+ }
80
+ function isActiveScheduleWithTimer(state) {
81
+ return state?.status === "active" && state.nextFireAt !== null;
82
+ }
83
+ async function hasCurrentScheduleTimer(internals, state) {
84
+ const timerKey = KEYS.scheduleTick(resolveEffectiveScheduleFireAt(state, state.nextFireAt), state.id);
85
+ if (await internals.storage.get(timerKey) === null)
86
+ return !1;
87
+ const timerIndexBytes = await internals.storage.get(`timer-idx:${createScheduleTimerId(state.id)}`);
88
+ return timerIndexBytes !== null && decode(timerIndexBytes) === timerKey;
89
+ }
64
90
  export function toScheduleSummary(state) {
65
91
  const { input: _input, ...summary } = state;
66
92
  return summary;
@@ -107,10 +133,7 @@ export async function cancelSchedule(internals, scheduleId) {
107
133
  await writeScheduleState(internals, updatedState, { includeTimer: !1 });
108
134
  }
109
135
  export async function updateSchedule(internals, scheduleId, newSpec) {
110
- const normalizedScheduleId = coerceScheduleId(scheduleId, "scheduleId"), normalizedSpec = normalizeScheduleSpec(newSpec), state = await requireScheduleState(internals, normalizedScheduleId);
111
- if (state.status === "active")
112
- await internals.scheduler.cancel(createScheduleTimerId(normalizedScheduleId), normalizedScheduleId);
113
- const now = internals.options.getNow(), {
136
+ const normalizedScheduleId = coerceScheduleId(scheduleId, "scheduleId"), normalizedSpec = normalizeScheduleSpec(newSpec), state = await requireScheduleState(internals, normalizedScheduleId), now = internals.options.getNow(), {
114
137
  cronExpression: _droppedCron,
115
138
  intervalMs: _droppedInterval,
116
139
  ...stateWithoutCadence
@@ -123,11 +146,15 @@ export async function updateSchedule(internals, scheduleId, newSpec) {
123
146
  };
124
147
  await writeScheduleState(internals, updatedState, { includeTimer: state.status === "active" });
125
148
  }
149
+ function scheduledRunOccupiesSlot(currentWorkflowState) {
150
+ const status = currentWorkflowState?.status;
151
+ return status === "running" || status === "pending" || status === "suspended";
152
+ }
126
153
  export async function refreshScheduledWorkflowState(internals, state, callbacks) {
127
154
  if (!state.currentWorkflowId)
128
155
  return { state, currentWorkflowState: null };
129
156
  const currentWorkflowState = await callbacks.loadWorkflowState(state.currentWorkflowId);
130
- if (currentWorkflowState?.status === "running" || currentWorkflowState?.status === "pending")
157
+ if (scheduledRunOccupiesSlot(currentWorkflowState))
131
158
  return { state, currentWorkflowState };
132
159
  await internals.storage.delete(KEYS.scheduleRun(state.currentWorkflowId));
133
160
  return {
@@ -135,36 +162,34 @@ export async function refreshScheduledWorkflowState(internals, state, callbacks)
135
162
  currentWorkflowState: currentWorkflowState ?? null
136
163
  };
137
164
  }
138
- export async function startScheduledRun(_internals, state, callbacks) {
139
- const workflowId = crypto.randomUUID(), scheduleRunOperations = state.overlap === "allow" ? void 0 : [{ type: "put", key: KEYS.scheduleRun(workflowId), value: encode(state.id) }];
140
- await callbacks.startWorkflow(state.workflowType, state.input, { id: workflowId }, scheduleRunOperations);
141
- return workflowId;
142
- }
143
165
  function hasActiveScheduledWorkflow(currentWorkflowState) {
144
- return currentWorkflowState?.status === "running" || currentWorkflowState?.status === "pending";
166
+ return scheduledRunOccupiesSlot(currentWorkflowState);
145
167
  }
146
- async function applyBlockedScheduleOccurrence(state, hasActiveWorkflow, callbacks) {
168
+ async function applyBlockedScheduleOccurrence(state, hasActiveWorkflow, callbacks, occurrence) {
147
169
  if (!hasActiveWorkflow)
148
- return { ...state, currentWorkflowId: await callbacks.startScheduledRun(state) };
170
+ return { ...state, currentWorkflowId: await callbacks.startScheduledRun(state, occurrence) };
149
171
  if (state.overlap === "cancel-running") {
150
172
  if (state.currentWorkflowId) {
151
173
  callbacks.getWorkflowResult(state.currentWorkflowId).catch(() => {});
152
174
  await callbacks.cancelWorkflow(state.currentWorkflowId);
153
175
  }
154
176
  const stateForStart = clearScheduleCurrentWorkflow(state);
155
- return { ...state, currentWorkflowId: await callbacks.startScheduledRun(stateForStart) };
177
+ return {
178
+ ...state,
179
+ currentWorkflowId: await callbacks.startScheduledRun(stateForStart, occurrence)
180
+ };
156
181
  }
157
182
  if (state.overlap === "queue")
158
183
  return { ...state, queuedRuns: state.queuedRuns + 1 };
159
184
  return state;
160
185
  }
161
- export async function applyScheduleOccurrence(_internals, state, callbacks) {
186
+ export async function applyScheduleOccurrence(_internals, state, callbacks, occurrence) {
162
187
  const { state: refreshedState, currentWorkflowState } = await callbacks.refreshScheduledWorkflowState(state), hasActiveWorkflow = hasActiveScheduledWorkflow(currentWorkflowState);
163
188
  if (refreshedState.overlap === "allow") {
164
- await callbacks.startScheduledRun(refreshedState);
189
+ await callbacks.startScheduledRun(refreshedState, occurrence);
165
190
  return refreshedState;
166
191
  }
167
- return applyBlockedScheduleOccurrence(refreshedState, hasActiveWorkflow, callbacks);
192
+ return applyBlockedScheduleOccurrence(refreshedState, hasActiveWorkflow, callbacks, occurrence);
168
193
  }
169
194
  export async function settleBackfillScheduleState(internals, state, callbacks) {
170
195
  if (!state.currentWorkflowId)
@@ -0,0 +1,4 @@
1
+ import type { ListFilter, SearchAttributeValue } from '../types.ts';
2
+ import type { EngineInternals } from './internals.ts';
3
+ export declare function readSearchAttributesForStates(internals: EngineInternals, stateBytesList: readonly (Uint8Array | null)[], filter: ListFilter | undefined): Promise<Map<string, Record<string, SearchAttributeValue> | null>>;
4
+ export declare function readSearchAttributesForFilter(internals: EngineInternals, workflowId: string, filter: ListFilter | undefined): Promise<Record<string, SearchAttributeValue> | null>;
@@ -0,0 +1,17 @@
1
+ import { KEYS } from "../../storage/interface.js";
2
+ import { decodeSearchAttributeRecord, listFilterHasAttributeFilters } from "./state-utilities.js";
3
+ import { decodeWorkflowState } from "./validation.js";
4
+ export async function readSearchAttributesForStates(internals, stateBytesList, filter) {
5
+ const searchAttributesByWorkflowId = new Map;
6
+ if (!listFilterHasAttributeFilters(filter))
7
+ return searchAttributesByWorkflowId;
8
+ const states = stateBytesList.flatMap((stateBytes) => stateBytes === null ? [] : [decodeWorkflowState(stateBytes)]), attributeBytesList = await Promise.all(states.map((state) => internals.storage.get(KEYS.attribute(state.id))));
9
+ for (let index = 0;index < states.length; index += 1)
10
+ searchAttributesByWorkflowId.set(states[index].id, decodeSearchAttributeRecord(attributeBytesList[index] ?? null));
11
+ return searchAttributesByWorkflowId;
12
+ }
13
+ export async function readSearchAttributesForFilter(internals, workflowId, filter) {
14
+ if (!listFilterHasAttributeFilters(filter))
15
+ return null;
16
+ return decodeSearchAttributeRecord(await internals.storage.get(KEYS.attribute(workflowId)));
17
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Best-effort, warn-only detection of a SECOND engine instance writing to the
3
+ * same durable store — a smoke alarm for singleton-deployment misconfiguration
4
+ * (an autoscaler accidentally set above one replica, or overlapping rolling
5
+ * deploys), NOT a correctness mechanism.
6
+ *
7
+ * **This is liveness, not fencing.** Weft's supported model is one engine process
8
+ * per durable store (see the recovery-and-deploys guide); fenced ownership is a
9
+ * future `MultiEngine` capability that does not exist yet. This detector never
10
+ * blocks boot, gates recovery, refuses a write, or claims ownership. It only
11
+ * observes whether another instance's heartbeat is *advancing while this instance
12
+ * is also running* and emits a warning if so. It does not prevent duplicate
13
+ * execution — infrastructure-level enforcement (`replicas: 1` + a `Recreate`
14
+ * deploy strategy, or a single systemd unit) is the real control.
15
+ *
16
+ * **Why liveness, not a boot check.** A boot-time check cannot distinguish a
17
+ * rolling-deploy handoff from a genuine second instance: both leave a recent
18
+ * heartbeat record in the store. Only observing a *foreign* heartbeat advance
19
+ * across several of our own ticks separates a live peer (autoscaling=2 → both
20
+ * heartbeats advance forever → both warn) from a dead-but-recent predecessor (a
21
+ * clean `Recreate` deploy → old heartbeat never advances after handoff → quiet).
22
+ *
23
+ * **Advance is measured by sequence, not wall clock.** Each heartbeat carries a
24
+ * per-instance monotonic `sequence`; a peer counts as advancing only when its
25
+ * `sequence` grows between two of *our* ticks. A peer's sequence cannot increase
26
+ * unless it is alive and ticking in our own time frame, so detection never
27
+ * compares clocks across hosts — it stays correct even if a peer's clock is
28
+ * frozen, skewed, or stepped backward. `heartbeatAt` exists only for the boot
29
+ * staleness sweep that garbage-collects long-dead instances' keys.
30
+ *
31
+ * Each engine writes its own heartbeat under `liveness:<instanceId>` and scans
32
+ * the `liveness:` prefix to observe peers. Per-instance keys (not one shared,
33
+ * clobbered key) keep every heartbeat independently observable and the sequence
34
+ * monotonic per writer.
35
+ *
36
+ * @module core/engine/second-instance-detector
37
+ */
38
+ import { type Storage } from '../../storage/interface.ts';
39
+ import type { EngineCleanupIntervalDisposalTracker } from './engine-leak-warnings.ts';
40
+ /** Options for {@link createSecondInstanceDetector}. */
41
+ export type SecondInstanceDetectorOptions = {
42
+ storage: Storage;
43
+ /** This engine's unique instance id. */
44
+ instanceId: string;
45
+ /** Wall-clock source (ms), injected so tests can advance time deterministically. */
46
+ getNow: () => number;
47
+ /**
48
+ * Heartbeat interval in ms. The staleness window is derived from this, so the
49
+ * interval also sets how long a deploy overlap must last before it warns.
50
+ */
51
+ intervalMs: number;
52
+ /**
53
+ * Emit a warning. Defaults to `process.emitWarning(message, WARNING_NAME)`,
54
+ * so the emitted `Warning.name` is `WeftSecondInstanceWarning` and consumers
55
+ * can filter on `warning.name` rather than scraping the message. Injected for
56
+ * tests; the seam is message-only because the name is a fixed constant.
57
+ */
58
+ warn?: (message: string) => void;
59
+ };
60
+ /**
61
+ * The `name` of the emitted warning. A stable, filterable identifier on the
62
+ * `Warning` object — consumers subscribe to the process `warning` event and
63
+ * match `warning.name === WARNING_NAME` (see the singleton-deployment guide).
64
+ */
65
+ export declare const SECOND_INSTANCE_WARNING_NAME = "WeftSecondInstanceWarning";
66
+ /**
67
+ * A running detector. `tick()` runs one heartbeat round (exposed for tests and
68
+ * driven by an interval in production); `stop()` clears the interval and
69
+ * best-effort removes this instance's heartbeat so the next boot starts quiet.
70
+ */
71
+ export type SecondInstanceDetector = {
72
+ /** Run one heartbeat round: observe peers, then write our own heartbeat. */
73
+ tick(): Promise<void>;
74
+ /** Stop the interval and best-effort delete this instance's heartbeat key. */
75
+ stop(): Promise<void>;
76
+ };
77
+ /**
78
+ * Create a best-effort second-instance detector. Does not start an interval
79
+ * itself — the engine owns timer lifecycle so disposal can clear it through the
80
+ * same path as its other intervals. Call {@link SecondInstanceDetector.tick} on
81
+ * an interval and {@link SecondInstanceDetector.stop} on dispose.
82
+ */
83
+ export declare function createSecondInstanceDetector(options: SecondInstanceDetectorOptions): SecondInstanceDetector;
84
+ /**
85
+ * Build the `setInterval` callback that drives a detector tick. `resolveDetector`
86
+ * returns the live detector, or `null` when the engine has been garbage-collected
87
+ * or disposed. When it returns `null` the tick SELF-CLEARS its own interval (via
88
+ * `tracker.secondInstanceDetectionInterval`) and returns — mirroring
89
+ * {@link createCleanupIntervalTick}. This is the prompt cleanup path: a leaked
90
+ * engine's first post-GC tick clears the timer immediately, rather than relying
91
+ * on the `FinalizationRegistry` backstop, whose callbacks are not guaranteed to
92
+ * run promptly (or at all). Extracted so the skip/clear guard is directly
93
+ * testable without a timer. A tick failure is swallowed: the detector is a smoke
94
+ * alarm, never a correctness path, so it must not surface as an unhandled rejection.
95
+ */
96
+ export declare function createSecondInstanceDetectionTick(resolveDetector: () => SecondInstanceDetector | null, tracker: EngineCleanupIntervalDisposalTracker): () => void;
@@ -0,0 +1,108 @@
1
+ import { KEYS } from "../../storage/interface.js";
2
+ const textEncoder = new TextEncoder, textDecoder = new TextDecoder;
3
+ function encodeHeartbeat(heartbeat) {
4
+ return textEncoder.encode(JSON.stringify(heartbeat));
5
+ }
6
+ async function bestEffort(operation) {
7
+ try {
8
+ await operation();
9
+ } catch {}
10
+ }
11
+ function isUsableHeartbeat(candidate) {
12
+ const { instanceId, heartbeatAt, sequence } = candidate;
13
+ return typeof instanceId === "string" && instanceId.length > 0 && typeof heartbeatAt === "number" && Number.isFinite(heartbeatAt) && typeof sequence === "number" && Number.isInteger(sequence) && sequence >= 0;
14
+ }
15
+ function decodeHeartbeat(raw) {
16
+ let parsed;
17
+ try {
18
+ parsed = JSON.parse(textDecoder.decode(raw));
19
+ } catch {
20
+ return null;
21
+ }
22
+ if (typeof parsed !== "object" || parsed === null)
23
+ return null;
24
+ const candidate = parsed;
25
+ return isUsableHeartbeat(candidate) ? candidate : null;
26
+ }
27
+ export const SECOND_INSTANCE_WARNING_NAME = "WeftSecondInstanceWarning";
28
+ const STALE_SWEEP_WINDOW_MULTIPLE = 10, ADVANCE_TICKS_BEFORE_WARN = 2;
29
+ export function createSecondInstanceDetector(options) {
30
+ const { storage, instanceId, getNow, intervalMs } = options, warn = options.warn ?? ((message) => process.emitWarning(message, SECOND_INSTANCE_WARNING_NAME)), stalenessWindowMs = intervalMs * (ADVANCE_TICKS_BEFORE_WARN + 1);
31
+ let sequence = 0, swept = !1, stopped = !1;
32
+ const observed = new Map, warnedInstanceIds = new Set;
33
+ async function readPeers() {
34
+ const peers = [];
35
+ for await (const [key, value] of storage.scan(KEYS.livenessPrefix())) {
36
+ const heartbeat = decodeHeartbeat(value);
37
+ if (heartbeat !== null && heartbeat.instanceId !== instanceId)
38
+ peers.push({ key, heartbeat });
39
+ }
40
+ return peers;
41
+ }
42
+ async function sweepStaleHeartbeats(peers, now) {
43
+ const staleBefore = now - stalenessWindowMs * STALE_SWEEP_WINDOW_MULTIPLE;
44
+ for (const peer of peers)
45
+ if (peer.heartbeat.heartbeatAt < staleBefore)
46
+ await bestEffort(() => storage.delete(peer.key));
47
+ }
48
+ function evaluatePeers(peers) {
49
+ const seenThisTick = new Set;
50
+ for (const { key, heartbeat } of peers) {
51
+ if (KEYS.liveness(heartbeat.instanceId) !== key)
52
+ continue;
53
+ seenThisTick.add(heartbeat.instanceId);
54
+ const previous = observed.get(heartbeat.instanceId), advances = previous !== void 0 && heartbeat.sequence > previous.sequence ? previous.advances + 1 : 0;
55
+ observed.set(heartbeat.instanceId, { sequence: heartbeat.sequence, advances });
56
+ if (advances >= ADVANCE_TICKS_BEFORE_WARN && !warnedInstanceIds.has(heartbeat.instanceId)) {
57
+ warnedInstanceIds.add(heartbeat.instanceId);
58
+ warn(`another engine instance (${heartbeat.instanceId}) is writing to this durable store. Weft supports one engine process per store; running two can cause duplicate workflow execution. Enforce a single instance at the infrastructure layer (for example, one replica with a Recreate deploy strategy).`);
59
+ }
60
+ }
61
+ for (const knownInstanceId of Array.from(observed.keys()))
62
+ if (!seenThisTick.has(knownInstanceId))
63
+ observed.delete(knownInstanceId);
64
+ }
65
+ let tickInFlight = !1;
66
+ async function tick() {
67
+ if (stopped || tickInFlight)
68
+ return;
69
+ tickInFlight = !0;
70
+ let wroteHeartbeat = !1;
71
+ try {
72
+ const now = getNow(), peers = await readPeers();
73
+ if (!swept) {
74
+ swept = !0;
75
+ await sweepStaleHeartbeats(peers, now);
76
+ }
77
+ evaluatePeers(peers);
78
+ if (stopped)
79
+ return;
80
+ sequence += 1;
81
+ const heartbeat = { instanceId, heartbeatAt: now, sequence };
82
+ wroteHeartbeat = !0;
83
+ await bestEffort(() => storage.put(KEYS.liveness(instanceId), encodeHeartbeat(heartbeat)));
84
+ } finally {
85
+ tickInFlight = !1;
86
+ if (wroteHeartbeat && stopped)
87
+ await bestEffort(() => storage.delete(KEYS.liveness(instanceId)));
88
+ }
89
+ }
90
+ async function stop() {
91
+ stopped = !0;
92
+ await bestEffort(() => storage.delete(KEYS.liveness(instanceId)));
93
+ }
94
+ return { tick, stop };
95
+ }
96
+ export function createSecondInstanceDetectionTick(resolveDetector, tracker) {
97
+ return function secondInstanceDetectionTick() {
98
+ const detector = resolveDetector();
99
+ if (detector === null) {
100
+ if (tracker.secondInstanceDetectionInterval !== null) {
101
+ clearInterval(tracker.secondInstanceDetectionInterval);
102
+ tracker.secondInstanceDetectionInterval = null;
103
+ }
104
+ return;
105
+ }
106
+ detector.tick().catch(() => {});
107
+ };
108
+ }
@@ -1,3 +1,4 @@
1
+ import type { BatchOperation } from '../../storage/interface.ts';
1
2
  import type { ComposedWorkflowInterceptor } from '../interceptor.ts';
2
3
  import type { SignalDeliveryOptions, WorkflowState } from '../types.ts';
3
4
  import type { EngineInternals } from './internals.ts';
@@ -31,8 +32,45 @@ export type ConsumedSignalResult = {
31
32
  export declare function signal(internals: EngineInternals, workflowId: string, name: string, payload: unknown, callbacks: SignalCallbacks, options?: SignalDeliveryOptions): Promise<void>;
32
33
  export declare function releaseSignalWaiter(internals: EngineInternals, workflowId: string, waiterKey: string, expectedResolve?: () => void): void;
33
34
  export declare function bufferSignalPayloads(internals: EngineInternals, workflowId: string, deliveries: BufferedSignalDelivery[], callbacks: SignalCallbacks, defaultOptions?: SignalDeliveryOptions): Promise<void>;
35
+ /**
36
+ * Build the durable operations for a single keyed signal so it can be folded
37
+ * into a workflow's create batch by {@link startOrSignal}. Writes the same pair
38
+ * the live signal path writes — the `sig:` payload (consumed on first drive by
39
+ * `processWaitSignalOperation`) and the `sigres:` accepted-response marker — so a
40
+ * concurrent caller that falls back to the standard signal path dedups against
41
+ * the SAME `signalId`. The accepted-response marker is consumption-independent:
42
+ * even after the winning run consumes the `sig:` payload, a late loser finds the
43
+ * `sigres:` key and short-circuits instead of re-delivering, which is what
44
+ * guarantees "one signal per signalId" across the create and signal paths.
45
+ *
46
+ * Returns both the put operations and the CAS condition. The condition gates on
47
+ * the `sigres:` accepted-response marker (the dedup identity, keyed by signalId
48
+ * alone), NOT the `sig:` payload key: the FIFO sort-class (#458) makes the
49
+ * start-signal's `sig:` key (class `0`) differ from the live signal path's (class
50
+ * `1`), so a `sig:`-keyed CAS would no longer collide with a pre-buffered signal
51
+ * of the same signalId and would buffer a second copy. The class-independent
52
+ * `sigres:` marker is what both paths share, so gating on it preserves "one
53
+ * signal per signalId" across the create and signal paths.
54
+ */
55
+ export declare function buildCreateBatchSignalOperations(internals: EngineInternals, workflowId: string, signalName: string, payload: unknown, signalId: string): {
56
+ operations: BatchOperation[];
57
+ condition: {
58
+ key: string;
59
+ expectedValue: null;
60
+ };
61
+ };
34
62
  export declare function hasBufferedSignal(internals: EngineInternals, workflowId: string, signalName: string): Promise<boolean>;
35
63
  export declare function consumeSignal(internals: EngineInternals, workflowId: string, signalName: string): Promise<ConsumedSignalResult>;
64
+ export declare function consumeSignalWithAtomicWorkflowCommit(internals: EngineInternals, workflowId: string, signalName: string): Promise<ConsumedSignalResult>;
65
+ /**
66
+ * Read the first buffered signal payload WITHOUT deleting it. Use when a caller
67
+ * must check for a buffered signal but might not end up consuming it — e.g. a
68
+ * `ctx.race` / `ctx.all` wait-signal branch that could lose, where a destructive
69
+ * {@link consumeSignal} on the losing path would silently drop the signal. The
70
+ * winner calls {@link consumeSignalWithAtomicWorkflowCommit} to stage the
71
+ * durable delete into the checkpoint batch.
72
+ */
73
+ export declare function peekSignal(internals: EngineInternals, workflowId: string, signalName: string): Promise<ConsumedSignalResult>;
36
74
  /** Register a waiter key in a workflow-keyed reverse index. */
37
75
  export declare function trackWaiterKey(reverseIndex: Map<string, TrackedWaiterKeys>, workflowId: string, waiterKey: string): void;
38
76
  /** Remove a waiter key from a workflow-keyed reverse index. */