@lostgradient/weft 0.3.0 → 0.5.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 (510) hide show
  1. package/README.md +79 -24
  2. package/dist/cli/conformance.js +7 -1
  3. package/dist/cli/generated/operation-client.generated.d.ts +28 -13
  4. package/dist/cli/generated/operation-client.generated.js +1 -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 +79 -78
  13. package/dist/client/handle-delegation.d.ts +3 -2
  14. package/dist/client/handle-delegation.js +3 -1
  15. package/dist/client/http-client-requests.d.ts +2 -1
  16. package/dist/client/http-client-requests.js +7 -0
  17. package/dist/client/http-client.d.ts +9 -6
  18. package/dist/client/http-client.js +10 -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 +61 -5
  23. package/dist/client/local-handles.d.ts +22 -0
  24. package/dist/client/local-handles.js +23 -0
  25. package/dist/client/local.d.ts +6 -3
  26. package/dist/client/local.js +12 -26
  27. package/dist/client/start-body.d.ts +4 -3
  28. package/dist/core/byte-arrays.d.ts +5 -0
  29. package/dist/core/byte-arrays.js +5 -0
  30. package/dist/core/checkpoint/lifecycle.d.ts +3 -0
  31. package/dist/core/checkpoint/lifecycle.js +9 -4
  32. package/dist/core/checkpoint/serialization.js +12 -5
  33. package/dist/core/codec/extension-codec.d.ts +16 -0
  34. package/dist/core/codec/extension-codec.js +100 -6
  35. package/dist/core/context/activity-retry-state.d.ts +38 -0
  36. package/dist/core/context/activity-retry-state.js +89 -0
  37. package/dist/core/context/activity-schedule-to-close.d.ts +159 -0
  38. package/dist/core/context/activity-schedule-to-close.js +69 -0
  39. package/dist/core/context/child-workflow-pipe.d.ts +5 -5
  40. package/dist/core/context/child-workflow-pipe.js +15 -2
  41. package/dist/core/context/context-presence.d.ts +19 -0
  42. package/dist/core/context/context-presence.js +13 -0
  43. package/dist/core/context/durable-operations.d.ts +25 -0
  44. package/dist/core/context/durable-operations.js +64 -0
  45. package/dist/core/context/index.d.ts +9 -5
  46. package/dist/core/context/index.js +17 -12
  47. package/dist/core/context/internals.d.ts +20 -0
  48. package/dist/core/context/internals.js +3 -1
  49. package/dist/core/context/operation-request.d.ts +35 -0
  50. package/dist/core/context/parallel-cache-entry.d.ts +14 -5
  51. package/dist/core/context/run-operation.d.ts +6 -1
  52. package/dist/core/context/run-operation.js +41 -87
  53. package/dist/core/context/session-state.js +7 -8
  54. package/dist/core/context/speculative-child.js +2 -0
  55. package/dist/core/context/types.d.ts +15 -0
  56. package/dist/core/context/version-patching.d.ts +8 -0
  57. package/dist/core/context/version-patching.js +40 -0
  58. package/dist/core/context/workflow-logger.d.ts +98 -0
  59. package/dist/core/context/workflow-logger.js +68 -0
  60. package/dist/core/engine/activity-heartbeat-tracking.d.ts +64 -0
  61. package/dist/core/engine/activity-heartbeat-tracking.js +42 -0
  62. package/dist/core/engine/activity-per-attempt-timeout.d.ts +46 -0
  63. package/dist/core/engine/activity-per-attempt-timeout.js +36 -0
  64. package/dist/core/engine/activity-reconciliation.d.ts +1 -0
  65. package/dist/core/engine/activity-reconciliation.js +12 -1
  66. package/dist/core/engine/activity-resolution.d.ts +23 -0
  67. package/dist/core/engine/activity-resolution.js +37 -0
  68. package/dist/core/engine/aggregate.js +66 -30
  69. package/dist/core/engine/async-activity-completion.d.ts +23 -6
  70. package/dist/core/engine/async-activity-completion.js +53 -12
  71. package/dist/core/engine/attributes-tags.js +9 -4
  72. package/dist/core/engine/bulk-operations-purge.d.ts +26 -0
  73. package/dist/core/engine/bulk-operations-purge.js +38 -3
  74. package/dist/core/engine/bulk-operations-shared.d.ts +2 -0
  75. package/dist/core/engine/bulk-operations-shared.js +8 -2
  76. package/dist/core/engine/bulk-operations.d.ts +4 -1
  77. package/dist/core/engine/bulk-operations.js +235 -24
  78. package/dist/core/engine/callback-creators-bundles.d.ts +9 -0
  79. package/dist/core/engine/callback-creators-bundles.js +30 -2
  80. package/dist/core/engine/callback-creators-core.js +6 -4
  81. package/dist/core/engine/callback-creators-router.js +10 -0
  82. package/dist/core/engine/callback-creators-schedule.d.ts +2 -2
  83. package/dist/core/engine/callback-creators-schedule.js +12 -8
  84. package/dist/core/engine/candidate-read-batching.d.ts +6 -0
  85. package/dist/core/engine/candidate-read-batching.js +1 -0
  86. package/dist/core/engine/checkpoint-io.js +61 -25
  87. package/dist/core/engine/checkpoint-reads.js +3 -2
  88. package/dist/core/engine/checkpoint-replay.d.ts +20 -0
  89. package/dist/core/engine/checkpoint-replay.js +242 -0
  90. package/dist/core/engine/checkpoint-side-effects.d.ts +23 -0
  91. package/dist/core/engine/checkpoint-side-effects.js +35 -0
  92. package/dist/core/engine/child-workflow-cancellation.d.ts +10 -0
  93. package/dist/core/engine/child-workflow-cancellation.js +40 -0
  94. package/dist/core/engine/child-workflow.js +27 -4
  95. package/dist/core/engine/condition-waiters.d.ts +14 -0
  96. package/dist/core/engine/condition-waiters.js +5 -0
  97. package/dist/core/engine/construction.d.ts +3 -0
  98. package/dist/core/engine/construction.js +21 -8
  99. package/dist/core/engine/coordination-branch-executors.d.ts +70 -0
  100. package/dist/core/engine/coordination-branch-executors.js +107 -0
  101. package/dist/core/engine/deferred-consume-envelope.d.ts +59 -0
  102. package/dist/core/engine/deferred-consume-envelope.js +14 -0
  103. package/dist/core/engine/disposal.js +13 -0
  104. package/dist/core/engine/engine-create-types.d.ts +14 -4
  105. package/dist/core/engine/engine-internal-types.d.ts +26 -3
  106. package/dist/core/engine/errors.d.ts +47 -0
  107. package/dist/core/engine/errors.js +20 -0
  108. package/dist/core/engine/event-log-compaction.d.ts +8 -0
  109. package/dist/core/engine/event-log-compaction.js +20 -2
  110. package/dist/core/engine/fenced-write.d.ts +65 -0
  111. package/dist/core/engine/fenced-write.js +71 -0
  112. package/dist/core/engine/finalizer-state.d.ts +28 -0
  113. package/dist/core/engine/finalizer-state.js +16 -0
  114. package/dist/core/engine/handles.d.ts +8 -0
  115. package/dist/core/engine/index.d.ts +34 -16
  116. package/dist/core/engine/index.js +136 -9
  117. package/dist/core/engine/inline-parking.js +3 -2
  118. package/dist/core/engine/internals.d.ts +107 -3
  119. package/dist/core/engine/lease-codec.d.ts +35 -0
  120. package/dist/core/engine/lease-codec.js +32 -0
  121. package/dist/core/engine/lease-deposition.d.ts +66 -0
  122. package/dist/core/engine/lease-deposition.js +10 -0
  123. package/dist/core/engine/lease-errors.d.ts +99 -0
  124. package/dist/core/engine/lease-errors.js +30 -0
  125. package/dist/core/engine/lease-manager.d.ts +114 -0
  126. package/dist/core/engine/lease-manager.js +135 -0
  127. package/dist/core/engine/lifecycle/persist.js +10 -32
  128. package/dist/core/engine/lifecycle/recovered-services.d.ts +8 -6
  129. package/dist/core/engine/lifecycle/recovered-services.js +18 -3
  130. package/dist/core/engine/lifecycle/resume.js +16 -9
  131. package/dist/core/engine/lifecycle/shared.d.ts +8 -1
  132. package/dist/core/engine/lifecycle/start-batch.d.ts +1 -1
  133. package/dist/core/engine/lifecycle/start-batch.js +2 -1
  134. package/dist/core/engine/lifecycle/start-commit.d.ts +9 -0
  135. package/dist/core/engine/lifecycle/start-commit.js +50 -8
  136. package/dist/core/engine/lifecycle/start-or-signal-create.d.ts +58 -0
  137. package/dist/core/engine/lifecycle/start-or-signal-create.js +95 -0
  138. package/dist/core/engine/lifecycle/start-or-signal.d.ts +3 -2
  139. package/dist/core/engine/lifecycle/start-or-signal.js +11 -81
  140. package/dist/core/engine/lifecycle/start-terminal-conflict-purge.d.ts +39 -0
  141. package/dist/core/engine/lifecycle/start-terminal-conflict-purge.js +28 -0
  142. package/dist/core/engine/lifecycle/start.d.ts +4 -4
  143. package/dist/core/engine/lifecycle/start.js +17 -9
  144. package/dist/core/engine/lifecycle/transition.js +17 -5
  145. package/dist/core/engine/lifecycle.d.ts +1 -1
  146. package/dist/core/engine/list-candidate-resolution.js +2 -2
  147. package/dist/core/engine/listing.js +9 -5
  148. package/dist/core/engine/operations-activity.d.ts +6 -13
  149. package/dist/core/engine/operations-activity.js +26 -53
  150. package/dist/core/engine/operations-coordination.d.ts +10 -0
  151. package/dist/core/engine/operations-coordination.js +44 -9
  152. package/dist/core/engine/operations-router.d.ts +6 -0
  153. package/dist/core/engine/operations-router.js +2 -0
  154. package/dist/core/engine/operations-speculate.js +6 -2
  155. package/dist/core/engine/operations-stream.js +12 -2
  156. package/dist/core/engine/operations-time.d.ts +4 -2
  157. package/dist/core/engine/operations-time.js +14 -2
  158. package/dist/core/engine/operations-wait-condition.d.ts +23 -0
  159. package/dist/core/engine/operations-wait-condition.js +62 -0
  160. package/dist/core/engine/ownership-options.d.ts +23 -0
  161. package/dist/core/engine/ownership-options.js +26 -0
  162. package/dist/core/engine/payload-size-policy.d.ts +3 -0
  163. package/dist/core/engine/payload-size-policy.js +4 -0
  164. package/dist/core/engine/pending-updates.d.ts +17 -0
  165. package/dist/core/engine/pending-updates.js +33 -11
  166. package/dist/core/engine/queries.js +1 -1
  167. package/dist/core/engine/registration.d.ts +1 -0
  168. package/dist/core/engine/registration.js +18 -3
  169. package/dist/core/engine/schedule-jitter.d.ts +5 -0
  170. package/dist/core/engine/schedule-jitter.js +10 -0
  171. package/dist/core/engine/schedule-run.d.ts +17 -0
  172. package/dist/core/engine/schedule-run.js +38 -0
  173. package/dist/core/engine/schedule-timer.js +81 -17
  174. package/dist/core/engine/schedules.d.ts +7 -4
  175. package/dist/core/engine/schedules.js +37 -16
  176. package/dist/core/engine/search-attribute-records.d.ts +4 -0
  177. package/dist/core/engine/search-attribute-records.js +17 -0
  178. package/dist/core/engine/signals.d.ts +18 -2
  179. package/dist/core/engine/signals.js +42 -21
  180. package/dist/core/engine/state-utilities.d.ts +49 -2
  181. package/dist/core/engine/state-utilities.js +114 -3
  182. package/dist/core/engine/storage-io.d.ts +16 -2
  183. package/dist/core/engine/storage-io.js +26 -5
  184. package/dist/core/engine/stream-chunk-loading.d.ts +2 -0
  185. package/dist/core/engine/stream-chunk-loading.js +24 -1
  186. package/dist/core/engine/sub-operation.js +25 -6
  187. package/dist/core/engine/termination/cleanup.d.ts +3 -1
  188. package/dist/core/engine/termination/cleanup.js +25 -1
  189. package/dist/core/engine/termination/complete.js +44 -6
  190. package/dist/core/engine/termination/finalizer-activity.d.ts +53 -0
  191. package/dist/core/engine/termination/finalizer-activity.js +30 -0
  192. package/dist/core/engine/termination/finalizer-claim.d.ts +110 -0
  193. package/dist/core/engine/termination/finalizer-claim.js +80 -0
  194. package/dist/core/engine/termination/finalizer.d.ts +48 -0
  195. package/dist/core/engine/termination/finalizer.js +159 -0
  196. package/dist/core/engine/termination/suspend.d.ts +3 -1
  197. package/dist/core/engine/termination.d.ts +1 -0
  198. package/dist/core/engine/termination.js +4 -0
  199. package/dist/core/engine/updates.js +5 -25
  200. package/dist/core/engine/validation/schedule.d.ts +2 -1
  201. package/dist/core/engine/validation/schedule.js +51 -2
  202. package/dist/core/engine/validation.js +32 -5
  203. package/dist/core/engine/waiting-update-response.d.ts +8 -0
  204. package/dist/core/engine/waiting-update-response.js +22 -0
  205. package/dist/core/engine/workflow-concurrency.d.ts +10 -0
  206. package/dist/core/engine/workflow-concurrency.js +136 -0
  207. package/dist/core/engine/workflow-feed.d.ts +4 -5
  208. package/dist/core/engine/workflow-feed.js +4 -1
  209. package/dist/core/engine/workflow-indexes.d.ts +9 -0
  210. package/dist/core/engine/workflow-indexes.js +10 -0
  211. package/dist/core/engine/workflow-state-stream.js +33 -13
  212. package/dist/core/engine/workflow-visibility-queries.d.ts +2 -2
  213. package/dist/core/engine-helpers.js +1 -0
  214. package/dist/core/events/activity-events.d.ts +43 -12
  215. package/dist/core/events/activity-events.js +28 -0
  216. package/dist/core/events/attribute-events.d.ts +2 -3
  217. package/dist/core/events/event-map.d.ts +18 -12
  218. package/dist/core/events/index.d.ts +1 -0
  219. package/dist/core/events/index.js +1 -0
  220. package/dist/core/events/schedule-events.d.ts +71 -0
  221. package/dist/core/events/schedule-events.js +29 -0
  222. package/dist/core/events/signal-events.d.ts +4 -6
  223. package/dist/core/events/system-events.d.ts +29 -19
  224. package/dist/core/events/system-events.js +26 -0
  225. package/dist/core/events/update-events.d.ts +6 -8
  226. package/dist/core/events/workflow-events.d.ts +88 -23
  227. package/dist/core/events/workflow-events.js +26 -0
  228. package/dist/core/failure-categories.js +5 -17
  229. package/dist/core/fault-code.d.ts +9 -3
  230. package/dist/core/fault-code.js +1 -0
  231. package/dist/core/inline-execution-strategy.context-options.d.ts +69 -0
  232. package/dist/core/inline-execution-strategy.context-options.js +56 -0
  233. package/dist/core/inline-execution-strategy.d.ts +31 -36
  234. package/dist/core/inline-execution-strategy.js +16 -27
  235. package/dist/core/list-filter-validation.d.ts +1 -1
  236. package/dist/core/payload-size.d.ts +1 -1
  237. package/dist/core/persisted-data-incompatible-error.d.ts +9 -7
  238. package/dist/core/review/events.d.ts +6 -8
  239. package/dist/core/scheduler/scheduler-class.d.ts +10 -1
  240. package/dist/core/scheduler/scheduler-class.js +38 -11
  241. package/dist/core/scheduler/timer-batch.js +9 -1
  242. package/dist/core/scheduler/timer-sources.d.ts +1 -0
  243. package/dist/core/scheduler/timer-sources.js +11 -5
  244. package/dist/core/session-state.d.ts +0 -1
  245. package/dist/core/session-state.js +2 -2
  246. package/dist/core/start-workflow-validation.d.ts +34 -0
  247. package/dist/core/start-workflow-validation.js +19 -0
  248. package/dist/core/types/activity-verification.d.ts +67 -0
  249. package/dist/core/types/activity-verification.js +0 -0
  250. package/dist/core/types/activity.d.ts +80 -61
  251. package/dist/core/types/bulk.d.ts +50 -9
  252. package/dist/core/types/checkpoint.d.ts +44 -8
  253. package/dist/core/types/list-options.d.ts +146 -0
  254. package/dist/core/types/list-options.js +0 -0
  255. package/dist/core/types/options.d.ts +121 -165
  256. package/dist/core/types/schedules.d.ts +39 -3
  257. package/dist/core/types/services-resolution.d.ts +2 -2
  258. package/dist/core/types/workflow-builder-runtime.d.ts +12 -3
  259. package/dist/core/types/workflow-builder-runtime.js +11 -2
  260. package/dist/core/types/workflow-builder.d.ts +42 -11
  261. package/dist/core/types/workflow-concurrency.d.ts +39 -0
  262. package/dist/core/types/workflow-concurrency.js +0 -0
  263. package/dist/core/types/workflow-context.d.ts +157 -14
  264. package/dist/core/types/workflow-definition.d.ts +53 -10
  265. package/dist/core/types/workflow-function.d.ts +84 -9
  266. package/dist/core/types/workflow-log.d.ts +98 -0
  267. package/dist/core/types/workflow-log.js +0 -0
  268. package/dist/core/types/workflow-registries.d.ts +14 -3
  269. package/dist/core/types/workflow-registry.d.ts +3 -0
  270. package/dist/core/types.d.ts +4 -0
  271. package/dist/core/types.js +4 -0
  272. package/dist/core/versioning.d.ts +9 -39
  273. package/dist/core/versioning.js +2 -12
  274. package/dist/core/weft-error.d.ts +44 -1
  275. package/dist/core/weft-error.js +16 -0
  276. package/dist/core/worker-execution-strategy-options.d.ts +26 -0
  277. package/dist/core/worker-execution-strategy.js +40 -36
  278. package/dist/core/worker-inbound-message.d.ts +38 -0
  279. package/dist/core/worker-inbound-message.js +37 -0
  280. package/dist/core/worker-log-abuse-counter.d.ts +175 -0
  281. package/dist/core/worker-log-abuse-counter.js +107 -0
  282. package/dist/core/worker-message-helpers.d.ts +47 -0
  283. package/dist/core/worker-message-helpers.js +26 -0
  284. package/dist/core/worker-protocol-guard.js +1 -1
  285. package/dist/core/worker-protocol-log.d.ts +56 -0
  286. package/dist/core/worker-protocol-log.js +18 -0
  287. package/dist/core/worker-protocol.d.ts +23 -0
  288. package/dist/core/worker-protocol.js +12 -2
  289. package/dist/core/workflow-version-tuple.d.ts +3 -9
  290. package/dist/core/workflow-version-tuple.js +2 -3
  291. package/dist/diagnostics/format.js +1 -6
  292. package/dist/diagnostics/types.d.ts +4 -5
  293. package/dist/diagnostics/validate.d.ts +0 -1
  294. package/dist/diagnostics/version-check.d.ts +1 -1
  295. package/dist/diagnostics/version-check.js +4 -9
  296. package/dist/index.d.ts +12 -10
  297. package/dist/index.js +29 -3
  298. package/dist/json-schema.js +3 -3
  299. package/dist/mcp/cli.js +19 -19
  300. package/dist/mcp/dispatcher.js +1 -1
  301. package/dist/mcp/http.js +16 -6
  302. package/dist/mcp/protocol.d.ts +2 -0
  303. package/dist/mcp/protocol.js +1 -1
  304. package/dist/mcp/session.d.ts +16 -0
  305. package/dist/mcp/session.js +59 -6
  306. package/dist/mcp/tools.js +73 -6
  307. package/dist/observability/index.js +2 -2
  308. package/dist/server/asyncapi-channels.js +5 -2
  309. package/dist/server/attribute-filters.d.ts +8 -3
  310. package/dist/server/attribute-filters.js +6 -1
  311. package/dist/server/authentication/api-key.d.ts +2 -1
  312. package/dist/server/authentication/api-key.js +5 -2
  313. package/dist/server/authentication/constant-time-api-key.d.ts +15 -0
  314. package/dist/server/authentication/constant-time-api-key.js +24 -0
  315. package/dist/server/authentication/index.d.ts +1 -1
  316. package/dist/server/authentication/index.js +7 -4
  317. package/dist/server/authentication/rotating-api-key-store.d.ts +5 -3
  318. package/dist/server/authentication/rotating-api-key-store.js +19 -11
  319. package/dist/server/engine-event-feed-backend.d.ts +4 -5
  320. package/dist/server/fault-to-http.js +8 -1
  321. package/dist/server/fault-to-json-rpc.js +1 -0
  322. package/dist/server/fleet-event-feed.d.ts +31 -0
  323. package/dist/server/fleet-event-feed.js +157 -0
  324. package/dist/server/handler/index.js +1 -1
  325. package/dist/server/handler/response-helpers.js +2 -1
  326. package/dist/server/handler/route-dispatch.d.ts +6 -3
  327. package/dist/server/handler/route-dispatch.js +2 -2
  328. package/dist/server/handler.js +23 -23
  329. package/dist/server/index.d.ts +32 -2
  330. package/dist/server/index.js +32 -32
  331. package/dist/server/json-rpc-transport-helpers.d.ts +1 -0
  332. package/dist/server/json-rpc-transport-helpers.js +2 -1
  333. package/dist/server/json-rpc-websocket-runtime.d.ts +11 -2
  334. package/dist/server/json-rpc-websocket-runtime.js +1 -0
  335. package/dist/server/json-rpc-websocket-subscriptions.d.ts +10 -0
  336. package/dist/server/json-rpc-websocket-subscriptions.js +29 -0
  337. package/dist/server/json-rpc-websocket-types.d.ts +22 -0
  338. package/dist/server/json-rpc-websocket-types.js +0 -0
  339. package/dist/server/json-rpc-websocket-validation.js +20 -4
  340. package/dist/server/json-rpc-websocket.d.ts +2 -43
  341. package/dist/server/json-rpc-websocket.js +57 -74
  342. package/dist/server/openapi.d.ts +2 -1
  343. package/dist/server/openapi.js +1 -3
  344. package/dist/server/openrpc-document-schema.d.ts +78 -0
  345. package/dist/server/openrpc-document-schema.js +27 -1
  346. package/dist/server/openrpc-errors.d.ts +3 -0
  347. package/dist/server/openrpc-errors.js +2 -1
  348. package/dist/server/openrpc.js +32 -0
  349. package/dist/server/operation-catalog/index.d.ts +1 -1
  350. package/dist/server/operation-catalog/pipeline-helpers.js +1 -0
  351. package/dist/server/operation-catalog/pipeline-stages.js +26 -12
  352. package/dist/server/operation-catalog/pipeline.js +8 -1
  353. package/dist/server/operation-catalog/registry.js +13 -0
  354. package/dist/server/operation-catalog/types.d.ts +20 -9
  355. package/dist/server/operation-fault.d.ts +10 -0
  356. package/dist/server/operation-fault.js +2 -0
  357. package/dist/server/operation-registry.d.ts +12 -11
  358. package/dist/server/operation-registry.js +12 -3
  359. package/dist/server/operations/aggregate-workflows.d.ts +15 -15
  360. package/dist/server/operations/async-activity.d.ts +2 -2
  361. package/dist/server/operations/async-activity.js +12 -7
  362. package/dist/server/operations/bulk-cancel-workflows.d.ts +1 -1
  363. package/dist/server/operations/bulk-cancel-workflows.js +2 -2
  364. package/dist/server/operations/bulk-delete-workflows.d.ts +1 -1
  365. package/dist/server/operations/bulk-delete-workflows.js +2 -2
  366. package/dist/server/operations/bulk-filter-helpers.d.ts +8 -6
  367. package/dist/server/operations/bulk-filter-helpers.js +54 -24
  368. package/dist/server/operations/bulk-mutate-workflow-tags.d.ts +11 -9
  369. package/dist/server/operations/bulk-mutate-workflow-tags.js +2 -2
  370. package/dist/server/operations/bulk-operation-helpers.d.ts +2 -2
  371. package/dist/server/operations/bulk-retry-failed-workflows.d.ts +7 -0
  372. package/dist/server/operations/bulk-retry-failed-workflows.js +64 -0
  373. package/dist/server/operations/bulk-signal-workflows.d.ts +11 -9
  374. package/dist/server/operations/bulk-signal-workflows.js +2 -2
  375. package/dist/server/operations/cancel-schedule.d.ts +1 -1
  376. package/dist/server/operations/create-schedule.d.ts +3 -1
  377. package/dist/server/operations/create-schedule.js +29 -11
  378. package/dist/server/operations/fleet-events-subscription.d.ts +54 -0
  379. package/dist/server/operations/fleet-events-subscription.js +99 -0
  380. package/dist/server/operations/fork-workflow.d.ts +1 -1
  381. package/dist/server/operations/fork-workflow.js +3 -2
  382. package/dist/server/operations/get-checkpoint-at.d.ts +1 -1
  383. package/dist/server/operations/get-registry.d.ts +1 -1
  384. package/dist/server/operations/get-retention-overview.d.ts +1 -1
  385. package/dist/server/operations/get-review.d.ts +1 -1
  386. package/dist/server/operations/get-schedule.d.ts +1 -1
  387. package/dist/server/operations/get-stream-chunks.d.ts +1 -1
  388. package/dist/server/operations/get-system-metrics.d.ts +2 -2
  389. package/dist/server/operations/get-task-diagnostics.d.ts +45 -6
  390. package/dist/server/operations/get-task-diagnostics.js +101 -7
  391. package/dist/server/operations/get-update-result.d.ts +1 -1
  392. package/dist/server/operations/get-workflow-attributes.d.ts +1 -1
  393. package/dist/server/operations/get-workflow-events.d.ts +1 -1
  394. package/dist/server/operations/get-workflow-result.d.ts +1 -1
  395. package/dist/server/operations/get-workflow-timeline.d.ts +1 -1
  396. package/dist/server/operations/get-workflow.d.ts +1 -1
  397. package/dist/server/operations/list-checkpoints.d.ts +1 -1
  398. package/dist/server/operations/list-reviews.d.ts +1 -1
  399. package/dist/server/operations/list-schedules.d.ts +1 -1
  400. package/dist/server/operations/list-task-queues.d.ts +2 -2
  401. package/dist/server/operations/list-workers.d.ts +2 -2
  402. package/dist/server/operations/list-workflows.d.ts +1 -1
  403. package/dist/server/operations/operation-helpers.d.ts +16 -1
  404. package/dist/server/operations/operation-helpers.js +15 -3
  405. package/dist/server/operations/pause-schedule.d.ts +1 -1
  406. package/dist/server/operations/purge-workflows.d.ts +5 -5
  407. package/dist/server/operations/purge-workflows.js +2 -2
  408. package/dist/server/operations/query-workflow.d.ts +1 -1
  409. package/dist/server/operations/query-workflow.js +3 -2
  410. package/dist/server/operations/recover-all.d.ts +1 -1
  411. package/dist/server/operations/replay-workflow.d.ts +1 -1
  412. package/dist/server/operations/resume-schedule.d.ts +1 -1
  413. package/dist/server/operations/set-workflow-attributes.d.ts +1 -1
  414. package/dist/server/operations/set-workflow-attributes.js +6 -3
  415. package/dist/server/operations/signal-workflow.js +8 -3
  416. package/dist/server/operations/single-workflow-tag-mutation.js +13 -3
  417. package/dist/server/operations/start-or-signal-workflow.d.ts +6 -1
  418. package/dist/server/operations/start-or-signal-workflow.js +14 -24
  419. package/dist/server/operations/start-workflow-options.d.ts +6 -0
  420. package/dist/server/operations/start-workflow-rest-input.d.ts +14 -0
  421. package/dist/server/operations/start-workflow-rest-input.js +31 -0
  422. package/dist/server/operations/start-workflow.d.ts +1 -1
  423. package/dist/server/operations/start-workflow.js +8 -23
  424. package/dist/server/operations/storage.d.ts +6 -6
  425. package/dist/server/operations/storage.js +24 -14
  426. package/dist/server/operations/stream-workflow-sse.d.ts +1 -1
  427. package/dist/server/operations/submit-review-decision.d.ts +1 -1
  428. package/dist/server/operations/submit-review-decision.js +6 -3
  429. package/dist/server/operations/update-schedule.d.ts +1 -1
  430. package/dist/server/operations/update-schedule.js +7 -4
  431. package/dist/server/operations/update-workflow.d.ts +1 -1
  432. package/dist/server/operations/update-workflow.js +7 -3
  433. package/dist/server/operations/worker-drain.d.ts +8 -8
  434. package/dist/server/operations/worker-drain.js +11 -8
  435. package/dist/server/operations/workflow-events-subscription.d.ts +7 -8
  436. package/dist/server/operations/workflow-events-subscription.js +43 -8
  437. package/dist/server/rest-binding.d.ts +3 -1
  438. package/dist/server/rest-bindings.js +12 -0
  439. package/dist/server/rest-body.d.ts +10 -0
  440. package/dist/server/rest-body.js +80 -0
  441. package/dist/server/runtime/authentication-bridge.d.ts +1 -0
  442. package/dist/server/runtime/authentication-bridge.js +33 -6
  443. package/dist/server/runtime/client-visible-events.d.ts +3 -0
  444. package/dist/server/runtime/client-visible-events.js +65 -0
  445. package/dist/server/runtime/context.d.ts +7 -0
  446. package/dist/server/runtime/event-broadcasting.d.ts +6 -0
  447. package/dist/server/runtime/event-broadcasting.js +72 -38
  448. package/dist/server/runtime/retry.d.ts +1 -0
  449. package/dist/server/runtime/retry.js +14 -0
  450. package/dist/server/runtime/shutdown.d.ts +7 -6
  451. package/dist/server/runtime/shutdown.js +6 -1
  452. package/dist/server/runtime/task-polling.js +61 -20
  453. package/dist/server/runtime/task-reconciliation.js +9 -1
  454. package/dist/server/runtime/task-result-resolution.d.ts +15 -0
  455. package/dist/server/runtime/task-result-resolution.js +81 -0
  456. package/dist/server/runtime/websocket-stream.d.ts +20 -1
  457. package/dist/server/runtime/websocket-stream.js +173 -7
  458. package/dist/server/runtime/websocket-upgrade.js +22 -3
  459. package/dist/server/runtime/websocket-worker.d.ts +1 -1
  460. package/dist/server/runtime/websocket-worker.js +38 -30
  461. package/dist/server/serve-internals.d.ts +3 -1
  462. package/dist/server/serve-internals.js +18 -2
  463. package/dist/server/task-state.d.ts +30 -0
  464. package/dist/server/task-state.js +28 -0
  465. package/dist/server/workflow-event-feed.d.ts +36 -115
  466. package/dist/server/workflow-event-feed.js +92 -27
  467. package/dist/service-worker/index.js +30 -30
  468. package/dist/storage/auto.js +1 -1
  469. package/dist/storage/bun-sql.js +24 -0
  470. package/dist/storage/compressed-storage.js +1 -1
  471. package/dist/storage/default-scope.d.ts +7 -4
  472. package/dist/storage/http.js +2 -2
  473. package/dist/storage/index.d.ts +76 -4
  474. package/dist/storage/indexeddb.js +1 -1
  475. package/dist/storage/interface.d.ts +163 -0
  476. package/dist/storage/interface.js +1 -1
  477. package/dist/storage/key-prefixes.d.ts +1 -1
  478. package/dist/storage/key-prefixes.js +10 -0
  479. package/dist/storage/lmdb.d.ts +4 -3
  480. package/dist/storage/lmdb.js +1 -1
  481. package/dist/storage/memory.js +1 -1
  482. package/dist/storage/neon-batch.d.ts +30 -0
  483. package/dist/storage/neon-batch.js +30 -0
  484. package/dist/storage/neon-value-mapping.d.ts +30 -3
  485. package/dist/storage/neon-value-mapping.js +12 -0
  486. package/dist/storage/neon.d.ts +16 -0
  487. package/dist/storage/neon.js +9 -4
  488. package/dist/storage/node-sqlite.js +23 -0
  489. package/dist/storage/postgres-key-value-queries.d.ts +69 -44
  490. package/dist/storage/postgres-key-value-queries.js +62 -24
  491. package/dist/storage/resolve.js +1 -1
  492. package/dist/storage/scoped-storage.js +1 -1
  493. package/dist/storage/testing.d.ts +168 -0
  494. package/dist/storage/testing.js +2 -0
  495. package/dist/storage/turso.d.ts +8 -0
  496. package/dist/storage/turso.js +2 -2
  497. package/dist/storage/typed-storage.js +1 -1
  498. package/dist/storage/web-extension.js +1 -1
  499. package/dist/testing/index.js +20 -20
  500. package/dist/version.d.ts +1 -1
  501. package/dist/version.js +1 -1
  502. package/dist/worker/protocol.js +1 -1
  503. package/dist/workers/worker-replay-state.d.ts +32 -0
  504. package/dist/workers/worker-replay-state.js +35 -0
  505. package/dist/workers/worker-state-namespace.d.ts +21 -0
  506. package/dist/workers/worker-state-namespace.js +13 -0
  507. package/dist/workers/workflow-runner.d.ts +19 -15
  508. package/dist/workers/workflow-runner.js +72 -60
  509. package/dist/workers/workflow-worker-entry.js +5 -2
  510. package/package.json +5 -1
