@workflow/core 5.0.0-beta.5 → 5.0.0-beta.51

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 (317) hide show
  1. package/README.md +5 -0
  2. package/dist/attribute-changes.d.ts +5 -0
  3. package/dist/attribute-changes.d.ts.map +1 -0
  4. package/dist/attribute-changes.js +25 -0
  5. package/dist/capabilities.d.ts +35 -2
  6. package/dist/capabilities.d.ts.map +1 -1
  7. package/dist/capabilities.js +74 -5
  8. package/dist/capture-stack.d.ts +1 -1
  9. package/dist/capture-stack.js +2 -2
  10. package/dist/class-serialization.d.ts +32 -0
  11. package/dist/class-serialization.d.ts.map +1 -1
  12. package/dist/class-serialization.js +37 -1
  13. package/dist/classify-error.d.ts +19 -3
  14. package/dist/classify-error.d.ts.map +1 -1
  15. package/dist/classify-error.js +98 -6
  16. package/dist/context-violation-error.d.ts +5 -5
  17. package/dist/context-violation-error.d.ts.map +1 -1
  18. package/dist/context-violation-error.js +8 -6
  19. package/dist/create-hook.d.ts +72 -7
  20. package/dist/create-hook.d.ts.map +1 -1
  21. package/dist/create-hook.js +1 -1
  22. package/dist/define-hook.js +2 -2
  23. package/dist/describe-error.d.ts +7 -7
  24. package/dist/describe-error.d.ts.map +1 -1
  25. package/dist/describe-error.js +97 -12
  26. package/dist/encryption.d.ts +38 -4
  27. package/dist/encryption.d.ts.map +1 -1
  28. package/dist/encryption.js +96 -14
  29. package/dist/events-consumer.d.ts +297 -0
  30. package/dist/events-consumer.d.ts.map +1 -1
  31. package/dist/events-consumer.js +673 -42
  32. package/dist/flushable-stream.d.ts +97 -1
  33. package/dist/flushable-stream.d.ts.map +1 -1
  34. package/dist/flushable-stream.js +291 -11
  35. package/dist/global.d.ts +36 -2
  36. package/dist/global.d.ts.map +1 -1
  37. package/dist/global.js +21 -6
  38. package/dist/index.d.ts +2 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +3 -2
  41. package/dist/log-format.d.ts +2 -2
  42. package/dist/log-format.js +19 -12
  43. package/dist/logger.d.ts +1 -0
  44. package/dist/logger.d.ts.map +1 -1
  45. package/dist/logger.js +10 -6
  46. package/dist/private.d.ts +226 -9
  47. package/dist/private.d.ts.map +1 -1
  48. package/dist/private.js +626 -7
  49. package/dist/replay-divergence.d.ts +25 -0
  50. package/dist/replay-divergence.d.ts.map +1 -0
  51. package/dist/replay-divergence.js +61 -0
  52. package/dist/replay-payload-cache.d.ts +64 -0
  53. package/dist/replay-payload-cache.d.ts.map +1 -0
  54. package/dist/replay-payload-cache.js +158 -0
  55. package/dist/runtime/compute-instance.d.ts +13 -0
  56. package/dist/runtime/compute-instance.d.ts.map +1 -0
  57. package/dist/runtime/compute-instance.js +14 -0
  58. package/dist/runtime/constants.d.ts +315 -0
  59. package/dist/runtime/constants.d.ts.map +1 -1
  60. package/dist/runtime/constants.js +502 -16
  61. package/dist/runtime/count-step-started-events.d.ts +52 -0
  62. package/dist/runtime/count-step-started-events.d.ts.map +1 -0
  63. package/dist/runtime/count-step-started-events.js +72 -0
  64. package/dist/runtime/deployment-guard.d.ts +99 -0
  65. package/dist/runtime/deployment-guard.d.ts.map +1 -0
  66. package/dist/runtime/deployment-guard.js +154 -0
  67. package/dist/runtime/get-port-lazy.d.ts +15 -0
  68. package/dist/runtime/get-port-lazy.d.ts.map +1 -1
  69. package/dist/runtime/get-port-lazy.js +75 -6
  70. package/dist/runtime/get-world-lazy.d.ts +5 -14
  71. package/dist/runtime/get-world-lazy.d.ts.map +1 -1
  72. package/dist/runtime/get-world-lazy.js +19 -29
  73. package/dist/runtime/helpers.d.ts +415 -29
  74. package/dist/runtime/helpers.d.ts.map +1 -1
  75. package/dist/runtime/helpers.js +686 -57
  76. package/dist/runtime/quickjs-assets.generated.d.ts +14 -0
  77. package/dist/runtime/quickjs-assets.generated.d.ts.map +1 -0
  78. package/dist/runtime/quickjs-assets.generated.js +30 -0
  79. package/dist/runtime/quickjs-entrypoint.d.ts +158 -0
  80. package/dist/runtime/quickjs-entrypoint.d.ts.map +1 -0
  81. package/dist/runtime/quickjs-entrypoint.js +1863 -0
  82. package/dist/runtime/quickjs-log-view.d.ts +129 -0
  83. package/dist/runtime/quickjs-log-view.d.ts.map +1 -0
  84. package/dist/runtime/quickjs-log-view.js +229 -0
  85. package/dist/runtime/quickjs-runtime.d.ts +244 -0
  86. package/dist/runtime/quickjs-runtime.d.ts.map +1 -0
  87. package/dist/runtime/quickjs-runtime.js +2427 -0
  88. package/dist/runtime/quickjs-serde.d.ts +107 -0
  89. package/dist/runtime/quickjs-serde.d.ts.map +1 -0
  90. package/dist/runtime/quickjs-serde.js +2159 -0
  91. package/dist/runtime/replay-budget.d.ts +97 -0
  92. package/dist/runtime/replay-budget.d.ts.map +1 -0
  93. package/dist/runtime/replay-budget.js +139 -0
  94. package/dist/runtime/replay-recovery-reporter.d.ts +36 -0
  95. package/dist/runtime/replay-recovery-reporter.d.ts.map +1 -0
  96. package/dist/runtime/replay-recovery-reporter.js +64 -0
  97. package/dist/runtime/resume-hook.d.ts +91 -8
  98. package/dist/runtime/resume-hook.d.ts.map +1 -1
  99. package/dist/runtime/resume-hook.js +512 -89
  100. package/dist/runtime/resume-latency.d.ts +150 -0
  101. package/dist/runtime/resume-latency.d.ts.map +1 -0
  102. package/dist/runtime/resume-latency.js +155 -0
  103. package/dist/runtime/run-id-time.d.ts +19 -0
  104. package/dist/runtime/run-id-time.d.ts.map +1 -0
  105. package/dist/runtime/run-id-time.js +42 -0
  106. package/dist/runtime/run.d.ts +72 -7
  107. package/dist/runtime/run.d.ts.map +1 -1
  108. package/dist/runtime/run.js +280 -53
  109. package/dist/runtime/runs.d.ts +54 -3
  110. package/dist/runtime/runs.d.ts.map +1 -1
  111. package/dist/runtime/runs.js +126 -16
  112. package/dist/runtime/start.d.ts +101 -2
  113. package/dist/runtime/start.d.ts.map +1 -1
  114. package/dist/runtime/start.js +352 -61
  115. package/dist/runtime/step-executor.d.ts +186 -5
  116. package/dist/runtime/step-executor.d.ts.map +1 -1
  117. package/dist/runtime/step-executor.js +768 -130
  118. package/dist/runtime/step-latency.d.ts +220 -0
  119. package/dist/runtime/step-latency.d.ts.map +1 -0
  120. package/dist/runtime/step-latency.js +215 -0
  121. package/dist/runtime/step-ownership.d.ts +72 -0
  122. package/dist/runtime/step-ownership.d.ts.map +1 -0
  123. package/dist/runtime/step-ownership.js +114 -0
  124. package/dist/runtime/step-single-flight.d.ts +12 -0
  125. package/dist/runtime/step-single-flight.d.ts.map +1 -0
  126. package/dist/runtime/step-single-flight.js +75 -0
  127. package/dist/runtime/suspension-handler.d.ts +247 -7
  128. package/dist/runtime/suspension-handler.d.ts.map +1 -1
  129. package/dist/runtime/suspension-handler.js +1304 -125
  130. package/dist/runtime/unserializable-step.d.ts +34 -0
  131. package/dist/runtime/unserializable-step.d.ts.map +1 -0
  132. package/dist/runtime/unserializable-step.js +52 -0
  133. package/dist/runtime/vm-mode.d.ts +44 -0
  134. package/dist/runtime/vm-mode.d.ts.map +1 -0
  135. package/dist/runtime/vm-mode.js +62 -0
  136. package/dist/runtime/vm-serde-bundle.generated.d.ts +14 -0
  137. package/dist/runtime/vm-serde-bundle.generated.d.ts.map +1 -0
  138. package/dist/runtime/vm-serde-bundle.generated.js +16 -0
  139. package/dist/runtime/wait-continuation.d.ts +106 -0
  140. package/dist/runtime/wait-continuation.d.ts.map +1 -0
  141. package/dist/runtime/wait-continuation.js +136 -0
  142. package/dist/runtime/wait-until.d.ts +18 -0
  143. package/dist/runtime/wait-until.d.ts.map +1 -0
  144. package/dist/runtime/wait-until.js +42 -0
  145. package/dist/runtime/world-compatibility.d.ts +32 -0
  146. package/dist/runtime/world-compatibility.d.ts.map +1 -0
  147. package/dist/runtime/world-compatibility.js +44 -0
  148. package/dist/runtime/world-init.d.ts +17 -42
  149. package/dist/runtime/world-init.d.ts.map +1 -1
  150. package/dist/runtime/world-init.js +18 -43
  151. package/dist/runtime/world.d.ts +14 -2
  152. package/dist/runtime/world.d.ts.map +1 -1
  153. package/dist/runtime/world.js +75 -19
  154. package/dist/runtime.d.ts +10 -6
  155. package/dist/runtime.d.ts.map +1 -1
  156. package/dist/runtime.js +3911 -489
  157. package/dist/sealed-box.d.ts +167 -0
  158. package/dist/sealed-box.d.ts.map +1 -0
  159. package/dist/sealed-box.js +571 -0
  160. package/dist/serialization/client.d.ts +3 -3
  161. package/dist/serialization/client.d.ts.map +1 -1
  162. package/dist/serialization/client.js +8 -4
  163. package/dist/serialization/codec-devalue-vm.d.ts +16 -0
  164. package/dist/serialization/codec-devalue-vm.d.ts.map +1 -0
  165. package/dist/serialization/codec-devalue-vm.js +148 -0
  166. package/dist/serialization/codec-devalue.d.ts +1 -1
  167. package/dist/serialization/codec-devalue.d.ts.map +1 -1
  168. package/dist/serialization/codec-devalue.js +24 -6
  169. package/dist/serialization/codec.d.ts +37 -2
  170. package/dist/serialization/codec.d.ts.map +1 -1
  171. package/dist/serialization/codec.js +2 -2
  172. package/dist/serialization/compression.d.ts +105 -0
  173. package/dist/serialization/compression.d.ts.map +1 -0
  174. package/dist/serialization/compression.js +261 -0
  175. package/dist/serialization/encryption.d.ts +119 -17
  176. package/dist/serialization/encryption.d.ts.map +1 -1
  177. package/dist/serialization/encryption.js +152 -17
  178. package/dist/serialization/errors.d.ts +13 -0
  179. package/dist/serialization/errors.d.ts.map +1 -1
  180. package/dist/serialization/errors.js +20 -2
  181. package/dist/serialization/format.d.ts +6 -6
  182. package/dist/serialization/format.js +7 -7
  183. package/dist/serialization/hardened.d.ts +156 -0
  184. package/dist/serialization/hardened.d.ts.map +1 -0
  185. package/dist/serialization/hardened.js +538 -0
  186. package/dist/serialization/index.d.ts +10 -8
  187. package/dist/serialization/index.d.ts.map +1 -1
  188. package/dist/serialization/index.js +10 -8
  189. package/dist/serialization/reducers/class-vm.d.ts +20 -0
  190. package/dist/serialization/reducers/class-vm.d.ts.map +1 -0
  191. package/dist/serialization/reducers/class-vm.js +77 -0
  192. package/dist/serialization/reducers/class.d.ts.map +1 -1
  193. package/dist/serialization/reducers/class.js +11 -6
  194. package/dist/serialization/reducers/common-vm.d.ts +15 -0
  195. package/dist/serialization/reducers/common-vm.d.ts.map +1 -0
  196. package/dist/serialization/reducers/common-vm.js +616 -0
  197. package/dist/serialization/reducers/common.d.ts +1 -1
  198. package/dist/serialization/reducers/common.d.ts.map +1 -1
  199. package/dist/serialization/reducers/common.js +202 -62
  200. package/dist/serialization/reducers/step-function-vm.d.ts +44 -0
  201. package/dist/serialization/reducers/step-function-vm.d.ts.map +1 -0
  202. package/dist/serialization/reducers/step-function-vm.js +97 -0
  203. package/dist/serialization/reducers/step-function.d.ts.map +1 -1
  204. package/dist/serialization/reducers/step-function.js +25 -9
  205. package/dist/serialization/step.d.ts +4 -4
  206. package/dist/serialization/step.d.ts.map +1 -1
  207. package/dist/serialization/step.js +9 -5
  208. package/dist/serialization/types.d.ts +80 -1
  209. package/dist/serialization/types.d.ts.map +1 -1
  210. package/dist/serialization/types.js +15 -1
  211. package/dist/serialization/workflow-vm.d.ts +29 -0
  212. package/dist/serialization/workflow-vm.d.ts.map +1 -0
  213. package/dist/serialization/workflow-vm.js +74 -0
  214. package/dist/serialization-format.d.ts +64 -8
  215. package/dist/serialization-format.d.ts.map +1 -1
  216. package/dist/serialization-format.js +225 -73
  217. package/dist/serialization.d.ts +262 -34
  218. package/dist/serialization.d.ts.map +1 -1
  219. package/dist/serialization.js +1885 -277
  220. package/dist/set-attributes.d.ts +13 -0
  221. package/dist/set-attributes.d.ts.map +1 -0
  222. package/dist/set-attributes.js +60 -0
  223. package/dist/source-map.d.ts +25 -0
  224. package/dist/source-map.d.ts.map +1 -1
  225. package/dist/source-map.js +154 -10
  226. package/dist/step/context-storage.d.ts +63 -2
  227. package/dist/step/context-storage.d.ts.map +1 -1
  228. package/dist/step/context-storage.js +7 -5
  229. package/dist/step/writable-stream.d.ts +3 -3
  230. package/dist/step/writable-stream.d.ts.map +1 -1
  231. package/dist/step/writable-stream.js +80 -13
  232. package/dist/step.d.ts.map +1 -1
  233. package/dist/step.js +148 -27
  234. package/dist/symbols.d.ts +50 -0
  235. package/dist/symbols.d.ts.map +1 -1
  236. package/dist/symbols.js +51 -1
  237. package/dist/telemetry/semantic-conventions.d.ts +433 -2
  238. package/dist/telemetry/semantic-conventions.d.ts.map +1 -1
  239. package/dist/telemetry/semantic-conventions.js +319 -3
  240. package/dist/telemetry.d.ts +91 -0
  241. package/dist/telemetry.d.ts.map +1 -1
  242. package/dist/telemetry.js +244 -17
  243. package/dist/test-support/orchestrator-context.d.ts +25 -0
  244. package/dist/test-support/orchestrator-context.d.ts.map +1 -0
  245. package/dist/test-support/orchestrator-context.js +107 -0
  246. package/dist/types.d.ts +6 -0
  247. package/dist/types.d.ts.map +1 -1
  248. package/dist/types.js +23 -1
  249. package/dist/util.d.ts +0 -5
  250. package/dist/util.d.ts.map +1 -1
  251. package/dist/util.js +1 -15
  252. package/dist/version.d.ts +1 -1
  253. package/dist/version.d.ts.map +1 -1
  254. package/dist/version.js +2 -2
  255. package/dist/vm/index.d.ts.map +1 -1
  256. package/dist/vm/index.js +84 -14
  257. package/dist/vm/script-cache.d.ts +26 -0
  258. package/dist/vm/script-cache.d.ts.map +1 -0
  259. package/dist/vm/script-cache.js +131 -0
  260. package/dist/vm/uint8array-base64.js +1 -1
  261. package/dist/workflow/abort-controller.d.ts +2 -2
  262. package/dist/workflow/abort-controller.d.ts.map +1 -1
  263. package/dist/workflow/abort-controller.js +88 -21
  264. package/dist/workflow/attribute-dispatcher.d.ts +6 -0
  265. package/dist/workflow/attribute-dispatcher.d.ts.map +1 -0
  266. package/dist/workflow/attribute-dispatcher.js +45 -0
  267. package/dist/workflow/create-hook.d.ts.map +1 -1
  268. package/dist/workflow/create-hook.js +24 -3
  269. package/dist/workflow/get-workflow-metadata.js +2 -2
  270. package/dist/workflow/hook.d.ts.map +1 -1
  271. package/dist/workflow/hook.js +304 -47
  272. package/dist/workflow/index.d.ts +1 -0
  273. package/dist/workflow/index.d.ts.map +1 -1
  274. package/dist/workflow/index.js +2 -1
  275. package/dist/workflow/set-attributes.d.ts +68 -0
  276. package/dist/workflow/set-attributes.d.ts.map +1 -0
  277. package/dist/workflow/set-attributes.js +60 -0
  278. package/dist/workflow/sleep.d.ts.map +1 -1
  279. package/dist/workflow/sleep.js +58 -12
  280. package/dist/workflow/world-init-stub.d.ts +3 -3
  281. package/dist/workflow/world-init-stub.js +4 -4
  282. package/dist/workflow.d.ts +95 -3
  283. package/dist/workflow.d.ts.map +1 -1
  284. package/dist/workflow.js +900 -561
  285. package/docs/api-reference/create-hook.mdx +89 -10
  286. package/docs/api-reference/create-webhook.mdx +16 -15
  287. package/docs/api-reference/define-hook.mdx +35 -33
  288. package/docs/api-reference/fatal-error.mdx +30 -8
  289. package/docs/api-reference/fetch.mdx +14 -10
  290. package/docs/api-reference/get-step-metadata.mdx +2 -2
  291. package/docs/api-reference/get-workflow-metadata.mdx +3 -3
  292. package/docs/api-reference/get-writable.mdx +7 -7
  293. package/docs/api-reference/index.mdx +4 -1
  294. package/docs/api-reference/retryable-error.mdx +1 -1
  295. package/docs/api-reference/set-attributes.mdx +61 -0
  296. package/docs/api-reference/sleep.mdx +4 -4
  297. package/docs/foundations/cancellation.mdx +31 -32
  298. package/docs/foundations/errors-and-retries.mdx +42 -11
  299. package/docs/foundations/hooks.mdx +64 -35
  300. package/docs/foundations/idempotency.mdx +244 -12
  301. package/docs/foundations/index.mdx +1 -23
  302. package/docs/foundations/meta.json +2 -1
  303. package/docs/foundations/serialization.mdx +21 -22
  304. package/docs/foundations/starting-workflows.mdx +106 -30
  305. package/docs/foundations/streaming.mdx +108 -60
  306. package/docs/foundations/versioning.mdx +263 -0
  307. package/docs/foundations/workflows-and-steps.mdx +9 -9
  308. package/docs/how-it-works/cancellation.mdx +63 -63
  309. package/docs/how-it-works/code-transform.mdx +83 -67
  310. package/docs/how-it-works/encryption.mdx +30 -26
  311. package/docs/how-it-works/event-sourcing.mdx +125 -34
  312. package/docs/how-it-works/framework-integrations.mdx +96 -337
  313. package/docs/how-it-works/understanding-directives.mdx +22 -22
  314. package/package.json +24 -15
  315. package/dist/runtime/step-handler.d.ts +0 -2
  316. package/dist/runtime/step-handler.d.ts.map +0 -1
  317. package/dist/runtime/step-handler.js +0 -776
