@workflow/core 5.0.0-beta.4 → 5.0.0-beta.40

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 (325) hide show
  1. package/dist/attribute-changes.d.ts +7 -0
  2. package/dist/attribute-changes.d.ts.map +1 -0
  3. package/dist/attribute-changes.js +25 -0
  4. package/dist/capabilities.d.ts +33 -1
  5. package/dist/capabilities.d.ts.map +1 -1
  6. package/dist/capabilities.js +71 -4
  7. package/dist/capture-stack.d.ts +16 -0
  8. package/dist/capture-stack.d.ts.map +1 -0
  9. package/dist/capture-stack.js +21 -0
  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 +26 -3
  14. package/dist/classify-error.d.ts.map +1 -1
  15. package/dist/classify-error.js +109 -6
  16. package/dist/context-errors.d.ts +27 -0
  17. package/dist/context-errors.d.ts.map +1 -0
  18. package/dist/context-errors.js +101 -0
  19. package/dist/context-violation-error.d.ts +97 -0
  20. package/dist/context-violation-error.d.ts.map +1 -0
  21. package/dist/context-violation-error.js +149 -0
  22. package/dist/correlation-id.d.ts +109 -0
  23. package/dist/correlation-id.d.ts.map +1 -0
  24. package/dist/correlation-id.js +139 -0
  25. package/dist/create-hook.d.ts +62 -4
  26. package/dist/create-hook.d.ts.map +1 -1
  27. package/dist/create-hook.js +4 -3
  28. package/dist/define-hook.d.ts.map +1 -1
  29. package/dist/define-hook.js +20 -5
  30. package/dist/describe-error.d.ts +70 -0
  31. package/dist/describe-error.d.ts.map +1 -0
  32. package/dist/describe-error.js +189 -0
  33. package/dist/encryption.d.ts +37 -3
  34. package/dist/encryption.d.ts.map +1 -1
  35. package/dist/encryption.js +97 -31
  36. package/dist/events-consumer.d.ts +26 -1
  37. package/dist/events-consumer.d.ts.map +1 -1
  38. package/dist/events-consumer.js +105 -18
  39. package/dist/flushable-stream.d.ts +65 -10
  40. package/dist/flushable-stream.d.ts.map +1 -1
  41. package/dist/flushable-stream.js +131 -18
  42. package/dist/global.d.ts +37 -1
  43. package/dist/global.d.ts.map +1 -1
  44. package/dist/global.js +21 -3
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +2 -1
  48. package/dist/log-format.d.ts +25 -0
  49. package/dist/log-format.d.ts.map +1 -0
  50. package/dist/log-format.js +250 -0
  51. package/dist/logger.d.ts +29 -30
  52. package/dist/logger.d.ts.map +1 -1
  53. package/dist/logger.js +82 -32
  54. package/dist/private.d.ts +178 -9
  55. package/dist/private.d.ts.map +1 -1
  56. package/dist/private.js +273 -9
  57. package/dist/replay-payload-cache.d.ts +68 -0
  58. package/dist/replay-payload-cache.d.ts.map +1 -0
  59. package/dist/replay-payload-cache.js +160 -0
  60. package/dist/runtime/compute-instance.d.ts +12 -0
  61. package/dist/runtime/compute-instance.d.ts.map +1 -0
  62. package/dist/runtime/compute-instance.js +13 -0
  63. package/dist/runtime/constants.d.ts +237 -0
  64. package/dist/runtime/constants.d.ts.map +1 -1
  65. package/dist/runtime/constants.js +405 -15
  66. package/dist/runtime/count-step-started-events.d.ts +52 -0
  67. package/dist/runtime/count-step-started-events.d.ts.map +1 -0
  68. package/dist/runtime/count-step-started-events.js +72 -0
  69. package/dist/runtime/deployment-guard.d.ts +99 -0
  70. package/dist/runtime/deployment-guard.d.ts.map +1 -0
  71. package/dist/runtime/deployment-guard.js +154 -0
  72. package/dist/runtime/get-port-lazy.d.ts +25 -0
  73. package/dist/runtime/get-port-lazy.d.ts.map +1 -0
  74. package/dist/runtime/get-port-lazy.js +92 -0
  75. package/dist/runtime/get-world-lazy.d.ts +23 -0
  76. package/dist/runtime/get-world-lazy.d.ts.map +1 -0
  77. package/dist/runtime/get-world-lazy.js +46 -0
  78. package/dist/runtime/helpers.d.ts +219 -10
  79. package/dist/runtime/helpers.d.ts.map +1 -1
  80. package/dist/runtime/helpers.js +414 -40
  81. package/dist/runtime/quickjs-assets.generated.d.ts +14 -0
  82. package/dist/runtime/quickjs-assets.generated.d.ts.map +1 -0
  83. package/dist/runtime/quickjs-assets.generated.js +30 -0
  84. package/dist/runtime/quickjs-entrypoint.d.ts +124 -0
  85. package/dist/runtime/quickjs-entrypoint.d.ts.map +1 -0
  86. package/dist/runtime/quickjs-entrypoint.js +1429 -0
  87. package/dist/runtime/quickjs-runtime.d.ts +193 -0
  88. package/dist/runtime/quickjs-runtime.d.ts.map +1 -0
  89. package/dist/runtime/quickjs-runtime.js +1944 -0
  90. package/dist/runtime/replay-budget.d.ts +98 -0
  91. package/dist/runtime/replay-budget.d.ts.map +1 -0
  92. package/dist/runtime/replay-budget.js +192 -0
  93. package/dist/runtime/replay-recovery-reporter.d.ts +36 -0
  94. package/dist/runtime/replay-recovery-reporter.d.ts.map +1 -0
  95. package/dist/runtime/replay-recovery-reporter.js +64 -0
  96. package/dist/runtime/resume-hook.d.ts +27 -4
  97. package/dist/runtime/resume-hook.d.ts.map +1 -1
  98. package/dist/runtime/resume-hook.js +448 -76
  99. package/dist/runtime/run-id-time.d.ts +19 -0
  100. package/dist/runtime/run-id-time.d.ts.map +1 -0
  101. package/dist/runtime/run-id-time.js +42 -0
  102. package/dist/runtime/run.d.ts +8 -2
  103. package/dist/runtime/run.d.ts.map +1 -1
  104. package/dist/runtime/run.js +62 -15
  105. package/dist/runtime/runs.d.ts +54 -3
  106. package/dist/runtime/runs.d.ts.map +1 -1
  107. package/dist/runtime/runs.js +117 -10
  108. package/dist/runtime/start.d.ts +70 -1
  109. package/dist/runtime/start.d.ts.map +1 -1
  110. package/dist/runtime/start.js +312 -54
  111. package/dist/runtime/step-executor.d.ts +177 -0
  112. package/dist/runtime/step-executor.d.ts.map +1 -0
  113. package/dist/runtime/step-executor.js +949 -0
  114. package/dist/runtime/step-latency.d.ts +197 -0
  115. package/dist/runtime/step-latency.d.ts.map +1 -0
  116. package/dist/runtime/step-latency.js +207 -0
  117. package/dist/runtime/step-ownership.d.ts +72 -0
  118. package/dist/runtime/step-ownership.d.ts.map +1 -0
  119. package/dist/runtime/step-ownership.js +114 -0
  120. package/dist/runtime/step-single-flight.d.ts +12 -0
  121. package/dist/runtime/step-single-flight.d.ts.map +1 -0
  122. package/dist/runtime/step-single-flight.js +69 -0
  123. package/dist/runtime/suspension-handler.d.ts +102 -7
  124. package/dist/runtime/suspension-handler.d.ts.map +1 -1
  125. package/dist/runtime/suspension-handler.js +439 -140
  126. package/dist/runtime/vm-mode.d.ts +44 -0
  127. package/dist/runtime/vm-mode.d.ts.map +1 -0
  128. package/dist/runtime/vm-mode.js +62 -0
  129. package/dist/runtime/vm-serde-bundle.generated.d.ts +14 -0
  130. package/dist/runtime/vm-serde-bundle.generated.d.ts.map +1 -0
  131. package/dist/runtime/vm-serde-bundle.generated.js +16 -0
  132. package/dist/runtime/wait-continuation.d.ts +84 -0
  133. package/dist/runtime/wait-continuation.d.ts.map +1 -0
  134. package/dist/runtime/wait-continuation.js +105 -0
  135. package/dist/runtime/wait-until.d.ts +18 -0
  136. package/dist/runtime/wait-until.d.ts.map +1 -0
  137. package/dist/runtime/wait-until.js +42 -0
  138. package/dist/runtime/world-compatibility.d.ts +5 -0
  139. package/dist/runtime/world-compatibility.d.ts.map +1 -0
  140. package/dist/runtime/world-compatibility.js +12 -0
  141. package/dist/runtime/world-init.d.ts +50 -0
  142. package/dist/runtime/world-init.d.ts.map +1 -0
  143. package/dist/runtime/world-init.js +50 -0
  144. package/dist/runtime/world.d.ts +14 -2
  145. package/dist/runtime/world.d.ts.map +1 -1
  146. package/dist/runtime/world.js +101 -26
  147. package/dist/runtime.d.ts +17 -12
  148. package/dist/runtime.d.ts.map +1 -1
  149. package/dist/runtime.js +3078 -327
  150. package/dist/schemas.d.ts +1 -1
  151. package/dist/schemas.d.ts.map +1 -1
  152. package/dist/schemas.js +1 -1
  153. package/dist/sealed-box.d.ts +167 -0
  154. package/dist/sealed-box.d.ts.map +1 -0
  155. package/dist/sealed-box.js +571 -0
  156. package/dist/serialization/client.d.ts +17 -0
  157. package/dist/serialization/client.d.ts.map +1 -0
  158. package/dist/serialization/client.js +48 -0
  159. package/dist/serialization/codec-devalue-vm.d.ts +9 -0
  160. package/dist/serialization/codec-devalue-vm.d.ts.map +1 -0
  161. package/dist/serialization/codec-devalue-vm.js +137 -0
  162. package/dist/serialization/codec-devalue.d.ts +14 -0
  163. package/dist/serialization/codec-devalue.d.ts.map +1 -0
  164. package/dist/serialization/codec-devalue.js +105 -0
  165. package/dist/serialization/codec.d.ts +125 -0
  166. package/dist/serialization/codec.d.ts.map +1 -0
  167. package/dist/serialization/codec.js +17 -0
  168. package/dist/serialization/compression.d.ts +104 -0
  169. package/dist/serialization/compression.d.ts.map +1 -0
  170. package/dist/serialization/compression.js +260 -0
  171. package/dist/serialization/encryption.d.ts +134 -0
  172. package/dist/serialization/encryption.d.ts.map +1 -0
  173. package/dist/serialization/encryption.js +186 -0
  174. package/dist/serialization/errors.d.ts +34 -0
  175. package/dist/serialization/errors.d.ts.map +1 -0
  176. package/dist/serialization/errors.js +59 -0
  177. package/dist/serialization/format.d.ts +60 -0
  178. package/dist/serialization/format.d.ts.map +1 -0
  179. package/dist/serialization/format.js +97 -0
  180. package/dist/serialization/hardened.d.ts +155 -0
  181. package/dist/serialization/hardened.d.ts.map +1 -0
  182. package/dist/serialization/hardened.js +523 -0
  183. package/dist/serialization/index.d.ts +20 -0
  184. package/dist/serialization/index.d.ts.map +1 -0
  185. package/dist/serialization/index.js +22 -0
  186. package/dist/serialization/reducers/class-vm.d.ts +20 -0
  187. package/dist/serialization/reducers/class-vm.d.ts.map +1 -0
  188. package/dist/serialization/reducers/class-vm.js +77 -0
  189. package/dist/serialization/reducers/class.d.ts +11 -0
  190. package/dist/serialization/reducers/class.d.ts.map +1 -0
  191. package/dist/serialization/reducers/class.js +73 -0
  192. package/dist/serialization/reducers/common-vm.d.ts +15 -0
  193. package/dist/serialization/reducers/common-vm.d.ts.map +1 -0
  194. package/dist/serialization/reducers/common-vm.js +579 -0
  195. package/dist/serialization/reducers/common.d.ts +16 -0
  196. package/dist/serialization/reducers/common.d.ts.map +1 -0
  197. package/dist/serialization/reducers/common.js +478 -0
  198. package/dist/serialization/reducers/step-function-vm.d.ts +44 -0
  199. package/dist/serialization/reducers/step-function-vm.d.ts.map +1 -0
  200. package/dist/serialization/reducers/step-function-vm.js +97 -0
  201. package/dist/serialization/reducers/step-function.d.ts +35 -0
  202. package/dist/serialization/reducers/step-function.d.ts.map +1 -0
  203. package/dist/serialization/reducers/step-function.js +104 -0
  204. package/dist/serialization/step.d.ts +17 -0
  205. package/dist/serialization/step.d.ts.map +1 -0
  206. package/dist/serialization/step.js +48 -0
  207. package/dist/serialization/types.d.ts +273 -0
  208. package/dist/serialization/types.d.ts.map +1 -0
  209. package/dist/serialization/types.js +35 -0
  210. package/dist/serialization/vm-bundle-entry.d.ts +12 -0
  211. package/dist/serialization/vm-bundle-entry.d.ts.map +1 -0
  212. package/dist/serialization/vm-bundle-entry.js +58 -0
  213. package/dist/serialization/workflow-vm.d.ts +24 -0
  214. package/dist/serialization/workflow-vm.d.ts.map +1 -0
  215. package/dist/serialization/workflow-vm.js +66 -0
  216. package/dist/serialization/workflow.d.ts +29 -0
  217. package/dist/serialization/workflow.d.ts.map +1 -0
  218. package/dist/serialization/workflow.js +54 -0
  219. package/dist/serialization-format.d.ts +60 -4
  220. package/dist/serialization-format.d.ts.map +1 -1
  221. package/dist/serialization-format.js +276 -56
  222. package/dist/serialization.d.ts +374 -221
  223. package/dist/serialization.d.ts.map +1 -1
  224. package/dist/serialization.js +2300 -783
  225. package/dist/set-attributes.d.ts +13 -0
  226. package/dist/set-attributes.d.ts.map +1 -0
  227. package/dist/set-attributes.js +60 -0
  228. package/dist/sleep.d.ts.map +1 -1
  229. package/dist/sleep.js +3 -2
  230. package/dist/source-map.d.ts +25 -0
  231. package/dist/source-map.d.ts.map +1 -1
  232. package/dist/source-map.js +148 -10
  233. package/dist/step/context-storage.d.ts +61 -2
  234. package/dist/step/context-storage.d.ts.map +1 -1
  235. package/dist/step/context-storage.js +7 -5
  236. package/dist/step/get-closure-vars.d.ts.map +1 -1
  237. package/dist/step/get-closure-vars.js +3 -2
  238. package/dist/step/get-step-metadata.d.ts.map +1 -1
  239. package/dist/step/get-step-metadata.js +3 -2
  240. package/dist/step/get-workflow-metadata.d.ts.map +1 -1
  241. package/dist/step/get-workflow-metadata.js +3 -2
  242. package/dist/step/writable-stream.d.ts.map +1 -1
  243. package/dist/step/writable-stream.js +71 -7
  244. package/dist/step.d.ts.map +1 -1
  245. package/dist/step.js +226 -30
  246. package/dist/symbols.d.ts +54 -0
  247. package/dist/symbols.d.ts.map +1 -1
  248. package/dist/symbols.js +55 -1
  249. package/dist/telemetry/semantic-conventions.d.ts +242 -2
  250. package/dist/telemetry/semantic-conventions.d.ts.map +1 -1
  251. package/dist/telemetry/semantic-conventions.js +173 -1
  252. package/dist/telemetry.d.ts +73 -0
  253. package/dist/telemetry.d.ts.map +1 -1
  254. package/dist/telemetry.js +153 -16
  255. package/dist/types.d.ts +6 -0
  256. package/dist/types.d.ts.map +1 -1
  257. package/dist/types.js +23 -1
  258. package/dist/util.d.ts +16 -6
  259. package/dist/util.d.ts.map +1 -1
  260. package/dist/util.js +25 -16
  261. package/dist/version.d.ts +1 -1
  262. package/dist/version.d.ts.map +1 -1
  263. package/dist/version.js +2 -2
  264. package/dist/vm/index.d.ts.map +1 -1
  265. package/dist/vm/index.js +77 -9
  266. package/dist/vm/script-cache.d.ts +28 -0
  267. package/dist/vm/script-cache.d.ts.map +1 -0
  268. package/dist/vm/script-cache.js +140 -0
  269. package/dist/workflow/abort-controller.d.ts +65 -0
  270. package/dist/workflow/abort-controller.d.ts.map +1 -0
  271. package/dist/workflow/abort-controller.js +314 -0
  272. package/dist/workflow/attribute-dispatcher.d.ts +6 -0
  273. package/dist/workflow/attribute-dispatcher.d.ts.map +1 -0
  274. package/dist/workflow/attribute-dispatcher.js +48 -0
  275. package/dist/workflow/create-hook.d.ts.map +1 -1
  276. package/dist/workflow/create-hook.js +25 -3
  277. package/dist/workflow/define-hook.d.ts +1 -1
  278. package/dist/workflow/define-hook.d.ts.map +1 -1
  279. package/dist/workflow/define-hook.js +8 -4
  280. package/dist/workflow/get-workflow-metadata.d.ts.map +1 -1
  281. package/dist/workflow/get-workflow-metadata.js +14 -3
  282. package/dist/workflow/hook.d.ts.map +1 -1
  283. package/dist/workflow/hook.js +303 -37
  284. package/dist/workflow/index.d.ts +1 -0
  285. package/dist/workflow/index.d.ts.map +1 -1
  286. package/dist/workflow/index.js +5 -3
  287. package/dist/workflow/set-attributes.d.ts +68 -0
  288. package/dist/workflow/set-attributes.d.ts.map +1 -0
  289. package/dist/workflow/set-attributes.js +60 -0
  290. package/dist/workflow/sleep.d.ts.map +1 -1
  291. package/dist/workflow/sleep.js +58 -9
  292. package/dist/workflow/world-init-stub.d.ts +15 -0
  293. package/dist/workflow/world-init-stub.d.ts.map +1 -0
  294. package/dist/workflow/world-init-stub.js +15 -0
  295. package/dist/workflow.d.ts +69 -3
  296. package/dist/workflow.d.ts.map +1 -1
  297. package/dist/workflow.js +791 -554
  298. package/docs/api-reference/create-hook.mdx +79 -0
  299. package/docs/api-reference/create-webhook.mdx +1 -0
  300. package/docs/api-reference/define-hook.mdx +26 -24
  301. package/docs/api-reference/fatal-error.mdx +29 -7
  302. package/docs/api-reference/fetch.mdx +8 -4
  303. package/docs/api-reference/index.mdx +3 -0
  304. package/docs/api-reference/set-attributes.mdx +61 -0
  305. package/docs/api-reference/sleep.mdx +1 -1
  306. package/docs/foundations/cancellation.mdx +459 -0
  307. package/docs/foundations/errors-and-retries.mdx +7 -3
  308. package/docs/foundations/hooks.mdx +29 -0
  309. package/docs/foundations/idempotency.mdx +243 -11
  310. package/docs/foundations/index.mdx +1 -23
  311. package/docs/foundations/meta.json +3 -1
  312. package/docs/foundations/serialization.mdx +77 -41
  313. package/docs/foundations/starting-workflows.mdx +79 -2
  314. package/docs/foundations/streaming.mdx +14 -23
  315. package/docs/foundations/versioning.mdx +263 -0
  316. package/docs/how-it-works/cancellation.mdx +287 -0
  317. package/docs/how-it-works/code-transform.mdx +21 -17
  318. package/docs/how-it-works/encryption.mdx +5 -5
  319. package/docs/how-it-works/event-sourcing.mdx +6 -6
  320. package/docs/how-it-works/framework-integrations.mdx +96 -337
  321. package/docs/how-it-works/meta.json +2 -1
  322. package/package.json +31 -13
  323. package/dist/runtime/step-handler.d.ts +0 -2
  324. package/dist/runtime/step-handler.d.ts.map +0 -1
  325. package/dist/runtime/step-handler.js +0 -678