@@ -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
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
@@ -139,36 +162,34 @@ export async function refreshScheduledWorkflowState(internals, state, callbacks)
139
162
  currentWorkflowState: currentWorkflowState ?? null
140
163
  };
141
164
  }
142
- export async function startScheduledRun(_internals, state, callbacks) {
143
- const workflowId = crypto.randomUUID(), scheduleRunOperations = state.overlap === "allow" ? void 0 : [{ type: "put", key: KEYS.scheduleRun(workflowId), value: encode(state.id) }];
144
- await callbacks.startWorkflow(state.workflowType, state.input, { id: workflowId }, scheduleRunOperations);
145
- return workflowId;
146
- }
147
165
  function hasActiveScheduledWorkflow(currentWorkflowState) {
148
166
  return scheduledRunOccupiesSlot(currentWorkflowState);
149
167
  }
150
- async function applyBlockedScheduleOccurrence(state, hasActiveWorkflow, callbacks) {
168
+ async function applyBlockedScheduleOccurrence(state, hasActiveWorkflow, callbacks, occurrence) {
151
169
  if (!hasActiveWorkflow)
152
- return { ...state, currentWorkflowId: await callbacks.startScheduledRun(state) };
170
+ return { ...state, currentWorkflowId: await callbacks.startScheduledRun(state, occurrence) };
153
171
  if (state.overlap === "cancel-running") {
154
172
  if (state.currentWorkflowId) {
155
173
  callbacks.getWorkflowResult(state.currentWorkflowId).catch(() => {});
156
174
  await callbacks.cancelWorkflow(state.currentWorkflowId);
157
175
  }
158
176
  const stateForStart = clearScheduleCurrentWorkflow(state);
159
- return { ...state, currentWorkflowId: await callbacks.startScheduledRun(stateForStart) };
177
+ return {
178
+ ...state,
179
+ currentWorkflowId: await callbacks.startScheduledRun(stateForStart, occurrence)
180
+ };
160
181
  }
161
182
  if (state.overlap === "queue")
162
183
  return { ...state, queuedRuns: state.queuedRuns + 1 };
163
184
  return state;
164
185
  }
165
- export async function applyScheduleOccurrence(_internals, state, callbacks) {
186
+ export async function applyScheduleOccurrence(_internals, state, callbacks, occurrence) {
166
187
  const { state: refreshedState, currentWorkflowState } = await callbacks.refreshScheduledWorkflowState(state), hasActiveWorkflow = hasActiveScheduledWorkflow(currentWorkflowState);
167
188
  if (refreshedState.overlap === "allow") {
168
- await callbacks.startScheduledRun(refreshedState);
189
+ await callbacks.startScheduledRun(refreshedState, occurrence);
169
190
  return refreshedState;
170
191
  }
171
- return applyBlockedScheduleOccurrence(refreshedState, hasActiveWorkflow, callbacks);
192
+ return applyBlockedScheduleOccurrence(refreshedState, hasActiveWorkflow, callbacks, occurrence);
172
193
  }
173
194
  export async function settleBackfillScheduleState(internals, state, callbacks) {
174
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
+ }
@@ -43,8 +43,14 @@ export declare function bufferSignalPayloads(internals: EngineInternals, workflo
43
43
  * `sigres:` key and short-circuits instead of re-delivering, which is what
44
44
  * guarantees "one signal per signalId" across the create and signal paths.
45
45
  *
46
- * Returns both the put operations and the CAS condition (the `sig:` key must be
47
- * absent) so the caller can gate the create batch on it.
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.
48
54
  */
49
55
  export declare function buildCreateBatchSignalOperations(internals: EngineInternals, workflowId: string, signalName: string, payload: unknown, signalId: string): {
50
56
  operations: BatchOperation[];
@@ -55,6 +61,16 @@ export declare function buildCreateBatchSignalOperations(internals: EngineIntern
55
61
  };
56
62
  export declare function hasBufferedSignal(internals: EngineInternals, workflowId: string, signalName: string): Promise<boolean>;
57
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>;
58
74
  /** Register a waiter key in a workflow-keyed reverse index. */
59
75
  export declare function trackWaiterKey(reverseIndex: Map<string, TrackedWaiterKeys>, workflowId: string, waiterKey: string): void;
60
76
  /** Remove a waiter key from a workflow-keyed reverse index. */
@@ -9,8 +9,9 @@ import { SignalReceivedEvent } from "../events.js";
9
9
  import { encodePayloadWithinLimit } from "../payload-size.js";
10
10
  import { validateSignalId } from "../signal-id.js";
11
11
  import { commitAnonymousSignalOperations } from "./anonymous-signal-sequence.js";
12
+ import { stageAtomicWorkflowCommitSideEffects } from "./checkpoint-side-effects.js";
12
13
  import { isTerminalWorkflowStatus } from "./validation.js";
13
- const EMPTY_STORAGE_VALUE = new Uint8Array(0), SIGNAL_ACCEPTED_RESPONSE = { ok: !0 };
14
+ const EMPTY_STORAGE_VALUE = new Uint8Array(0), SIGNAL_ACCEPTED_RESPONSE = { ok: !0 }, SIGNAL_KEY_COMPONENT_COUNT = 5;
14
15
  export async function signal(internals, workflowId, name, payload, callbacks, options = {}) {
15
16
  const deliverSignal = async (targetWorkflowId, signalName, signalPayload) => {
16
17
  await bufferSignalPayloads(internals, targetWorkflowId, [{ signalName, payload: signalPayload }], callbacks, options);
@@ -65,10 +66,8 @@ export async function bufferSignalPayloads(internals, workflowId, deliveries, ca
65
66
  const delivery = deliveries[0], acceptedResponseKey = KEYS.signalAcceptedResponse(workflowId, delivery.signalName, signalId), acceptedResponse = encode(SIGNAL_ACCEPTED_RESPONSE);
66
67
  if (await internals.storage.get(acceptedResponseKey) !== null)
67
68
  return;
68
- if (!await storageConditionalBatch(internals.storage, [{ key: KEYS.signal(workflowId, delivery.signalName, signalId), expectedValue: null }], [...operations, { type: "put", key: acceptedResponseKey, value: acceptedResponse }])) {
69
- await ensureDuplicateSignalAcceptedResponse(internals, acceptedResponseKey, acceptedResponse);
69
+ if (!await storageConditionalBatch(internals.storage, [{ key: acceptedResponseKey, expectedValue: null }], [...operations, { type: "put", key: acceptedResponseKey, value: acceptedResponse }]))
70
70
  return;
71
- }
72
71
  markTerminalCleanupTracked(internals, workflowId);
73
72
  deliverBufferedSignals(internals, workflowId, deliveries, callbacks);
74
73
  return;
@@ -85,7 +84,7 @@ function createExplicitSignalOperations(internals, workflowId, deliveries, signa
85
84
  }
86
85
  export function buildCreateBatchSignalOperations(internals, workflowId, signalName, payload, signalId) {
87
86
  validateSignalId(signalId);
88
- const signalKey = KEYS.signal(workflowId, signalName, signalId), acceptedResponseKey = KEYS.signalAcceptedResponse(workflowId, signalName, signalId);
87
+ const signalKey = KEYS.startSignal(workflowId, signalName, signalId), acceptedResponseKey = KEYS.signalAcceptedResponse(workflowId, signalName, signalId);
89
88
  return {
90
89
  operations: [
91
90
  {
@@ -95,7 +94,7 @@ export function buildCreateBatchSignalOperations(internals, workflowId, signalNa
95
94
  },
96
95
  { type: "put", key: acceptedResponseKey, value: encode(SIGNAL_ACCEPTED_RESPONSE) }
97
96
  ],
98
- condition: { key: signalKey, expectedValue: null }
97
+ condition: { key: acceptedResponseKey, expectedValue: null }
99
98
  };
100
99
  }
101
100
  function appendTerminalCleanupOperation(internals, workflowId, operations) {
@@ -130,18 +129,45 @@ function deliverBufferedSignals(internals, workflowId, deliveries, callbacks) {
130
129
  callbacks.resumeParkedInlineWorkflow(workflowId);
131
130
  }
132
131
  export async function hasBufferedSignal(internals, workflowId, signalName) {
133
- const prefix = `sig:${encodeStorageKeyComponent(workflowId)}:${signalName}:`;
134
- for await (const _entry of internals.storage.scan(prefix, { limit: 1 }))
135
- return !0;
136
- return !1;
132
+ return await findBufferedSignalRecord(internals, workflowId, signalName) !== null;
137
133
  }
138
134
  export async function consumeSignal(internals, workflowId, signalName) {
139
- const prefix = `sig:${encodeStorageKeyComponent(workflowId)}:${signalName}:`;
140
- for await (const [key, value] of internals.storage.scan(prefix, { limit: 1 })) {
141
- await internals.storage.delete(key);
142
- return { found: !0, payload: decode(value) };
143
- }
144
- return { found: !1 };
135
+ const record = await findBufferedSignalRecord(internals, workflowId, signalName);
136
+ if (record === null)
137
+ return { found: !1 };
138
+ await internals.storage.delete(record.key);
139
+ return { found: !0, payload: decode(record.value) };
140
+ }
141
+ export async function consumeSignalWithAtomicWorkflowCommit(internals, workflowId, signalName) {
142
+ const record = await findBufferedSignalRecord(internals, workflowId, signalName);
143
+ if (record === null)
144
+ return { found: !1 };
145
+ stageAtomicWorkflowCommitSideEffects(internals, workflowId, {
146
+ conditions: [{ key: record.key, expectedValue: new Uint8Array(record.value) }],
147
+ operations: [{ type: "delete", key: record.key }]
148
+ });
149
+ return { found: !0, payload: decode(record.value) };
150
+ }
151
+ export async function peekSignal(internals, workflowId, signalName) {
152
+ const record = await findBufferedSignalRecord(internals, workflowId, signalName);
153
+ return record === null ? { found: !1 } : { found: !0, payload: decode(record.value) };
154
+ }
155
+ async function findBufferedSignalRecord(internals, workflowId, signalName) {
156
+ const encodedWorkflowId = encodeStorageKeyComponent(workflowId), encodedSignalName = encodeStorageKeyComponent(signalName), encodedPrefix = signalKeyPrefix(encodedWorkflowId, encodedSignalName);
157
+ return await findBufferedSignalRecordByPrefix(internals, encodedPrefix, encodedWorkflowId, encodedSignalName);
158
+ }
159
+ async function findBufferedSignalRecordByPrefix(internals, prefix, encodedWorkflowId, signalNameKeyComponent) {
160
+ for await (const [key, value] of internals.storage.scan(prefix, { limit: 1 }))
161
+ if (isExactSignalKey(key, encodedWorkflowId, signalNameKeyComponent))
162
+ return { key, value };
163
+ return null;
164
+ }
165
+ function signalKeyPrefix(encodedWorkflowId, signalNameKeyComponent) {
166
+ return `sig:${encodedWorkflowId}:${signalNameKeyComponent}:`;
167
+ }
168
+ function isExactSignalKey(key, encodedWorkflowId, signalNameKeyComponent) {
169
+ const components = key.split(":");
170
+ return components.length === SIGNAL_KEY_COMPONENT_COUNT && components[0] === "sig" && components[1] === encodedWorkflowId && components[2] === signalNameKeyComponent && (components[3] === "0" || components[3] === "1") && components[4] !== "";
145
171
  }
146
172
  function getSingleSignalId(deliveries, defaultOptions) {
147
173
  if (deliveries.length !== 1)
@@ -159,11 +185,6 @@ function validateSignalIdsBeforeKeyConstruction(deliveries, defaultOptions, sing
159
185
  validateSignalId(deliverySignalId);
160
186
  }
161
187
  }
162
- async function ensureDuplicateSignalAcceptedResponse(internals, acceptedResponseKey, acceptedResponse) {
163
- if (await internals.storage.get(acceptedResponseKey) !== null)
164
- return;
165
- await storageConditionalBatch(internals.storage, [{ key: acceptedResponseKey, expectedValue: null }], [{ type: "put", key: acceptedResponseKey, value: acceptedResponse }]);
166
- }
167
188
  export function trackWaiterKey(reverseIndex, workflowId, waiterKey) {
168
189
  let keys = reverseIndex.get(workflowId);
169
190
  if (!keys) {
@@ -1,5 +1,49 @@
1
1
  import type { ContextOperationRequest } from '../context.ts';
2
- import type { ListFilter, PaginatedResult, ScheduleFilter, ScheduleState, ScheduleSummary, WorkflowState, WorkflowSummary } from '../types.ts';
2
+ import type { AttributeFilter, AttributeFilterScalarValue, ListFilter, PaginatedResult, ScheduleFilter, ScheduleState, ScheduleSummary, SearchAttributeValue, WorkflowState, WorkflowSummary } from '../types.ts';
3
+ /**
4
+ * Durable execution-claim record stored at {@link KEYS.teardownOwed} for a workflow
5
+ * that owes an engine-driven finalizer run (issue #446 Phase 2). A holder fenced-CAS's
6
+ * it from `'owed'` to `'running'` before invoking the finalizer, then back to `'owed'`
7
+ * (with `attempts` bumped) on a retryable failure, or deletes it on success/dead-letter.
8
+ *
9
+ * Liveness is decided by TIME, not by an in-memory set or a lease epoch: a `'running'`
10
+ * claim is reclaimable once `claimedAt` is older than the stale threshold (the
11
+ * finalizer's per-attempt timeout plus a margin — see `teardownStaleThresholdMs`). This
12
+ * makes crash recovery an ordinary stale-claim retry with no special re-hydration: a
13
+ * fresh process simply re-fires the surviving timer and reclaims the stale `'running'`
14
+ * marker. The cost is that a finalizer running past the threshold may be re-driven
15
+ * concurrently, which is why workflow finalizers must be idempotent.
16
+ *
17
+ * - `status`: `'owed'` until claimed, `'running'` while a holder is executing it.
18
+ * - `attempts`: count of finalizer attempts so far (`0` until the first claim).
19
+ * - `token`: ties the marker to its `wf-teardown:` timer so a stale timer for a
20
+ * re-armed (different-token) claim cannot drive it.
21
+ * - `claimedAt`: engine clock at the `owed → running` transition; `undefined` while
22
+ * `'owed'`. Drives the time-based reclaim of an abandoned `'running'` claim.
23
+ */
24
+ export type TeardownClaim = {
25
+ status: 'owed' | 'running';
26
+ attempts: number;
27
+ token: string;
28
+ claimedAt?: number;
29
+ };
30
+ /** Build the durable teardown timer id from its claim token (parsed by {@link parseTeardownTimerId}). */
31
+ export declare function createTeardownTimerId(token: string): string;
32
+ /** Recover the claim token from a teardown timer id, or `null` when the id is malformed. */
33
+ export declare function parseTeardownTimerId(timerId: string): string | null;
34
+ /**
35
+ * Runtime type guard for a decoded {@link TeardownClaim} read back from storage.
36
+ *
37
+ * `attempts` must be a non-negative SAFE INTEGER and `claimedAt` (when present) a
38
+ * finite non-negative number — not merely `typeof === 'number'`. A persisted `NaN`
39
+ * or `Infinity` would otherwise drive the marker forever: `attempt >= MAX_TEARDOWN_ATTEMPTS`
40
+ * is always false for `NaN` (never dead-letters) and `now - claimedAt >= threshold` never
41
+ * holds for a non-finite `claimedAt` (never reclaims a stale running claim). Rejecting them
42
+ * here routes a corrupt-but-claim-shaped marker through the clear path instead. `claimedAt`
43
+ * is checked with `isFinite` rather than `isSafeInteger` because `getNow()` may return a
44
+ * fractional timestamp.
45
+ */
46
+ export declare function isTeardownClaim(value: unknown): value is TeardownClaim;
3
47
  type PaginationFilter = {
4
48
  limit?: number;
5
49
  offset?: number;
@@ -34,7 +78,10 @@ export declare function encodeWorkflowStartHeaders(headers: Map<string, string>)
34
78
  export declare function decodeWorkflowStartHeaders(bytes: Uint8Array): Map<string, string>;
35
79
  export declare function selectPersistedWorkflowStartHeaders(headers: Map<string, string> | undefined): Map<string, string> | undefined;
36
80
  export declare function intersectIdentifierSets(idSets: Set<string>[]): Set<string> | null;
37
- export declare function matchesListFilter(state: WorkflowState, filter: ListFilter | undefined, constrainedIds: Set<string> | null, normalizedTagFilters: readonly string[] | undefined): boolean;
81
+ export declare function listFilterHasAttributeFilters(filter: ListFilter | undefined): boolean;
82
+ export declare function decodeSearchAttributeRecord(attributeBytes: Uint8Array | null): Record<string, SearchAttributeValue> | null;
83
+ export declare function attributeFilterExactValues(filter: AttributeFilter): readonly AttributeFilterScalarValue[] | null;
84
+ export declare function matchesListFilter(state: WorkflowState, filter: ListFilter | undefined, constrainedIds: Set<string> | null, normalizedTagFilters: readonly string[] | undefined, searchAttributes?: Readonly<Record<string, SearchAttributeValue>> | null): boolean;
38
85
  /**
39
86
  * Slice an in-memory list of {@link WorkflowSummary} into a {@link PaginatedResult}.
40
87
  *
@@ -1,8 +1,32 @@
1
1
  import { decode, encode } from "../codec.js";
2
2
  import { isRecord, safeDebugStringify, sanitizeDebugValueForDisplay } from "../debug-output.js";
3
+ import { encodeAttributeValue } from "../search-attributes.js";
4
+ import { searchAttributeName } from "../types.js";
3
5
  import { matchesWorkflowTagFilter } from "../workflow-tags.js";
6
+ import { stripInternalCheckpointReplayPayload } from "./checkpoint-replay.js";
4
7
  import { isPlainObjectRecord, isSanitizedSearchAttributeValue } from "./validation.js";
5
- const PERSISTED_WORKFLOW_START_HEADER_NAMES = new Set(["traceparent", "tracestate"]), PRESERVE_OUTPUT_TERMINAL_CLEANUP_TIMER_PREFIX = "terminal-cleanup:preserve-output:", FULL_TERMINAL_CLEANUP_TIMER_PREFIX = "terminal-cleanup:full:";
8
+ const PERSISTED_WORKFLOW_START_HEADER_NAMES = new Set(["traceparent", "tracestate"]), PRESERVE_OUTPUT_TERMINAL_CLEANUP_TIMER_PREFIX = "terminal-cleanup:preserve-output:", FULL_TERMINAL_CLEANUP_TIMER_PREFIX = "terminal-cleanup:full:", TEARDOWN_TIMER_PREFIX = "teardown:";
9
+ export function createTeardownTimerId(token) {
10
+ return `${TEARDOWN_TIMER_PREFIX}${token}`;
11
+ }
12
+ export function parseTeardownTimerId(timerId) {
13
+ if (!timerId.startsWith(TEARDOWN_TIMER_PREFIX))
14
+ return null;
15
+ const token = timerId.slice(TEARDOWN_TIMER_PREFIX.length);
16
+ return token.length === 0 ? null : token;
17
+ }
18
+ export function isTeardownClaim(value) {
19
+ if (typeof value !== "object" || value === null)
20
+ return !1;
21
+ const candidate = value;
22
+ return (candidate.status === "owed" || candidate.status === "running") && typeof candidate.token === "string" && isNonNegativeSafeInteger(candidate.attempts) && isAbsentOrFiniteNonNegative(candidate.claimedAt);
23
+ }
24
+ function isNonNegativeSafeInteger(value) {
25
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
26
+ }
27
+ function isAbsentOrFiniteNonNegative(value) {
28
+ return value === void 0 || typeof value === "number" && Number.isFinite(value) && value >= 0;
29
+ }
6
30
  export function workflowFeedListenerKey(workflowId, selector) {
7
31
  return `${workflowId}\x00${selector}`;
8
32
  }
@@ -33,6 +57,8 @@ export function getTimelineOperationLabel(operation) {
33
57
  return operation.signalName;
34
58
  case "wait-update":
35
59
  return operation.updateName;
60
+ case "get-version":
61
+ return operation.changeId;
36
62
  case "child-workflow":
37
63
  return operation.workflowType;
38
64
  case "load":
@@ -84,6 +110,13 @@ export function getTimelineBasicInputSummary(operation) {
84
110
  return summarizeTimelineValue({ signalName: operation.signalName });
85
111
  case "wait-update":
86
112
  return summarizeTimelineValue({ updateName: operation.updateName });
113
+ case "get-version":
114
+ return summarizeTimelineValue({
115
+ changeId: operation.changeId,
116
+ minSupported: operation.minSupported,
117
+ maxSupported: operation.maxSupported,
118
+ version: operation.version
119
+ });
87
120
  case "parallel":
88
121
  case "race":
89
122
  return summarizeTimelineValue({ operationCount: operation.operations.length });
@@ -138,7 +171,7 @@ export function sanitizeCheckpointState(checkpoint) {
138
171
  }
139
172
  export function sanitizeWorkflowEventPayload(payload) {
140
173
  const sanitized = sanitizeDebugValueForDisplay(payload);
141
- return isRecord(sanitized) ? sanitized : { value: sanitized };
174
+ return isRecord(sanitized) ? stripInternalCheckpointReplayPayload(sanitized) : { value: sanitized };
142
175
  }
143
176
  export function sanitizeTimelineSummary(summary) {
144
177
  if (summary === void 0)
@@ -207,7 +240,83 @@ function matchesListFilterFailureCategory(state, filter) {
207
240
  const categories = Array.isArray(filter.failureCategory) ? filter.failureCategory : [filter.failureCategory];
208
241
  return state.failureCategory !== void 0 && state.failureCategory !== null && categories.includes(state.failureCategory);
209
242
  }
210
- export function matchesListFilter(state, filter, constrainedIds, normalizedTagFilters) {
243
+ export function listFilterHasAttributeFilters(filter) {
244
+ return (filter?.attributes?.length ?? 0) > 0;
245
+ }
246
+ function isSearchAttributeFilterValue(value) {
247
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value instanceof Date)
248
+ return !0;
249
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
250
+ }
251
+ export function decodeSearchAttributeRecord(attributeBytes) {
252
+ if (attributeBytes === null)
253
+ return null;
254
+ const decoded = decode(attributeBytes);
255
+ if (!isPlainObjectRecord(decoded))
256
+ return null;
257
+ const attributes = {};
258
+ for (const [key, value] of Object.entries(decoded))
259
+ if (isSearchAttributeFilterValue(value))
260
+ attributes[key] = value;
261
+ return attributes;
262
+ }
263
+ export function attributeFilterExactValues(filter) {
264
+ if (filter.value === void 0)
265
+ return null;
266
+ if (Array.isArray(filter.value))
267
+ return [...filter.value];
268
+ return [filter.value];
269
+ }
270
+ function storedAttributeScalarValues(value) {
271
+ return Array.isArray(value) ? value : [value];
272
+ }
273
+ function attributeValuesEqual(actual, expected) {
274
+ if (actual instanceof Date || expected instanceof Date)
275
+ return actual instanceof Date && expected instanceof Date && actual.getTime() === expected.getTime();
276
+ return actual === expected;
277
+ }
278
+ function attributeValueMatchesAnyOf(actual, expectedValues) {
279
+ if (expectedValues.length === 0)
280
+ return !1;
281
+ for (const actualValue of storedAttributeScalarValues(actual))
282
+ if (expectedValues.some((expectedValue) => attributeValuesEqual(actualValue, expectedValue)))
283
+ return !0;
284
+ return !1;
285
+ }
286
+ function encodedAttributeValueSatisfiesRange(encodedValue, filter) {
287
+ if (filter.gte !== void 0 && encodedValue < encodeAttributeValue(filter.gte))
288
+ return !1;
289
+ if (filter.gt !== void 0 && encodedValue <= encodeAttributeValue(filter.gt))
290
+ return !1;
291
+ if (filter.lte !== void 0 && encodedValue > encodeAttributeValue(filter.lte))
292
+ return !1;
293
+ if (filter.lt !== void 0 && encodedValue >= encodeAttributeValue(filter.lt))
294
+ return !1;
295
+ return !0;
296
+ }
297
+ function attributeValueMatchesRange(value, filter) {
298
+ for (const scalarValue of storedAttributeScalarValues(value))
299
+ if (encodedAttributeValueSatisfiesRange(encodeAttributeValue(scalarValue), filter))
300
+ return !0;
301
+ return !1;
302
+ }
303
+ function matchesAttributeFilter(searchAttributes, filter) {
304
+ const attributeValue = searchAttributes[searchAttributeName(filter.key)];
305
+ if (attributeValue === void 0)
306
+ return !1;
307
+ const exactValues = attributeFilterExactValues(filter);
308
+ if (exactValues !== null)
309
+ return attributeValueMatchesAnyOf(attributeValue, exactValues);
310
+ return attributeValueMatchesRange(attributeValue, filter);
311
+ }
312
+ function matchesListFilterAttributes(searchAttributes, filter) {
313
+ if (!listFilterHasAttributeFilters(filter))
314
+ return !0;
315
+ if (searchAttributes === null || searchAttributes === void 0)
316
+ return !1;
317
+ return filter.attributes.every((attributeFilter) => matchesAttributeFilter(searchAttributes, attributeFilter));
318
+ }
319
+ export function matchesListFilter(state, filter, constrainedIds, normalizedTagFilters, searchAttributes) {
211
320
  if (constrainedIds !== null && !constrainedIds.has(state.id))
212
321
  return !1;
213
322
  if (!matchesWorkflowTagFilter(state.tags, normalizedTagFilters))
@@ -220,6 +329,8 @@ export function matchesListFilter(state, filter, constrainedIds, normalizedTagFi
220
329
  return !1;
221
330
  if (!matchesListFilterFailureCategory(state, filter))
222
331
  return !1;
332
+ if (!matchesListFilterAttributes(searchAttributes, filter))
333
+ return !1;
223
334
  return !0;
224
335
  }
225
336
  function timestampInRange(value, range) {
@@ -7,8 +7,21 @@ export declare function runSerializedWorkflowStateWrite<TResult>(internals: Engi
7
7
  export declare function loadWorkflowState(internals: EngineInternals, workflowId: string): Promise<WorkflowState | null>;
8
8
  /** Load a terminal workflow result or throw the persisted terminal error. */
9
9
  export declare function loadWorkflowResult(internals: EngineInternals, workflowId: string): Promise<unknown>;
10
- /** Commit workflow state operations. */
11
- export declare function commitWorkflowStateOperations(internals: EngineInternals, _state: WorkflowState, operations: BatchOperation[]): Promise<void>;
10
+ type WorkflowStateCommitOptions = {
11
+ includePendingAtomicSideEffects?: boolean;
12
+ };
13
+ /**
14
+ * Commit an engine-generator-owned workflow-state advance, FENCED on the lease
15
+ * epoch under `ownership: 'lease'` (issue #470 Step 2). A deposed engine's write
16
+ * loses its CAS instead of corrupting the successor's state; the deposition is
17
+ * detected and the engine halts (see {@link commitFencedEngineWrite}). Under
18
+ * `ownership: 'none'` it is byte-for-byte the pre-Step-2 commit shape — the epoch
19
+ * condition is only appended when a lease is held. Use this for suspend,
20
+ * completion, and other state advances driven by the workflow lifecycle. Operator/
21
+ * external mutations (search-attribute and tag edits) do NOT use this helper; they
22
+ * batch directly and are intentionally never fenced.
23
+ */
24
+ export declare function commitFencedWorkflowStateOperations(internals: EngineInternals, state: WorkflowState, operations: BatchOperation[], options?: WorkflowStateCommitOptions): Promise<void>;
12
25
  /** Load and decode persisted schedule state by schedule ID. */
13
26
  export declare function loadScheduleState(internals: EngineInternals, scheduleId: string): Promise<ScheduleState | null>;
14
27
  /** Load a schedule state. */
@@ -19,3 +32,4 @@ export declare function writeScheduleState(internals: EngineInternals, state: Sc
19
32
  }): Promise<void>;
20
33
  /** Load persisted workflow start headers by workflow ID. */
21
34
  export declare function loadWorkflowStartHeaders(internals: EngineInternals, workflowId: string): Promise<Map<string, string> | undefined>;
35
+ export {};
@@ -1,8 +1,16 @@
1
- import { KEYS } from "../../storage/interface.js";
1
+ import {
2
+ KEYS
3
+ } from "../../storage/interface.js";
2
4
  import { encode } from "../codec.js";
3
5
  import { buildTimerBatchOperations } from "../scheduler.js";
4
6
  import { WorkflowTimeoutError } from "../timeouts.js";
7
+ import {
8
+ clearPendingAtomicWorkflowCommitSideEffects,
9
+ takePendingAtomicWorkflowCommitSideEffects
10
+ } from "./checkpoint-side-effects.js";
11
+ import { commitFencedEngineWrite } from "./fenced-write.js";
5
12
  import { getWorkflowExecutionStartedAt } from "./handles.js";
13
+ import { resolveEffectiveScheduleFireAt } from "./schedule-jitter.js";
6
14
  import { createScheduleTimerId, decodeWorkflowStartHeaders } from "./state-utilities.js";
7
15
  import { decodeWorkflowState } from "./validation.js";
8
16
  import { decodeScheduleState } from "./validation/schedule.js";
@@ -48,8 +56,19 @@ export async function loadWorkflowResult(internals, workflowId) {
48
56
  }
49
57
  throw Error(`Workflow "${workflowId}" is still ${state.status}`);
50
58
  }
51
- export async function commitWorkflowStateOperations(internals, _state, operations) {
52
- await internals.storage.batch(operations);
59
+ function buildWorkflowStateCommit(internals, workflowId, operations, options) {
60
+ const pendingSideEffects = options.includePendingAtomicSideEffects ? takePendingAtomicWorkflowCommitSideEffects(internals, workflowId) : void 0, operationsWithSideEffects = pendingSideEffects === void 0 ? operations : [...operations, ...pendingSideEffects.operations], conditions = internals.storage.capabilities().conditionalBatch ? pendingSideEffects?.conditions ?? [] : [];
61
+ return {
62
+ operations: operationsWithSideEffects,
63
+ conditions,
64
+ hasPendingSideEffects: pendingSideEffects !== void 0
65
+ };
66
+ }
67
+ export async function commitFencedWorkflowStateOperations(internals, state, operations, options = {}) {
68
+ const commit = buildWorkflowStateCommit(internals, state.id, operations, options);
69
+ await commitFencedEngineWrite(internals, commit.operations, commit.conditions, () => Error(`Workflow state commit for workflow "${state.id}" lost its atomic side-effect precondition.`));
70
+ if (commit.hasPendingSideEffects)
71
+ clearPendingAtomicWorkflowCommitSideEffects(internals, state.id);
53
72
  }
54
73
  export async function loadScheduleState(internals, scheduleId) {
55
74
  const bytes = await internals.storage.get(KEYS.schedule(scheduleId));
@@ -65,13 +84,15 @@ export async function writeScheduleState(internals, state, options) {
65
84
  const operations = [
66
85
  { type: "put", key: KEYS.schedule(state.id), value: encode(state) }
67
86
  ];
68
- if ((options?.includeTimer ?? state.status === "active") && state.status === "active" && state.nextFireAt !== null)
87
+ if ((options?.includeTimer ?? state.status === "active") && state.status === "active" && state.nextFireAt !== null) {
88
+ const effectiveFireAt = resolveEffectiveScheduleFireAt(state, state.nextFireAt);
69
89
  operations.push(...buildTimerBatchOperations({
70
90
  id: createScheduleTimerId(state.id),
71
91
  workflowId: state.id,
72
- fireAt: state.nextFireAt,
92
+ fireAt: effectiveFireAt,
73
93
  kind: "schedule"
74
94
  }));
95
+ }
75
96
  await internals.storage.batch(operations);
76
97
  }
77
98
  export async function loadWorkflowStartHeaders(internals, workflowId) {
@@ -3,3 +3,5 @@ import type { StoredStreamChunk } from '../context.ts';
3
3
  export declare function loadStoredStreamChunks(storage: Storage, workflowId: string, key: string, options?: {
4
4
  after?: number;
5
5
  }): Promise<StoredStreamChunk[]>;
6
+ export declare function encodeStoredStreamTailSequence(sequence: number): Uint8Array;
7
+ export declare function loadStoredStreamTailSequence(storage: Storage, workflowId: string, key: string): Promise<number | null>;
@@ -1,5 +1,5 @@
1
1
  import { KEYS } from "../../storage/interface.js";
2
- import { decode } from "../codec.js";
2
+ import { decode, encode } from "../codec.js";
3
3
  const STREAM_CHUNK_SEQUENCE_PATTERN = /^\d+$/;
4
4
  function parseStreamChunkSequence(sequenceText) {
5
5
  if (!STREAM_CHUNK_SEQUENCE_PATTERN.test(sequenceText))
@@ -20,3 +20,26 @@ export async function loadStoredStreamChunks(storage, workflowId, key, options)
20
20
  }
21
21
  return chunks;
22
22
  }
23
+ export function encodeStoredStreamTailSequence(sequence) {
24
+ return encode({ sequence });
25
+ }
26
+ function parseStoredStreamTailSequence(value) {
27
+ if (typeof value !== "object" || value === null || Array.isArray(value))
28
+ return null;
29
+ if (!("sequence" in value))
30
+ return null;
31
+ const sequence = value.sequence;
32
+ if (typeof sequence !== "number" || !Number.isSafeInteger(sequence) || sequence < -1)
33
+ return null;
34
+ return sequence;
35
+ }
36
+ export async function loadStoredStreamTailSequence(storage, workflowId, key) {
37
+ const bytes = await storage.get(KEYS.streamTail(workflowId, key));
38
+ if (bytes === null)
39
+ return null;
40
+ try {
41
+ return parseStoredStreamTailSequence(decode(bytes));
42
+ } catch {
43
+ return null;
44
+ }
45
+ }