@@ -0,0 +1,97 @@
1
+ import { type SlotSnapshotParams } from './helpers.js';
2
+ /**
3
+ * Per-invocation accounting of the *non-step* portion of a workflow
4
+ * handler run: deterministic event-log replay, workflow-VM execution
5
+ * between step boundaries, suspension handling, queue round-trips, etc.
6
+ * Inline step bodies (`"use step"` functions invoked via `executeStep`)
7
+ * are intentionally excluded since they are bounded by the platform's
8
+ * function `maxDuration` and the `NO_INLINE_REPLAY_AFTER_MS` early-return
9
+ * guard.
10
+ *
11
+ * Usage:
12
+ *
13
+ * ```ts
14
+ * const budget = new ReplayBudget();
15
+ * // …non-step work happens here, accumulates against the budget…
16
+ * budget.pause();
17
+ * try {
18
+ * await executeStep(...); // not charged
19
+ * } finally {
20
+ * budget.resume();
21
+ * }
22
+ * // back to charging
23
+ * if (budget.isExhausted()) { ... }
24
+ * ```
25
+ *
26
+ * Implementation notes:
27
+ *
28
+ * - `pause()` and `resume()` are idempotent: calling `pause()` while
29
+ * already paused (or `resume()` while already resumed) is a no-op.
30
+ * This protects against double-counting in future refactors that nest
31
+ * step execution or take an early-return path between a `pause()` and
32
+ * the matching `resume()`.
33
+ * - `isExhausted()` is checked at loop boundaries by the caller; the
34
+ * budget itself does not arm any timers. This means an in-flight
35
+ * pathological `runWorkflow` call (e.g. a huge event-log replay) can
36
+ * overshoot the budget by up to one iteration's worth of work before
37
+ * the next check fires. In practice the 20s headroom built into
38
+ * `MAX_REPLAY_TIMEOUT_MS` (and the function `maxDuration` ceiling)
39
+ * gives us slack; the old `setTimeout`-based approach also ultimately
40
+ * relied on the platform SIGTERM as the hard backstop.
41
+ */
42
+ export declare class ReplayBudget {
43
+ private readonly limitMs;
44
+ private elapsedMs;
45
+ private intervalStart;
46
+ constructor(limitMs?: number);
47
+ /**
48
+ * The configured replay-timeout limit, in ms. Useful for log messages.
49
+ */
50
+ get configuredLimitMs(): number;
51
+ /**
52
+ * Total non-step time accumulated so far, in ms. Includes the
53
+ * currently-active interval if the budget is not paused.
54
+ */
55
+ elapsed(): number;
56
+ /**
57
+ * Stop counting elapsed time toward the budget. Idempotent: safe to
58
+ * call multiple times in a row; subsequent calls are no-ops until
59
+ * `resume()` reopens an interval.
60
+ */
61
+ pause(): void;
62
+ /**
63
+ * Resume counting elapsed time toward the budget. Idempotent: safe to
64
+ * call multiple times in a row; subsequent calls re-anchor the
65
+ * interval start to `now()`, which is fine because no time accrues
66
+ * between back-to-back `resume()` calls.
67
+ */
68
+ resume(): void;
69
+ /**
70
+ * True if the budget has been exhausted (`elapsed() >= limitMs`).
71
+ * Callers should invoke `handleExhausted(...)` afterward and return
72
+ * from the handler.
73
+ */
74
+ isExhausted(): boolean;
75
+ }
76
+ export declare class ReplayTimeoutRetryError extends Error {
77
+ readonly name = "ReplayTimeoutRetryError";
78
+ }
79
+ /**
80
+ * Reject the current delivery while replay-timeout retries remain. Once
81
+ * exhausted, fail the run and let the queue acknowledge the delivery.
82
+ */
83
+ export declare function handleReplayBudgetExhausted(args: {
84
+ runId: string;
85
+ workflowName: string;
86
+ requestId: string | undefined;
87
+ attempt: number;
88
+ limitMs: number;
89
+ /**
90
+ * How much of the log the replay that ran out of budget had loaded. Carried
91
+ * onto the terminal write for the same reason every other write from that
92
+ * replay carries it: the run is being failed on a view of the log, and the
93
+ * World should be told which view.
94
+ */
95
+ slotSnapshot?: SlotSnapshotParams;
96
+ }): Promise<void>;
97
+ //# sourceMappingURL=replay-budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay-budget.d.ts","sourceRoot":"","sources":["../../src/runtime/replay-budget.ts"],"names":[],"mappings":"AAMA,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAG7E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,aAAa,CAAgB;gBAEzB,OAAO,GAAE,MAA6B;IAKlD;;OAEG;IACH,IAAI,iBAAiB,IAAI,MAAM,CAE9B;IAED;;;OAGG;IACH,OAAO,IAAI,MAAM;IAMjB;;;;OAIG;IACH,KAAK,IAAI,IAAI;IAMb;;;;;OAKG;IACH,MAAM,IAAI,IAAI;IAId;;;;OAIG;IACH,WAAW,IAAI,OAAO;CAGvB;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,SAAkB,IAAI,6BAA6B;CACpD;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,CAAC,IAAI,EAAE;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,kBAAkB,CAAC;CACnC,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDhB"}
@@ -0,0 +1,139 @@
1
+ import { FatalError, RUN_ERROR_CODES } from '@workflow/errors';
2
+ import { SPEC_VERSION_CURRENT } from '@workflow/world';
3
+ import { describeError } from '../describe-error.js';
4
+ import { runtimeLogger } from '../logger.js';
5
+ import { dehydrateRunError } from '../serialization.js';
6
+ import { getReplayTimeoutMaxRetries, getReplayTimeoutMs } from './constants.js';
7
+ import { memoizeEncryptionKey } from './helpers.js';
8
+ import { getWorld } from './world.js';
9
+ /**
10
+ * Per-invocation accounting of the *non-step* portion of a workflow
11
+ * handler run: deterministic event-log replay, workflow-VM execution
12
+ * between step boundaries, suspension handling, queue round-trips, etc.
13
+ * Inline step bodies (`"use step"` functions invoked via `executeStep`)
14
+ * are intentionally excluded since they are bounded by the platform's
15
+ * function `maxDuration` and the `NO_INLINE_REPLAY_AFTER_MS` early-return
16
+ * guard.
17
+ *
18
+ * Usage:
19
+ *
20
+ * ```ts
21
+ * const budget = new ReplayBudget();
22
+ * // …non-step work happens here, accumulates against the budget…
23
+ * budget.pause();
24
+ * try {
25
+ * await executeStep(...); // not charged
26
+ * } finally {
27
+ * budget.resume();
28
+ * }
29
+ * // back to charging
30
+ * if (budget.isExhausted()) { ... }
31
+ * ```
32
+ *
33
+ * Implementation notes:
34
+ *
35
+ * - `pause()` and `resume()` are idempotent: calling `pause()` while
36
+ * already paused (or `resume()` while already resumed) is a no-op.
37
+ * This protects against double-counting in future refactors that nest
38
+ * step execution or take an early-return path between a `pause()` and
39
+ * the matching `resume()`.
40
+ * - `isExhausted()` is checked at loop boundaries by the caller; the
41
+ * budget itself does not arm any timers. This means an in-flight
42
+ * pathological `runWorkflow` call (e.g. a huge event-log replay) can
43
+ * overshoot the budget by up to one iteration's worth of work before
44
+ * the next check fires. In practice the 20s headroom built into
45
+ * `MAX_REPLAY_TIMEOUT_MS` (and the function `maxDuration` ceiling)
46
+ * gives us slack; the old `setTimeout`-based approach also ultimately
47
+ * relied on the platform SIGTERM as the hard backstop.
48
+ */
49
+ export class ReplayBudget {
50
+ limitMs;
51
+ elapsedMs = 0;
52
+ intervalStart;
53
+ constructor(limitMs = getReplayTimeoutMs()) {
54
+ this.limitMs = limitMs;
55
+ this.intervalStart = Date.now();
56
+ }
57
+ /**
58
+ * The configured replay-timeout limit, in ms. Useful for log messages.
59
+ */
60
+ get configuredLimitMs() {
61
+ return this.limitMs;
62
+ }
63
+ /**
64
+ * Total non-step time accumulated so far, in ms. Includes the
65
+ * currently-active interval if the budget is not paused.
66
+ */
67
+ elapsed() {
68
+ const open = this.intervalStart === null ? 0 : Date.now() - this.intervalStart;
69
+ return this.elapsedMs + open;
70
+ }
71
+ /**
72
+ * Stop counting elapsed time toward the budget. Idempotent: safe to
73
+ * call multiple times in a row; subsequent calls are no-ops until
74
+ * `resume()` reopens an interval.
75
+ */
76
+ pause() {
77
+ if (this.intervalStart === null)
78
+ return;
79
+ this.elapsedMs += Date.now() - this.intervalStart;
80
+ this.intervalStart = null;
81
+ }
82
+ /**
83
+ * Resume counting elapsed time toward the budget. Idempotent: safe to
84
+ * call multiple times in a row; subsequent calls re-anchor the
85
+ * interval start to `now()`, which is fine because no time accrues
86
+ * between back-to-back `resume()` calls.
87
+ */
88
+ resume() {
89
+ this.intervalStart = Date.now();
90
+ }
91
+ /**
92
+ * True if the budget has been exhausted (`elapsed() >= limitMs`).
93
+ * Callers should invoke `handleExhausted(...)` afterward and return
94
+ * from the handler.
95
+ */
96
+ isExhausted() {
97
+ return this.elapsed() >= this.limitMs;
98
+ }
99
+ }
100
+ export class ReplayTimeoutRetryError extends Error {
101
+ name = 'ReplayTimeoutRetryError';
102
+ }
103
+ /**
104
+ * Reject the current delivery while replay-timeout retries remain. Once
105
+ * exhausted, fail the run and let the queue acknowledge the delivery.
106
+ */
107
+ export async function handleReplayBudgetExhausted(args) {
108
+ const { runId, workflowName, requestId, attempt, limitMs, slotSnapshot } = args;
109
+ const runLogger = runtimeLogger.forRun(runId, workflowName);
110
+ const maxRetries = getReplayTimeoutMaxRetries();
111
+ if (attempt <= maxRetries) {
112
+ runLogger.warn('Workflow replay exceeded timeout; retrying delivery', {
113
+ timeoutMs: limitMs,
114
+ attempt,
115
+ maxRetries,
116
+ });
117
+ throw new ReplayTimeoutRetryError(`Workflow replay exceeded maximum duration (${limitMs / 1000}s)`);
118
+ }
119
+ const world = await getWorld();
120
+ const replayTimeoutDescription = describeError(undefined, RUN_ERROR_CODES.REPLAY_TIMEOUT);
121
+ runLogger.error('Workflow replay exceeded timeout and max retries exceeded. Failing the run', {
122
+ timeoutMs: limitMs,
123
+ attempt,
124
+ maxRetries,
125
+ errorCode: replayTimeoutDescription.errorCode,
126
+ errorAttribution: replayTimeoutDescription.attribution,
127
+ });
128
+ const getEncryptionKey = memoizeEncryptionKey(world, runId);
129
+ const timeoutErr = new FatalError(`Workflow replay exceeded maximum duration (${limitMs / 1000}s) after ${attempt} attempts`);
130
+ await world.events.create(runId, {
131
+ eventType: 'run_failed',
132
+ specVersion: SPEC_VERSION_CURRENT,
133
+ eventData: {
134
+ error: await dehydrateRunError(timeoutErr, runId, await getEncryptionKey()),
135
+ errorCode: RUN_ERROR_CODES.REPLAY_TIMEOUT,
136
+ },
137
+ }, { requestId, ...slotSnapshot });
138
+ }
139
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVwbGF5LWJ1ZGdldC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydW50aW1lL3JlcGxheS1idWRnZXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxlQUFlLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUMvRCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN2RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDckQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUM3QyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsMEJBQTBCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNoRixPQUFPLEVBQUUsb0JBQW9CLEVBQTJCLE1BQU0sY0FBYyxDQUFDO0FBQzdFLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFFdEM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXVDRztBQUNILE1BQU0sT0FBTyxZQUFZO0lBQ04sT0FBTyxDQUFTO0lBQ3pCLFNBQVMsR0FBRyxDQUFDLENBQUM7SUFDZCxhQUFhLENBQWdCO0lBRXJDLFlBQVksVUFBa0Isa0JBQWtCLEVBQUU7UUFDaEQsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFDdkIsSUFBSSxDQUFDLGFBQWEsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDbEMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsSUFBSSxpQkFBaUI7UUFDbkIsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDO0lBQ3RCLENBQUM7SUFFRDs7O09BR0c7SUFDSCxPQUFPO1FBQ0wsTUFBTSxJQUFJLEdBQ1IsSUFBSSxDQUFDLGFBQWEsS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUM7UUFDcEUsT0FBTyxJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztJQUMvQixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILEtBQUs7UUFDSCxJQUFJLElBQUksQ0FBQyxhQUFhLEtBQUssSUFBSTtZQUFFLE9BQU87UUFDeEMsSUFBSSxDQUFDLFNBQVMsSUFBSSxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQztRQUNsRCxJQUFJLENBQUMsYUFBYSxHQUFHLElBQUksQ0FBQztJQUM1QixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSCxNQUFNO1FBQ0osSUFBSSxDQUFDLGFBQWEsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDbEMsQ0FBQztJQUVEOzs7O09BSUc7SUFDSCxXQUFXO1FBQ1QsT0FBTyxJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQztJQUN4QyxDQUFDO0NBQ0Y7QUFFRCxNQUFNLE9BQU8sdUJBQXdCLFNBQVEsS0FBSztJQUM5QixJQUFJLEdBQUcseUJBQXlCLENBQUM7Q0FDcEQ7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLDJCQUEyQixDQUFDLElBYWpEO0lBQ0MsTUFBTSxFQUFFLEtBQUssRUFBRSxZQUFZLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsWUFBWSxFQUFFLEdBQ3RFLElBQUksQ0FBQztJQUNQLE1BQU0sU0FBUyxHQUFHLGFBQWEsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLFlBQVksQ0FBQyxDQUFDO0lBRTVELE1BQU0sVUFBVSxHQUFHLDBCQUEwQixFQUFFLENBQUM7SUFDaEQsSUFBSSxPQUFPLElBQUksVUFBVSxFQUFFLENBQUM7UUFDMUIsU0FBUyxDQUFDLElBQUksQ0FBQyxxREFBcUQsRUFBRTtZQUNwRSxTQUFTLEVBQUUsT0FBTztZQUNsQixPQUFPO1lBQ1AsVUFBVTtTQUNYLENBQUMsQ0FBQztRQUNILE1BQU0sSUFBSSx1QkFBdUIsQ0FDL0IsOENBQThDLE9BQU8sR0FBRyxJQUFJLElBQUksQ0FDakUsQ0FBQztJQUNKLENBQUM7SUFFRCxNQUFNLEtBQUssR0FBRyxNQUFNLFFBQVEsRUFBRSxDQUFDO0lBRS9CLE1BQU0sd0JBQXdCLEdBQUcsYUFBYSxDQUM1QyxTQUFTLEVBQ1QsZUFBZSxDQUFDLGNBQWMsQ0FDL0IsQ0FBQztJQUNGLFNBQVMsQ0FBQyxLQUFLLENBQ2IsNEVBQTRFLEVBQzVFO1FBQ0UsU0FBUyxFQUFFLE9BQU87UUFDbEIsT0FBTztRQUNQLFVBQVU7UUFDVixTQUFTLEVBQUUsd0JBQXdCLENBQUMsU0FBUztRQUM3QyxnQkFBZ0IsRUFBRSx3QkFBd0IsQ0FBQyxXQUFXO0tBQ3ZELENBQ0YsQ0FBQztJQUVGLE1BQU0sZ0JBQWdCLEdBQUcsb0JBQW9CLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQzVELE1BQU0sVUFBVSxHQUFHLElBQUksVUFBVSxDQUMvQiw4Q0FBOEMsT0FBTyxHQUFHLElBQUksWUFBWSxPQUFPLFdBQVcsQ0FDM0YsQ0FBQztJQUNGLE1BQU0sS0FBSyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQ3ZCLEtBQUssRUFDTDtRQUNFLFNBQVMsRUFBRSxZQUFZO1FBQ3ZCLFdBQVcsRUFBRSxvQkFBb0I7UUFDakMsU0FBUyxFQUFFO1lBQ1QsS0FBSyxFQUFFLE1BQU0saUJBQWlCLENBQzVCLFVBQVUsRUFDVixLQUFLLEVBQ0wsTUFBTSxnQkFBZ0IsRUFBRSxDQUN6QjtZQUNELFNBQVMsRUFBRSxlQUFlLENBQUMsY0FBYztTQUMxQztLQUNGLEVBQ0QsRUFBRSxTQUFTLEVBQUUsR0FBRyxZQUFZLEVBQUUsQ0FDL0IsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBGYXRhbEVycm9yLCBSVU5fRVJST1JfQ09ERVMgfSBmcm9tICdAd29ya2Zsb3cvZXJyb3JzJztcbmltcG9ydCB7IFNQRUNfVkVSU0lPTl9DVVJSRU5UIH0gZnJvbSAnQHdvcmtmbG93L3dvcmxkJztcbmltcG9ydCB7IGRlc2NyaWJlRXJyb3IgfSBmcm9tICcuLi9kZXNjcmliZS1lcnJvci5qcyc7XG5pbXBvcnQgeyBydW50aW1lTG9nZ2VyIH0gZnJvbSAnLi4vbG9nZ2VyLmpzJztcbmltcG9ydCB7IGRlaHlkcmF0ZVJ1bkVycm9yIH0gZnJvbSAnLi4vc2VyaWFsaXphdGlvbi5qcyc7XG5pbXBvcnQgeyBnZXRSZXBsYXlUaW1lb3V0TWF4UmV0cmllcywgZ2V0UmVwbGF5VGltZW91dE1zIH0gZnJvbSAnLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHsgbWVtb2l6ZUVuY3J5cHRpb25LZXksIHR5cGUgU2xvdFNuYXBzaG90UGFyYW1zIH0gZnJvbSAnLi9oZWxwZXJzLmpzJztcbmltcG9ydCB7IGdldFdvcmxkIH0gZnJvbSAnLi93b3JsZC5qcyc7XG5cbi8qKlxuICogUGVyLWludm9jYXRpb24gYWNjb3VudGluZyBvZiB0aGUgKm5vbi1zdGVwKiBwb3J0aW9uIG9mIGEgd29ya2Zsb3dcbiAqIGhhbmRsZXIgcnVuOiBkZXRlcm1pbmlzdGljIGV2ZW50LWxvZyByZXBsYXksIHdvcmtmbG93LVZNIGV4ZWN1dGlvblxuICogYmV0d2VlbiBzdGVwIGJvdW5kYXJpZXMsIHN1c3BlbnNpb24gaGFuZGxpbmcsIHF1ZXVlIHJvdW5kLXRyaXBzLCBldGMuXG4gKiBJbmxpbmUgc3RlcCBib2RpZXMgKGBcInVzZSBzdGVwXCJgIGZ1bmN0aW9ucyBpbnZva2VkIHZpYSBgZXhlY3V0ZVN0ZXBgKVxuICogYXJlIGludGVudGlvbmFsbHkgZXhjbHVkZWQgc2luY2UgdGhleSBhcmUgYm91bmRlZCBieSB0aGUgcGxhdGZvcm0nc1xuICogZnVuY3Rpb24gYG1heER1cmF0aW9uYCBhbmQgdGhlIGBOT19JTkxJTkVfUkVQTEFZX0FGVEVSX01TYCBlYXJseS1yZXR1cm5cbiAqIGd1YXJkLlxuICpcbiAqIFVzYWdlOlxuICpcbiAqIGBgYHRzXG4gKiBjb25zdCBidWRnZXQgPSBuZXcgUmVwbGF5QnVkZ2V0KCk7XG4gKiAvLyDigKZub24tc3RlcCB3b3JrIGhhcHBlbnMgaGVyZSwgYWNjdW11bGF0ZXMgYWdhaW5zdCB0aGUgYnVkZ2V04oCmXG4gKiBidWRnZXQucGF1c2UoKTtcbiAqIHRyeSB7XG4gKiAgIGF3YWl0IGV4ZWN1dGVTdGVwKC4uLik7IC8vIG5vdCBjaGFyZ2VkXG4gKiB9IGZpbmFsbHkge1xuICogICBidWRnZXQucmVzdW1lKCk7XG4gKiB9XG4gKiAvLyBiYWNrIHRvIGNoYXJnaW5nXG4gKiBpZiAoYnVkZ2V0LmlzRXhoYXVzdGVkKCkpIHsgLi4uIH1cbiAqIGBgYFxuICpcbiAqIEltcGxlbWVudGF0aW9uIG5vdGVzOlxuICpcbiAqIC0gYHBhdXNlKClgIGFuZCBgcmVzdW1lKClgIGFyZSBpZGVtcG90ZW50OiBjYWxsaW5nIGBwYXVzZSgpYCB3aGlsZVxuICogICBhbHJlYWR5IHBhdXNlZCAob3IgYHJlc3VtZSgpYCB3aGlsZSBhbHJlYWR5IHJlc3VtZWQpIGlzIGEgbm8tb3AuXG4gKiAgIFRoaXMgcHJvdGVjdHMgYWdhaW5zdCBkb3VibGUtY291bnRpbmcgaW4gZnV0dXJlIHJlZmFjdG9ycyB0aGF0IG5lc3RcbiAqICAgc3RlcCBleGVjdXRpb24gb3IgdGFrZSBhbiBlYXJseS1yZXR1cm4gcGF0aCBiZXR3ZWVuIGEgYHBhdXNlKClgIGFuZFxuICogICB0aGUgbWF0Y2hpbmcgYHJlc3VtZSgpYC5cbiAqIC0gYGlzRXhoYXVzdGVkKClgIGlzIGNoZWNrZWQgYXQgbG9vcCBib3VuZGFyaWVzIGJ5IHRoZSBjYWxsZXI7IHRoZVxuICogICBidWRnZXQgaXRzZWxmIGRvZXMgbm90IGFybSBhbnkgdGltZXJzLiBUaGlzIG1lYW5zIGFuIGluLWZsaWdodFxuICogICBwYXRob2xvZ2ljYWwgYHJ1bldvcmtmbG93YCBjYWxsIChlLmcuIGEgaHVnZSBldmVudC1sb2cgcmVwbGF5KSBjYW5cbiAqICAgb3ZlcnNob290IHRoZSBidWRnZXQgYnkgdXAgdG8gb25lIGl0ZXJhdGlvbidzIHdvcnRoIG9mIHdvcmsgYmVmb3JlXG4gKiAgIHRoZSBuZXh0IGNoZWNrIGZpcmVzLiBJbiBwcmFjdGljZSB0aGUgMjBzIGhlYWRyb29tIGJ1aWx0IGludG9cbiAqICAgYE1BWF9SRVBMQVlfVElNRU9VVF9NU2AgKGFuZCB0aGUgZnVuY3Rpb24gYG1heER1cmF0aW9uYCBjZWlsaW5nKVxuICogICBnaXZlcyB1cyBzbGFjazsgdGhlIG9sZCBgc2V0VGltZW91dGAtYmFzZWQgYXBwcm9hY2ggYWxzbyB1bHRpbWF0ZWx5XG4gKiAgIHJlbGllZCBvbiB0aGUgcGxhdGZvcm0gU0lHVEVSTSBhcyB0aGUgaGFyZCBiYWNrc3RvcC5cbiAqL1xuZXhwb3J0IGNsYXNzIFJlcGxheUJ1ZGdldCB7XG4gIHByaXZhdGUgcmVhZG9ubHkgbGltaXRNczogbnVtYmVyO1xuICBwcml2YXRlIGVsYXBzZWRNcyA9IDA7XG4gIHByaXZhdGUgaW50ZXJ2YWxTdGFydDogbnVtYmVyIHwgbnVsbDtcblxuICBjb25zdHJ1Y3RvcihsaW1pdE1zOiBudW1iZXIgPSBnZXRSZXBsYXlUaW1lb3V0TXMoKSkge1xuICAgIHRoaXMubGltaXRNcyA9IGxpbWl0TXM7XG4gICAgdGhpcy5pbnRlcnZhbFN0YXJ0ID0gRGF0ZS5ub3coKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgY29uZmlndXJlZCByZXBsYXktdGltZW91dCBsaW1pdCwgaW4gbXMuIFVzZWZ1bCBmb3IgbG9nIG1lc3NhZ2VzLlxuICAgKi9cbiAgZ2V0IGNvbmZpZ3VyZWRMaW1pdE1zKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMubGltaXRNcztcbiAgfVxuXG4gIC8qKlxuICAgKiBUb3RhbCBub24tc3RlcCB0aW1lIGFjY3VtdWxhdGVkIHNvIGZhciwgaW4gbXMuIEluY2x1ZGVzIHRoZVxuICAgKiBjdXJyZW50bHktYWN0aXZlIGludGVydmFsIGlmIHRoZSBidWRnZXQgaXMgbm90IHBhdXNlZC5cbiAgICovXG4gIGVsYXBzZWQoKTogbnVtYmVyIHtcbiAgICBjb25zdCBvcGVuID1cbiAgICAgIHRoaXMuaW50ZXJ2YWxTdGFydCA9PT0gbnVsbCA/IDAgOiBEYXRlLm5vdygpIC0gdGhpcy5pbnRlcnZhbFN0YXJ0O1xuICAgIHJldHVybiB0aGlzLmVsYXBzZWRNcyArIG9wZW47XG4gIH1cblxuICAvKipcbiAgICogU3RvcCBjb3VudGluZyBlbGFwc2VkIHRpbWUgdG93YXJkIHRoZSBidWRnZXQuIElkZW1wb3RlbnQ6IHNhZmUgdG9cbiAgICogY2FsbCBtdWx0aXBsZSB0aW1lcyBpbiBhIHJvdzsgc3Vic2VxdWVudCBjYWxscyBhcmUgbm8tb3BzIHVudGlsXG4gICAqIGByZXN1bWUoKWAgcmVvcGVucyBhbiBpbnRlcnZhbC5cbiAgICovXG4gIHBhdXNlKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmludGVydmFsU3RhcnQgPT09IG51bGwpIHJldHVybjtcbiAgICB0aGlzLmVsYXBzZWRNcyArPSBEYXRlLm5vdygpIC0gdGhpcy5pbnRlcnZhbFN0YXJ0O1xuICAgIHRoaXMuaW50ZXJ2YWxTdGFydCA9IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogUmVzdW1lIGNvdW50aW5nIGVsYXBzZWQgdGltZSB0b3dhcmQgdGhlIGJ1ZGdldC4gSWRlbXBvdGVudDogc2FmZSB0b1xuICAgKiBjYWxsIG11bHRpcGxlIHRpbWVzIGluIGEgcm93OyBzdWJzZXF1ZW50IGNhbGxzIHJlLWFuY2hvciB0aGVcbiAgICogaW50ZXJ2YWwgc3RhcnQgdG8gYG5vdygpYCwgd2hpY2ggaXMgZmluZSBiZWNhdXNlIG5vIHRpbWUgYWNjcnVlc1xuICAgKiBiZXR3ZWVuIGJhY2stdG8tYmFjayBgcmVzdW1lKClgIGNhbGxzLlxuICAgKi9cbiAgcmVzdW1lKCk6IHZvaWQge1xuICAgIHRoaXMuaW50ZXJ2YWxTdGFydCA9IERhdGUubm93KCk7XG4gIH1cblxuICAvKipcbiAgICogVHJ1ZSBpZiB0aGUgYnVkZ2V0IGhhcyBiZWVuIGV4aGF1c3RlZCAoYGVsYXBzZWQoKSA+PSBsaW1pdE1zYCkuXG4gICAqIENhbGxlcnMgc2hvdWxkIGludm9rZSBgaGFuZGxlRXhoYXVzdGVkKC4uLilgIGFmdGVyd2FyZCBhbmQgcmV0dXJuXG4gICAqIGZyb20gdGhlIGhhbmRsZXIuXG4gICAqL1xuICBpc0V4aGF1c3RlZCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5lbGFwc2VkKCkgPj0gdGhpcy5saW1pdE1zO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBSZXBsYXlUaW1lb3V0UmV0cnlFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgb3ZlcnJpZGUgcmVhZG9ubHkgbmFtZSA9ICdSZXBsYXlUaW1lb3V0UmV0cnlFcnJvcic7XG59XG5cbi8qKlxuICogUmVqZWN0IHRoZSBjdXJyZW50IGRlbGl2ZXJ5IHdoaWxlIHJlcGxheS10aW1lb3V0IHJldHJpZXMgcmVtYWluLiBPbmNlXG4gKiBleGhhdXN0ZWQsIGZhaWwgdGhlIHJ1biBhbmQgbGV0IHRoZSBxdWV1ZSBhY2tub3dsZWRnZSB0aGUgZGVsaXZlcnkuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVSZXBsYXlCdWRnZXRFeGhhdXN0ZWQoYXJnczoge1xuICBydW5JZDogc3RyaW5nO1xuICB3b3JrZmxvd05hbWU6IHN0cmluZztcbiAgcmVxdWVzdElkOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIGF0dGVtcHQ6IG51bWJlcjtcbiAgbGltaXRNczogbnVtYmVyO1xuICAvKipcbiAgICogSG93IG11Y2ggb2YgdGhlIGxvZyB0aGUgcmVwbGF5IHRoYXQgcmFuIG91dCBvZiBidWRnZXQgaGFkIGxvYWRlZC4gQ2FycmllZFxuICAgKiBvbnRvIHRoZSB0ZXJtaW5hbCB3cml0ZSBmb3IgdGhlIHNhbWUgcmVhc29uIGV2ZXJ5IG90aGVyIHdyaXRlIGZyb20gdGhhdFxuICAgKiByZXBsYXkgY2FycmllcyBpdDogdGhlIHJ1biBpcyBiZWluZyBmYWlsZWQgb24gYSB2aWV3IG9mIHRoZSBsb2csIGFuZCB0aGVcbiAgICogV29ybGQgc2hvdWxkIGJlIHRvbGQgd2hpY2ggdmlldy5cbiAgICovXG4gIHNsb3RTbmFwc2hvdD86IFNsb3RTbmFwc2hvdFBhcmFtcztcbn0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgeyBydW5JZCwgd29ya2Zsb3dOYW1lLCByZXF1ZXN0SWQsIGF0dGVtcHQsIGxpbWl0TXMsIHNsb3RTbmFwc2hvdCB9ID1cbiAgICBhcmdzO1xuICBjb25zdCBydW5Mb2dnZXIgPSBydW50aW1lTG9nZ2VyLmZvclJ1bihydW5JZCwgd29ya2Zsb3dOYW1lKTtcblxuICBjb25zdCBtYXhSZXRyaWVzID0gZ2V0UmVwbGF5VGltZW91dE1heFJldHJpZXMoKTtcbiAgaWYgKGF0dGVtcHQgPD0gbWF4UmV0cmllcykge1xuICAgIHJ1bkxvZ2dlci53YXJuKCdXb3JrZmxvdyByZXBsYXkgZXhjZWVkZWQgdGltZW91dDsgcmV0cnlpbmcgZGVsaXZlcnknLCB7XG4gICAgICB0aW1lb3V0TXM6IGxpbWl0TXMsXG4gICAgICBhdHRlbXB0LFxuICAgICAgbWF4UmV0cmllcyxcbiAgICB9KTtcbiAgICB0aHJvdyBuZXcgUmVwbGF5VGltZW91dFJldHJ5RXJyb3IoXG4gICAgICBgV29ya2Zsb3cgcmVwbGF5IGV4Y2VlZGVkIG1heGltdW0gZHVyYXRpb24gKCR7bGltaXRNcyAvIDEwMDB9cylgXG4gICAgKTtcbiAgfVxuXG4gIGNvbnN0IHdvcmxkID0gYXdhaXQgZ2V0V29ybGQoKTtcblxuICBjb25zdCByZXBsYXlUaW1lb3V0RGVzY3JpcHRpb24gPSBkZXNjcmliZUVycm9yKFxuICAgIHVuZGVmaW5lZCxcbiAgICBSVU5fRVJST1JfQ09ERVMuUkVQTEFZX1RJTUVPVVRcbiAgKTtcbiAgcnVuTG9nZ2VyLmVycm9yKFxuICAgICdXb3JrZmxvdyByZXBsYXkgZXhjZWVkZWQgdGltZW91dCBhbmQgbWF4IHJldHJpZXMgZXhjZWVkZWQuIEZhaWxpbmcgdGhlIHJ1bicsXG4gICAge1xuICAgICAgdGltZW91dE1zOiBsaW1pdE1zLFxuICAgICAgYXR0ZW1wdCxcbiAgICAgIG1heFJldHJpZXMsXG4gICAgICBlcnJvckNvZGU6IHJlcGxheVRpbWVvdXREZXNjcmlwdGlvbi5lcnJvckNvZGUsXG4gICAgICBlcnJvckF0dHJpYnV0aW9uOiByZXBsYXlUaW1lb3V0RGVzY3JpcHRpb24uYXR0cmlidXRpb24sXG4gICAgfVxuICApO1xuXG4gIGNvbnN0IGdldEVuY3J5cHRpb25LZXkgPSBtZW1vaXplRW5jcnlwdGlvbktleSh3b3JsZCwgcnVuSWQpO1xuICBjb25zdCB0aW1lb3V0RXJyID0gbmV3IEZhdGFsRXJyb3IoXG4gICAgYFdvcmtmbG93IHJlcGxheSBleGNlZWRlZCBtYXhpbXVtIGR1cmF0aW9uICgke2xpbWl0TXMgLyAxMDAwfXMpIGFmdGVyICR7YXR0ZW1wdH0gYXR0ZW1wdHNgXG4gICk7XG4gIGF3YWl0IHdvcmxkLmV2ZW50cy5jcmVhdGUoXG4gICAgcnVuSWQsXG4gICAge1xuICAgICAgZXZlbnRUeXBlOiAncnVuX2ZhaWxlZCcsXG4gICAgICBzcGVjVmVyc2lvbjogU1BFQ19WRVJTSU9OX0NVUlJFTlQsXG4gICAgICBldmVudERhdGE6IHtcbiAgICAgICAgZXJyb3I6IGF3YWl0IGRlaHlkcmF0ZVJ1bkVycm9yKFxuICAgICAgICAgIHRpbWVvdXRFcnIsXG4gICAgICAgICAgcnVuSWQsXG4gICAgICAgICAgYXdhaXQgZ2V0RW5jcnlwdGlvbktleSgpXG4gICAgICAgICksXG4gICAgICAgIGVycm9yQ29kZTogUlVOX0VSUk9SX0NPREVTLlJFUExBWV9USU1FT1VULFxuICAgICAgfSxcbiAgICB9LFxuICAgIHsgcmVxdWVzdElkLCAuLi5zbG90U25hcHNob3QgfVxuICApO1xufVxuIl19
@@ -0,0 +1,36 @@
1
+ import type { CreateEventParams } from '@workflow/world';
2
+ /**
3
+ * Invocation-scoped, one-shot reporter for a replay-divergence recovery.
4
+ *
5
+ * The reporter stays dormant until replay reaches a valid deterministic
6
+ * boundary. The first subsequent event write carries the telemetry for free.
7
+ * A recovered invocation with no natural event write intentionally reports
8
+ * nothing.
9
+ */
10
+ export declare class ReplayRecoveryReporter {
11
+ private readonly divergenceCount;
12
+ private active;
13
+ private reported;
14
+ constructor(divergenceCount: number);
15
+ /**
16
+ * A reporter for an invocation that never diverged: `activate()` cannot arm
17
+ * it, so `withEventCreate` always passes writes through untouched. Lets every
18
+ * call site hold a non-optional reporter instead of branching on one.
19
+ *
20
+ * Structurally inert rather than a zero-count reporter on purpose: the
21
+ * server rejects a count below 1 as out-of-range and emits a
22
+ * `telemetry_rejected` metric, so a reporter that *can* report zero is a
23
+ * live footgun.
24
+ */
25
+ static inert(): ReplayRecoveryReporter;
26
+ activate(): void;
27
+ /**
28
+ * Decorate one natural event write with recovery telemetry.
29
+ *
30
+ * The claim is taken synchronously, so concurrent suspension writes cannot
31
+ * all carry the same count. A failed request releases it for a later natural
32
+ * write in the same invocation.
33
+ */
34
+ withEventCreate<T>(params: CreateEventParams | undefined, create: (params: CreateEventParams | undefined) => Promise<T>): Promise<T>;
35
+ }
36
+ //# sourceMappingURL=replay-recovery-reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay-recovery-reporter.d.ts","sourceRoot":"","sources":["../../src/runtime/replay-recovery-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD;;;;;;;GAOG;AACH,qBAAa,sBAAsB;IAIrB,OAAO,CAAC,QAAQ,CAAC,eAAe;IAH5C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAS;gBAEI,eAAe,EAAE,MAAM;IAEpD;;;;;;;;;OASG;IACH,MAAM,CAAC,KAAK,IAAI,sBAAsB;IAItC,QAAQ,IAAI,IAAI;IAIhB;;;;;;OAMG;IACG,eAAe,CAAC,CAAC,EACrB,MAAM,EAAE,iBAAiB,GAAG,SAAS,EACrC,MAAM,EAAE,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,GAC5D,OAAO,CAAC,CAAC,CAAC;CAcd"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Invocation-scoped, one-shot reporter for a replay-divergence recovery.
3
+ *
4
+ * The reporter stays dormant until replay reaches a valid deterministic
5
+ * boundary. The first subsequent event write carries the telemetry for free.
6
+ * A recovered invocation with no natural event write intentionally reports
7
+ * nothing.
8
+ */
9
+ export class ReplayRecoveryReporter {
10
+ divergenceCount;
11
+ active = false;
12
+ reported = false;
13
+ constructor(divergenceCount) {
14
+ this.divergenceCount = divergenceCount;
15
+ }
16
+ /**
17
+ * A reporter for an invocation that never diverged: `activate()` cannot arm
18
+ * it, so `withEventCreate` always passes writes through untouched. Lets every
19
+ * call site hold a non-optional reporter instead of branching on one.
20
+ *
21
+ * Structurally inert rather than a zero-count reporter on purpose: the
22
+ * server rejects a count below 1 as out-of-range and emits a
23
+ * `telemetry_rejected` metric, so a reporter that *can* report zero is a
24
+ * live footgun.
25
+ */
26
+ static inert() {
27
+ return new InertReplayRecoveryReporter();
28
+ }
29
+ activate() {
30
+ this.active = true;
31
+ }
32
+ /**
33
+ * Decorate one natural event write with recovery telemetry.
34
+ *
35
+ * The claim is taken synchronously, so concurrent suspension writes cannot
36
+ * all carry the same count. A failed request releases it for a later natural
37
+ * write in the same invocation.
38
+ */
39
+ async withEventCreate(params, create) {
40
+ if (!this.active || this.reported)
41
+ return create(params);
42
+ this.reported = true;
43
+ try {
44
+ return await create({
45
+ ...params,
46
+ replayDivergenceCount: this.divergenceCount,
47
+ });
48
+ }
49
+ catch (error) {
50
+ this.reported = false;
51
+ throw error;
52
+ }
53
+ }
54
+ }
55
+ /** See {@link ReplayRecoveryReporter.inert}. */
56
+ class InertReplayRecoveryReporter extends ReplayRecoveryReporter {
57
+ constructor() {
58
+ super(0);
59
+ }
60
+ activate() {
61
+ // Deliberately empty: nothing to report, so nothing can arm.
62
+ }
63
+ }
64
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVwbGF5LXJlY292ZXJ5LXJlcG9ydGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3J1bnRpbWUvcmVwbGF5LXJlY292ZXJ5LXJlcG9ydGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLE9BQU8sc0JBQXNCO0lBSUo7SUFIckIsTUFBTSxHQUFHLEtBQUssQ0FBQztJQUNmLFFBQVEsR0FBRyxLQUFLLENBQUM7SUFFekIsWUFBNkIsZUFBdUI7UUFBdkIsb0JBQWUsR0FBZixlQUFlLENBQVE7SUFBRyxDQUFDO0lBRXhEOzs7Ozs7Ozs7T0FTRztJQUNILE1BQU0sQ0FBQyxLQUFLO1FBQ1YsT0FBTyxJQUFJLDJCQUEyQixFQUFFLENBQUM7SUFDM0MsQ0FBQztJQUVELFFBQVE7UUFDTixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztJQUNyQixDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0gsS0FBSyxDQUFDLGVBQWUsQ0FDbkIsTUFBcUMsRUFDckMsTUFBNkQ7UUFFN0QsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLFFBQVE7WUFBRSxPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUV6RCxJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQztRQUNyQixJQUFJLENBQUM7WUFDSCxPQUFPLE1BQU0sTUFBTSxDQUFDO2dCQUNsQixHQUFHLE1BQU07Z0JBQ1QscUJBQXFCLEVBQUUsSUFBSSxDQUFDLGVBQWU7YUFDNUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLENBQUMsUUFBUSxHQUFHLEtBQUssQ0FBQztZQUN0QixNQUFNLEtBQUssQ0FBQztRQUNkLENBQUM7SUFDSCxDQUFDO0NBQ0Y7QUFFRCxnREFBZ0Q7QUFDaEQsTUFBTSwyQkFBNEIsU0FBUSxzQkFBc0I7SUFDOUQ7UUFDRSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDWCxDQUFDO0lBRVEsUUFBUTtRQUNmLDZEQUE2RDtJQUMvRCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IENyZWF0ZUV2ZW50UGFyYW1zIH0gZnJvbSAnQHdvcmtmbG93L3dvcmxkJztcblxuLyoqXG4gKiBJbnZvY2F0aW9uLXNjb3BlZCwgb25lLXNob3QgcmVwb3J0ZXIgZm9yIGEgcmVwbGF5LWRpdmVyZ2VuY2UgcmVjb3ZlcnkuXG4gKlxuICogVGhlIHJlcG9ydGVyIHN0YXlzIGRvcm1hbnQgdW50aWwgcmVwbGF5IHJlYWNoZXMgYSB2YWxpZCBkZXRlcm1pbmlzdGljXG4gKiBib3VuZGFyeS4gVGhlIGZpcnN0IHN1YnNlcXVlbnQgZXZlbnQgd3JpdGUgY2FycmllcyB0aGUgdGVsZW1ldHJ5IGZvciBmcmVlLlxuICogQSByZWNvdmVyZWQgaW52b2NhdGlvbiB3aXRoIG5vIG5hdHVyYWwgZXZlbnQgd3JpdGUgaW50ZW50aW9uYWxseSByZXBvcnRzXG4gKiBub3RoaW5nLlxuICovXG5leHBvcnQgY2xhc3MgUmVwbGF5UmVjb3ZlcnlSZXBvcnRlciB7XG4gIHByaXZhdGUgYWN0aXZlID0gZmFsc2U7XG4gIHByaXZhdGUgcmVwb3J0ZWQgPSBmYWxzZTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IGRpdmVyZ2VuY2VDb3VudDogbnVtYmVyKSB7fVxuXG4gIC8qKlxuICAgKiBBIHJlcG9ydGVyIGZvciBhbiBpbnZvY2F0aW9uIHRoYXQgbmV2ZXIgZGl2ZXJnZWQ6IGBhY3RpdmF0ZSgpYCBjYW5ub3QgYXJtXG4gICAqIGl0LCBzbyBgd2l0aEV2ZW50Q3JlYXRlYCBhbHdheXMgcGFzc2VzIHdyaXRlcyB0aHJvdWdoIHVudG91Y2hlZC4gTGV0cyBldmVyeVxuICAgKiBjYWxsIHNpdGUgaG9sZCBhIG5vbi1vcHRpb25hbCByZXBvcnRlciBpbnN0ZWFkIG9mIGJyYW5jaGluZyBvbiBvbmUuXG4gICAqXG4gICAqIFN0cnVjdHVyYWxseSBpbmVydCByYXRoZXIgdGhhbiBhIHplcm8tY291bnQgcmVwb3J0ZXIgb24gcHVycG9zZTogdGhlXG4gICAqIHNlcnZlciByZWplY3RzIGEgY291bnQgYmVsb3cgMSBhcyBvdXQtb2YtcmFuZ2UgYW5kIGVtaXRzIGFcbiAgICogYHRlbGVtZXRyeV9yZWplY3RlZGAgbWV0cmljLCBzbyBhIHJlcG9ydGVyIHRoYXQgKmNhbiogcmVwb3J0IHplcm8gaXMgYVxuICAgKiBsaXZlIGZvb3RndW4uXG4gICAqL1xuICBzdGF0aWMgaW5lcnQoKTogUmVwbGF5UmVjb3ZlcnlSZXBvcnRlciB7XG4gICAgcmV0dXJuIG5ldyBJbmVydFJlcGxheVJlY292ZXJ5UmVwb3J0ZXIoKTtcbiAgfVxuXG4gIGFjdGl2YXRlKCk6IHZvaWQge1xuICAgIHRoaXMuYWN0aXZlID0gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEZWNvcmF0ZSBvbmUgbmF0dXJhbCBldmVudCB3cml0ZSB3aXRoIHJlY292ZXJ5IHRlbGVtZXRyeS5cbiAgICpcbiAgICogVGhlIGNsYWltIGlzIHRha2VuIHN5bmNocm9ub3VzbHksIHNvIGNvbmN1cnJlbnQgc3VzcGVuc2lvbiB3cml0ZXMgY2Fubm90XG4gICAqIGFsbCBjYXJyeSB0aGUgc2FtZSBjb3VudC4gQSBmYWlsZWQgcmVxdWVzdCByZWxlYXNlcyBpdCBmb3IgYSBsYXRlciBuYXR1cmFsXG4gICAqIHdyaXRlIGluIHRoZSBzYW1lIGludm9jYXRpb24uXG4gICAqL1xuICBhc3luYyB3aXRoRXZlbnRDcmVhdGU8VD4oXG4gICAgcGFyYW1zOiBDcmVhdGVFdmVudFBhcmFtcyB8IHVuZGVmaW5lZCxcbiAgICBjcmVhdGU6IChwYXJhbXM6IENyZWF0ZUV2ZW50UGFyYW1zIHwgdW5kZWZpbmVkKSA9PiBQcm9taXNlPFQ+XG4gICk6IFByb21pc2U8VD4ge1xuICAgIGlmICghdGhpcy5hY3RpdmUgfHwgdGhpcy5yZXBvcnRlZCkgcmV0dXJuIGNyZWF0ZShwYXJhbXMpO1xuXG4gICAgdGhpcy5yZXBvcnRlZCA9IHRydWU7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBhd2FpdCBjcmVhdGUoe1xuICAgICAgICAuLi5wYXJhbXMsXG4gICAgICAgIHJlcGxheURpdmVyZ2VuY2VDb3VudDogdGhpcy5kaXZlcmdlbmNlQ291bnQsXG4gICAgICB9KTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgdGhpcy5yZXBvcnRlZCA9IGZhbHNlO1xuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9XG59XG5cbi8qKiBTZWUge0BsaW5rIFJlcGxheVJlY292ZXJ5UmVwb3J0ZXIuaW5lcnR9LiAqL1xuY2xhc3MgSW5lcnRSZXBsYXlSZWNvdmVyeVJlcG9ydGVyIGV4dGVuZHMgUmVwbGF5UmVjb3ZlcnlSZXBvcnRlciB7XG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHN1cGVyKDApO1xuICB9XG5cbiAgb3ZlcnJpZGUgYWN0aXZhdGUoKTogdm9pZCB7XG4gICAgLy8gRGVsaWJlcmF0ZWx5IGVtcHR5OiBub3RoaW5nIHRvIHJlcG9ydCwgc28gbm90aGluZyBjYW4gYXJtLlxuICB9XG59XG4iXX0=
@@ -1,23 +1,105 @@
1
- import { type Hook } from '@workflow/world';
2
- import { type CryptoKey } from '../encryption.js';
1
+ import { type Hook as WorldHook } from '@workflow/world';
2
+ import { type PayloadKey } from '../serialization.js';
3
3
  /**
4
- * Get the hook by token to find the associated workflow run,
5
- * and hydrate the `metadata` property if it was set from within
6
- * the workflow run.
4
+ * A hook record with its serialized `metadata` omitted: everything a resume
5
+ * actually reads. Resuming never looks at metadata, so the resume path accepts
6
+ * both a raw {@link WorldHook} straight out of a World and a {@link Hook}
7
+ * whose `metadata` is a Promise.
8
+ */
9
+ type ResumableHook = Omit<WorldHook, 'metadata'>;
10
+ /**
11
+ * A hook as returned by {@link getHookByToken} and {@link resumeHook}: the
12
+ * World's {@link WorldHook} record with its user-defined `metadata` hydrated
13
+ * lazily.
14
+ *
15
+ * `metadata` is a getter that returns a Promise — the same shape as
16
+ * `Run.returnValue` — so looking a hook up by token costs exactly one read.
17
+ * Hydrating metadata is a decrypting READ that needs the owning run's payload
18
+ * keys, and resolving those can cost a run fetch plus a `run-key` API round
19
+ * trip (~350ms). Deferring that to first access keeps the lookup fast for the
20
+ * many callers that only need `runId`/`token` — most importantly hook
21
+ * resumption, which never reads metadata at all.
22
+ *
23
+ * The promise is memoized: hydration (and the key resolution behind it) runs at
24
+ * most once per hook object. Awaiting it on a hook that stored no metadata
25
+ * resolves `undefined` and performs no I/O.
26
+ *
27
+ * Like `Run.returnValue`, the accessor is non-enumerable, so it is absent from
28
+ * `{ ...hook }` and `JSON.stringify(hook)` — read it explicitly and include the
29
+ * awaited value if you need to forward it.
30
+ *
31
+ * @example
32
+ *
33
+ * ```ts
34
+ * const hook = await getHookByToken(token);
35
+ * console.log(hook.runId); // no metadata work
36
+ * const metadata = (await hook.metadata) as { allowedUserId?: string } | undefined;
37
+ * ```
38
+ */
39
+ export interface Hook extends ResumableHook {
40
+ /**
41
+ * The hook's user-defined metadata, hydrated on first access and memoized.
42
+ * Resolves `undefined` when the hook carries no metadata.
43
+ */
44
+ readonly metadata: Promise<unknown>;
45
+ }
46
+ /**
47
+ * Get the hook by token to find the associated workflow run.
48
+ *
49
+ * This is a single read. The returned hook's `metadata` is a getter that
50
+ * resolves a Promise (see {@link Hook}), so the run fetch and
51
+ * `run-key` round trip that hydrating it can require are only paid by callers
52
+ * that actually await it:
53
+ *
54
+ * ```ts
55
+ * const hook = await getHookByToken(token);
56
+ * const metadata = await hook.metadata;
57
+ * ```
58
+ *
59
+ * A Hook kept by minimum retention remains available here after its run ends,
60
+ * but cannot be resumed.
7
61
  *
8
62
  * @param token - The unique token identifying the hook
9
63
  */