@@ -8,6 +8,7 @@ prerequisites:
8
8
  related:
9
9
  - /docs/api-reference/workflow/define-hook
10
10
  - /docs/api-reference/workflow/create-webhook
11
+ - /docs/foundations/idempotency
11
12
  ---
12
13
 
13
14
  Creates a low-level hook primitive that can be used to resume a workflow run with arbitrary payloads.
@@ -65,6 +66,8 @@ export default Hook;`}
65
66
 
66
67
  The returned `Hook` object also implements `AsyncIterable<T>`, which allows you to iterate over incoming payloads using `for await...of` syntax.
67
68
 
69
+ Use `hook.getConflict()` to check whether the hook token is already claimed by another hook, including one kept reserved after its run ends, without waiting for hook payload data. Calling `createHook()` on its own does not register the hook — registration is only committed when the workflow suspends. Awaiting `hook.getConflict()` suspends the workflow to commit the registration, then resolves with `null` once `hook_created` is recorded, or with the conflicting [`Run`](/docs/api-reference/workflow-api/get-run).
70
+
68
71
  ## Examples
69
72
 
70
73
  ### Basic Usage
@@ -112,6 +115,81 @@ export async function slackBotWorkflow(channelId: string) {
112
115
  }
113
116
  ```
114
117
 