10
64
  export declare function getHookByToken(token: string): Promise<Hook>;
65
+ /**
66
+ * The result of {@link resumeHook}: a {@link Hook} augmented
67
+ * with an optional resilience signal.
68
+ *
69
+ * `resilientResume` is retained for source compatibility and is never set.
70
+ * `resumeHook()` now requires the durable `hook_received` write and workflow
71
+ * wake to both succeed before it resolves. Treat the result as a plain
72
+ * {@link Hook}.
73
+ */
74
+ export type ResumedHook = Hook & {
75
+ resilientResume?: boolean;
76
+ };
11
77
  /**
12
78
  * Resumes a workflow run by sending a payload to a hook identified by its token.
13
79
  *
14
80
  * This function is called externally (e.g., from an API route or server action)
15
81
  * to send data to a hook and resume the associated workflow run.
16
82
  *
83
+ * Resolving means BOTH that the `hook_received` event is durably recorded in
84
+ * the run's event log and that the workflow wake was accepted by the queue, in
85
+ * that order. A {@link HookNotFoundError} means this invocation committed no
86
+ * event. Any other error after the write is ambiguous only in dispatch, never
87
+ * in durability: the event may already be committed, and a later wake of the
88
+ * run (from any source) will deliver it.
89
+ *
90
+ * Prefer passing the token string over a cached {@link Hook} object. A token
91
+ * is looked up fresh, so the live backend can attest its atomic resume claim
92
+ * and the durable write becomes idempotent-on-retry (transport retries of the
93
+ * same write converge on one event). A supplied Hook object may carry a stale
94
+ * attestation, so it is deliberately ignored and the write is claim-less —
95
+ * meaning a lost response cannot be retried safely: retrying at the
96
+ * application level mints a fresh claim and can commit a second
97
+ * `hook_received`.
98
+ *
17
99
  * @param tokenOrHook - The unique token identifying the hook, or the hook object itself
18
100
  * @param payload - The data payload to send to the hook
19
- * @returns Promise resolving to the hook
20
- * @throws Error if the hook is not found or if there's an error during the process
101
+ * @returns Promise resolving to the {@link ResumedHook}
102
+ * @throws {HookNotFoundError} If the Hook does not exist or its run has ended
21
103
  *
22
104
  * @example
23
105
  *
@@ -37,7 +119,7 @@ export declare function getHookByToken(token: string): Promise<Hook>;
37
119
  * }
38
120
  * ```
39
121
  */