118
+ ### Detecting Token Conflicts
119
+
120
+ Use `hook.getConflict()` when the workflow needs to claim a hook token before doing other work, but does not need a payload yet:
121
+
122
+ ```typescript lineNumbers
123
+ import { createHook } from "workflow";
124
+
125
+ declare function chargeOrder(orderId: string): Promise<void>; // @setup
126
+
127
+ async function processOrder(orderId: string) {
128
+ "use workflow";
129
+
130
+ using hook = createHook({ // [!code highlight]
131
+ token: `order:${orderId}` // [!code highlight]
132
+ }); // [!code highlight]
133
+
134
+ const conflict = await hook.getConflict(); // [!code highlight]
135
+ if (conflict) { // [!code highlight]
136
+ // Another active workflow run already owns this token.
137
+ return { dedupedTo: conflict.runId };
138
+ }
139
+
140
+ await chargeOrder(orderId);
141
+ }
142
+ ```
143
+
144
+ Because `createHook()` alone does not suspend the workflow, awaiting `hook.getConflict()` is what actually suspends the run and commits the hook registration. It only waits for registration — to receive payload data from a future `resumeHook()` call, await the hook itself or iterate it with `for await...of`.
145
+
146
+ On a conflict, the resolved value is a `Run` handle for the run that owns the token, with durable step-backed accessors. The duplicate run can decide in code how to handle it: return or log `conflict.runId`, inspect `await conflict.status`, wait on `await conflict.returnValue`, or cancel the owner with `await conflict.cancel()` and continue in the current run. See [Run idempotency](/docs/foundations/idempotency#run-idempotency) for these strategies in context.
147
+
148
+ <Callout type="info">
149
+ Custom hook tokens are the recommended way to coordinate active workflow runs. Use a deterministic token from your domain, such as an order ID or conversation ID, create the hook near the beginning of the workflow, and check `await hook.getConflict()` before work that depends on owning the token. See [Run idempotency](/docs/foundations/idempotency#run-idempotency).
150
+ </Callout>
151
+
152
+ ### Keep a Token Unavailable After the Run Ends
153
+
154
+ By default, another Hook can use the token after its workflow ends. Set `experimental_minRetention` to keep the token unavailable for at least a specific time after `createHook()` runs:
155
+
156
+ ```typescript lineNumbers
157
+ import { createHook } from "workflow";
158
+
159
+ declare function processOwnedOrder(orderId: string): Promise<void>; // @setup
160
+
161
+ export async function processOrder(orderId: string) {
162
+ "use workflow";
163
+
164
+ const hook = createHook({ // [!code highlight]
165
+ token: `order:${orderId}`, // [!code highlight]
166
+ experimental_minRetention: "30d", // [!code highlight]
167
+ }); // [!code highlight]
168
+
169
+ const conflict = await hook.getConflict();
170
+ if (conflict) {
171
+ return { status: "duplicate" as const, runId: conflict.runId };
172
+ }
173
+
174
+ await processOwnedOrder(orderId);
175
+ return { status: "processed" as const };
176
+ }
177
+ ```
178
+
179
+ `experimental_minRetention` accepts the same values as [`sleep()`](/docs/api-reference/workflow/sleep): a duration string such as `"30d"`, a number of milliseconds, or an absolute `Date`. Durations start when `createHook()` runs.
180
+
181
+ The Hook remains active until the workflow ends, even if the configured time passes first. Another Hook can use the token only after both the workflow has ended and the configured time has passed. For example, `"30d"` keeps the token unavailable for 29 more days if the workflow ends after 1 day. A workflow that runs for more than 30 days releases the token when it ends.
182
+
183
+ After the workflow ends, [`getHookByToken()`](/docs/api-reference/workflow-api/get-hook-by-token) can still find the Hook until retention ends, but the Hook cannot be resumed.
184
+
185
+ <Callout type="warn">
186
+ `using` auto-disposes the Hook at scope exit, which releases the token immediately and defeats `experimental_minRetention`. Declare retained Hooks with `const` and let the runtime clean them up when the run ends.
187
+ </Callout>
188
+
189
+ <Callout type="warn">
190
+ This option is experimental. Worlds can limit how long tokens are retained; see [World configuration](/docs/configuration/worlds) for each World's limit. If the configured World does not support minimum retention, the workflow fails when registering the Hook. `createWebhook()` does not accept this option.
191
+ </Callout>
192
+
115
193
  ### Waiting for Multiple Payloads
116
194
 
117
195
  You can also wait for multiple payloads by using the `for await...of` syntax.
@@ -195,3 +273,4 @@ This is equivalent to manually calling `dispose()` but ensures the hook is alway
195
273
  - [`defineHook()`](/docs/api-reference/workflow/define-hook) - Type-safe hook helper
196
274
  - [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook) - Resume a hook with a payload
197
275
  - [`createWebhook()`](/docs/api-reference/workflow/create-webhook) - Higher-level HTTP webhook abstraction
276
+ - [Idempotency](/docs/foundations/idempotency) - Deduplicate step side effects and workflow starts
@@ -55,6 +55,7 @@ The returned `Webhook` object has:
55
55
 
56
56
  - `url`: The HTTP endpoint URL that external systems can call
57
57
  - `token`: The unique token identifying this webhook
58
+ - `getConflict()`: A promise that resolves with the conflicting run if another active hook already owns this token, or `null` once the webhook endpoint has been registered
58
59
  - Implements `AsyncIterable<T>` for handling multiple requests, where `T` is `Request` (default) or `RequestWithResponse` (manual mode)
59
60
 
60
61
  When using `createWebhook({ respondWith: 'manual' })`, the resolved request type is `RequestWithResponse`, which extends the standard `Request` interface with a `respondWith(response: Response): Promise<void>` method for sending custom responses back to the caller.
@@ -46,24 +46,21 @@ showSections={['parameters']}
46
46
 
47
47
  ### Returns
48
48
 
49
+ `defineHook()` returns a `TypedHook<TInput, TOutput>`:
50
+
49
51
  <TSDoc
50
52
  definition={`
51
- interface DefineHook<T> {
52
- /**
53
-
54
- * Creates a new hook with the defined payload type.
55
- */
56
- create: (options?: HookOptions) => Hook<T>;
57
-
58
- /**
59
-
60
- * Resumes a hook by sending a payload with the defined type.
61
- */
62
- resume: (token: string, payload: T) => Promise<HookEntity | null>;
53
+ interface TypedHook<TInput, TOutput> {
54
+ /** Creates the hook. Call inside a "use workflow" function. */
55
+ create(options?: HookOptions): Hook<TOutput>;
56
+ /** Resumes the hook from runtime code. Resolves to the resumed hook; throws HookNotFoundError if the token does not match an active hook. */
57
+ resume(token: string, payload: TInput): Promise<HookEntity>;
63
58
  }
64
- export default DefineHook;`}
59
+ export default TypedHook;`}
65
60
  />
66
61
 
62
+ `create()` is called inside a `"use workflow"` function to create the hook; `resume()` is called from runtime code (an API route or server action). When a `schema` is provided, `resume()` accepts the raw input type (`TInput`) and the workflow receives the validated and possibly transformed output type (`TOutput`); without a schema, `TOutput` defaults to `TInput`. `resume()` throws [`HookNotFoundError`](/docs/api-reference/workflow-errors/hook-not-found-error) if the token does not match an active hook — it does not return `null`.
63
+
67
64
  ## Examples
68
65
 
69
66
  ### Basic Type-Safe Hook Definition
@@ -93,24 +90,29 @@ export async function workflowWithApproval() {
93
90
 
94
91
  ### Resuming with Type Safety
95
92
 
96
- Hooks can be resumed using the same defined hook and a token. By using the same hook, you can ensure that the payload matches the defined type when resuming a hook.
93
+ Hooks can be resumed using the same defined hook and a token. By using the same hook, you can ensure that the payload matches the defined type when resuming a hook. `resume()` resolves to the resumed hook and throws [`HookNotFoundError`](/docs/api-reference/workflow-errors/hook-not-found-error) if the token does not match an active hook.
97
94
 
98
95
  ```typescript lineNumbers
96
+ import { HookNotFoundError } from "workflow/errors";
97
+
99
98
  // Use the same defined hook to resume
100
99
  export async function POST(request: Request) {
101
100
  const { token, approved, comment } = await request.json();
102
101
 
103
- // Type-safe resumption - TypeScript ensures the payload matches
104
- const result = await approvalHook.resume(token, { // [!code highlight]
105
- approved, // [!code highlight]
106
- comment, // [!code highlight]
107
- }); // [!code highlight]
108
-
109
- if (!result) {
110
- return Response.json({ error: "Hook not found" }, { status: 404 });
102
+ try {
103
+ // Type-safe resumption - TypeScript ensures the payload matches
104
+ const hook = await approvalHook.resume(token, { // [!code highlight]
105
+ approved, // [!code highlight]
106
+ comment, // [!code highlight]
107
+ }); // [!code highlight]
108
+
109
+ return Response.json({ success: true, runId: hook.runId });
110
+ } catch (error) {
111
+ if (HookNotFoundError.is(error)) { // [!code highlight]
112
+ return Response.json({ error: "Hook not found" }, { status: 404 });
113
+ }
114
+ throw error;
111
115
  }
112
-
113
- return Response.json({ success: true, runId: result.runId });
114
116
  }
115
117
  ```
116
118
 
@@ -29,16 +29,38 @@ async function fallibleStep() {
29
29
 
30
30
  ## API Signature
31
31
 
32
- ### Parameters
32
+ ### Constructor
33
+
34
+ `new FatalError(message)`
35
+
36
+ | Parameter | Type | Description |
37
+ | --------- | -------- | ------------------ |
38
+ | `message` | `string` | The error message. |
39
+
40
+ ### Properties
33
41
 
34
42
  <TSDoc
35
43
  definition={`
36
- interface Error {
37
- /**
38
-
39
- * The error message.
40
- */
44
+ interface FatalError {
45
+ /** The error message. */
41
46
  message: string;
47
+ /** Always true. Marks the error as non-retryable. */
48
+ fatal: boolean;
42
49
  }
43
- export default Error;`}
50
+ export default FatalError;`}
44
51
  />
52
+
53
+ ### Static Methods
54
+
55
+ #### `FatalError.is(value)`
56
+
57
+ Type-safe check for `FatalError` instances. Preferred over `instanceof` because it works across module boundaries and VM contexts.
58
+
59
+ ```typescript
60
+ import { FatalError } from "workflow"
61
+ declare const error: unknown; // @setup
62
+
63
+ if (FatalError.is(error)) {
64
+ // error is typed as FatalError
65
+ }
66
+ ```
@@ -7,12 +7,17 @@ prerequisites:
7
7
  - /docs/foundations/workflows-and-steps
8
8
  related:
9
9
  - /docs/errors/fetch-in-workflow
10
+ - /docs/foundations/idempotency
10
11
  ---
11
12
 
12
13
  Makes HTTP requests from within a workflow. This is a special step function that wraps the standard `fetch` API, automatically handling serialization and providing retry semantics.
13
14
 
14
15
  This is useful when you need to call external APIs or services from within your workflow.
15
16
 
17
+ <Callout type="warn">
18
+ Because workflow `fetch()` has retry semantics, use idempotency keys when the request mutates an external system, such as creating a charge, sending an email, or enqueueing work. See [Idempotency](/docs/foundations/idempotency).
19
+ </Callout>
20
+
16
21
  <Callout>
17
22
  `fetch` is a *special* type of step function provided and should be called directly inside workflow functions.
18
23
  </Callout>
@@ -88,7 +93,7 @@ This API is provided as a convenience to easily use `fetch` in workflow, but oft
88
93
 
89
94
  ### Customizing Fetch Behavior
90
95
 
91
- Here's an example of a custom fetch wrapper that provides more sophisticated error handling with custom retry logic:
96
+ Here's an example of a custom fetch wrapper that provides more sophisticated error handling with custom retry logic. Call `globalThis.fetch` inside your own `"use step"` function — calling the workflow `fetch` imported from `workflow` would nest a step inside a step:
92
97
 
93
98
  ```typescript lineNumbers
94
99
  import { FatalError, RetryableError } from "workflow"
@@ -99,7 +104,7 @@ export async function customFetch(
99
104
  ) {
100
105
  "use step"
101
106
 
102
- const response = await fetch(url, init)
107
+ const response = await globalThis.fetch(url, init)
103
108
 
104
109
  // Handle client errors (4xx) - don't retry
105
110
  if (response.status >= 400 && response.status < 500) {
@@ -140,7 +145,6 @@ export async function customFetch(
140
145
 
141
146
  This example demonstrates:
142
147
 
143
- - Setting custom `maxRetries` to 5 retries (6 total attempts including the initial attempt).
144
148
  - Throwing [`FatalError`](/docs/api-reference/workflow/fatal-error) for client errors (400-499) to prevent retries.
145
149
  - Handling 429 rate limiting by reading the `Retry-After` header and using [`RetryableError`](/docs/api-reference/workflow/retryable-error).
146
- - Allowing automatic retries for server errors (5xx).
150
+ - Allowing automatic retries for server errors (5xx) by throwing a plain `Error`.
@@ -47,6 +47,9 @@ Workflow SDK contains the following functions you can use inside your workflow f
47
47
  <Card href="/docs/api-reference/workflow/get-writable" title="getWritable()">
48
48
  Access the current workflow run's default stream.
49
49
  </Card>
50
+ <Card href="/docs/api-reference/workflow/set-attributes" title="setAttributes()">
51
+ Attach string metadata to the current workflow run.
52
+ </Card>
50
53
  </Cards>
51
54
 
52
55
  ## Error Classes
@@ -0,0 +1,61 @@
1
+ ---
2
+ title: setAttributes
3
+ description: Attach string metadata to workflow run for observability.
4
+ type: reference
5
+ summary: Use setAttributes inside a workflow or step function to set run attributes.
6
+ prerequisites:
7
+ - /docs/foundations/workflows-and-steps
8
+ related:
9
+ - /docs/observability/attributes
10
+ - /docs/api-reference/workflow/fatal-error
11
+ ---
12
+
13
+ Attaches string metadata to the current workflow run.
14
+
15
+ ```typescript lineNumbers
16
+ import { setAttributes } from "workflow"
17
+
18
+ export async function orderWorkflow(orderId: string) {
19
+ "use workflow"
20
+
21
+ await setAttributes({
22
+ phase: "received",
23
+ orderId,
24
+ })
25
+ }
26
+ ```
27
+
28
+ ## API Signature
29
+
30
+ ### Parameters
31
+
32
+ <TSDoc
33
+ definition={`
34
+ import { setAttributes } from "workflow";
35
+ export default setAttributes;`}
36
+ showSections={['parameters']}
37
+ />
38
+
39
+ ## Usage
40
+
41
+ Call `setAttributes` from a `"use workflow"` function or a `"use step"` function. Calling it from plain application code is not supported because there is no active workflow run.
42
+
43
+ Attribute values must be strings. Pass `undefined` to remove an attribute:
44
+
45
+ ```typescript lineNumbers
46
+ import { setAttributes } from "workflow"
47
+
48
+ export async function cleanupAttributes() {
49
+ "use workflow"
50
+
51
+ await setAttributes({ staleKey: undefined })
52
+ }
53
+ ```
54
+
55
+ Attribute keys must be 1-256 characters, values must be strings up to 256 bytes, and each run can have up to 64 attributes. Keys that start with `$` are reserved for framework and library code.
56
+
57
+ Validation errors throw [`FatalError`](/docs/api-reference/workflow/fatal-error) and fail the run before an attribute write is attempted.
58
+
59
+ Calls from both workflow and step bodies append a native `attr_set` event, which the World materializes onto `run.attributes`. Workflow-originated events record a workflow writer; step-originated events record the originating step ID and attempt.
60
+
61
+ Native attributes require spec version 4 or later. Step-body storage errors throw from `setAttributes`; catch them inside the step if the write should be best-effort. Workflow-body writes are committed when the workflow suspends: transient storage errors are retried with the suspension, while a write the World rejects as invalid — such as exceeding the per-run attribute cap across multiple calls — fails the run with the validation error.
@@ -14,7 +14,7 @@ Suspends a workflow for a specified duration or until an end date without consum
14
14
  This is useful when you want to resume a workflow after some duration or date.
15
15
 
16
16
  <Callout>
17
- `sleep` is a *special* type of step function and should be called directly inside workflow functions.
17
+ `sleep` is a built-in workflow runtime function (backed by a timer event in the event log, not a step) and should be called directly inside workflow functions.
18
18
  </Callout>
19
19
 
20
20
  ```typescript lineNumbers