40
- export declare function resumeHook<T = any>(tokenOrHook: string | Hook, payload: T, encryptionKeyOverride?: CryptoKey): Promise<Hook>;
122
+ export declare function resumeHook<T = any>(tokenOrHook: string | ResumableHook, payload: T, encryptionKeyOverride?: PayloadKey): Promise<ResumedHook>;
41
123
  /**
42
124
  * Resumes a webhook by sending a {@link https://developer.mozilla.org/en-US/docs/Web/API/Request | Request}
43
125
  * object to a hook identified by its token.
@@ -74,4 +156,5 @@ export declare function resumeHook<T = any>(tokenOrHook: string | Hook, payload:
74
156
  * ```
75
157
  */
76
158
  export declare function resumeWebhook(token: string, request: Request): Promise<Response>;
159
+ export {};
77
160
  //# sourceMappingURL=resume-hook.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resume-hook.d.ts","sourceRoot":"","sources":["../../src/runtime/resume-hook.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,KAAK,IAAI,EAMV,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,KAAK,SAAS,EAAa,MAAM,kBAAkB,CAAC;AAqC7D;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGjE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,UAAU,CAAC,CAAC,GAAG,GAAG,EACtC,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,OAAO,EAAE,CAAC,EACV,qBAAqB,CAAC,EAAE,SAAS,GAChC,OAAO,CAAC,IAAI,CAAC,CAoHf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,QAAQ,CAAC,CA0DnB"}
1
+ {"version":3,"file":"resume-hook.d.ts","sourceRoot":"","sources":["../../src/runtime/resume-hook.ts"],"names":[],"mappings":"AAMA,OAAO,EAUL,KAAK,IAAI,IAAI,SAAS,EACvB,MAAM,iBAAiB,CAAC;AAOzB,OAAO,EAGL,KAAK,UAAU,EAGhB,MAAM,qBAAqB,CAAC;AAkG7B;;;;;GAKG;AACH,KAAK,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,WAAW,IAAK,SAAQ,aAAa;IACzC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CACrC;AAiLD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGjE;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAsB,UAAU,CAAC,CAAC,GAAG,GAAG,EACtC,WAAW,EAAE,MAAM,GAAG,aAAa,EACnC,OAAO,EAAE,CAAC,EACV,qBAAqB,CAAC,EAAE,UAAU,GACjC,OAAO,CAAC,WAAW,CAAC,CAoBtB;AAsUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,QAAQ,CAAC,CAsFnB"}