@workflow/core 5.0.0-beta.2 → 5.0.0-beta.21

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 (251) 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 +22 -1
  5. package/dist/capabilities.d.ts.map +1 -1
  6. package/dist/capabilities.js +41 -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 +11 -3
  14. package/dist/classify-error.d.ts.map +1 -1
  15. package/dist/classify-error.js +59 -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 +147 -0
  22. package/dist/create-hook.d.ts +40 -1
  23. package/dist/create-hook.d.ts.map +1 -1
  24. package/dist/create-hook.js +4 -3
  25. package/dist/define-hook.d.ts.map +1 -1
  26. package/dist/define-hook.js +20 -5
  27. package/dist/describe-error.d.ts +70 -0
  28. package/dist/describe-error.d.ts.map +1 -0
  29. package/dist/describe-error.js +165 -0
  30. package/dist/encryption.d.ts +22 -1
  31. package/dist/encryption.d.ts.map +1 -1
  32. package/dist/encryption.js +72 -27
  33. package/dist/events-consumer.d.ts +24 -0
  34. package/dist/events-consumer.d.ts.map +1 -1
  35. package/dist/events-consumer.js +79 -16
  36. package/dist/flushable-stream.d.ts +10 -10
  37. package/dist/flushable-stream.d.ts.map +1 -1
  38. package/dist/flushable-stream.js +18 -13
  39. package/dist/global.d.ts +15 -1
  40. package/dist/global.d.ts.map +1 -1
  41. package/dist/global.js +21 -3
  42. package/dist/index.d.ts +1 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +2 -1
  45. package/dist/log-format.d.ts +25 -0
  46. package/dist/log-format.d.ts.map +1 -0
  47. package/dist/log-format.js +250 -0
  48. package/dist/logger.d.ts +28 -30
  49. package/dist/logger.d.ts.map +1 -1
  50. package/dist/logger.js +77 -31
  51. package/dist/private.d.ts +107 -2
  52. package/dist/private.d.ts.map +1 -1
  53. package/dist/private.js +80 -3
  54. package/dist/runtime/constants.d.ts +121 -0
  55. package/dist/runtime/constants.d.ts.map +1 -1
  56. package/dist/runtime/constants.js +206 -8
  57. package/dist/runtime/get-port-lazy.d.ts +25 -0
  58. package/dist/runtime/get-port-lazy.d.ts.map +1 -0
  59. package/dist/runtime/get-port-lazy.js +92 -0
  60. package/dist/runtime/get-world-lazy.d.ts +32 -0
  61. package/dist/runtime/get-world-lazy.d.ts.map +1 -0
  62. package/dist/runtime/get-world-lazy.js +56 -0
  63. package/dist/runtime/helpers.d.ts +50 -7
  64. package/dist/runtime/helpers.d.ts.map +1 -1
  65. package/dist/runtime/helpers.js +149 -31
  66. package/dist/runtime/replay-budget.d.ts +98 -0
  67. package/dist/runtime/replay-budget.d.ts.map +1 -0
  68. package/dist/runtime/replay-budget.js +191 -0
  69. package/dist/runtime/resume-hook.d.ts.map +1 -1
  70. package/dist/runtime/resume-hook.js +39 -23
  71. package/dist/runtime/run-id-time.d.ts +10 -0
  72. package/dist/runtime/run-id-time.d.ts.map +1 -0
  73. package/dist/runtime/run-id-time.js +31 -0
  74. package/dist/runtime/run.d.ts.map +1 -1
  75. package/dist/runtime/run.js +44 -9
  76. package/dist/runtime/runs.d.ts.map +1 -1
  77. package/dist/runtime/runs.js +4 -1
  78. package/dist/runtime/start.d.ts +30 -1
  79. package/dist/runtime/start.d.ts.map +1 -1
  80. package/dist/runtime/start.js +138 -20
  81. package/dist/runtime/step-executor.d.ts +108 -0
  82. package/dist/runtime/step-executor.d.ts.map +1 -0
  83. package/dist/runtime/step-executor.js +715 -0
  84. package/dist/runtime/step-handler.d.ts.map +1 -1
  85. package/dist/runtime/step-handler.js +715 -556
  86. package/dist/runtime/suspension-handler.d.ts +74 -7
  87. package/dist/runtime/suspension-handler.d.ts.map +1 -1
  88. package/dist/runtime/suspension-handler.js +308 -116
  89. package/dist/runtime/wait-continuation.d.ts +84 -0
  90. package/dist/runtime/wait-continuation.d.ts.map +1 -0
  91. package/dist/runtime/wait-continuation.js +92 -0
  92. package/dist/runtime/wait-until.d.ts +18 -0
  93. package/dist/runtime/wait-until.d.ts.map +1 -0
  94. package/dist/runtime/wait-until.js +42 -0
  95. package/dist/runtime/world-init.d.ts +75 -0
  96. package/dist/runtime/world-init.d.ts.map +1 -0
  97. package/dist/runtime/world-init.js +75 -0
  98. package/dist/runtime/world.d.ts.map +1 -1
  99. package/dist/runtime/world.js +47 -18
  100. package/dist/runtime.d.ts +11 -8
  101. package/dist/runtime.d.ts.map +1 -1
  102. package/dist/runtime.js +1395 -319
  103. package/dist/schemas.d.ts +1 -1
  104. package/dist/schemas.d.ts.map +1 -1
  105. package/dist/schemas.js +1 -1
  106. package/dist/serialization/client.d.ts +17 -0
  107. package/dist/serialization/client.d.ts.map +1 -0
  108. package/dist/serialization/client.js +48 -0
  109. package/dist/serialization/codec-devalue.d.ts +14 -0
  110. package/dist/serialization/codec-devalue.d.ts.map +1 -0
  111. package/dist/serialization/codec-devalue.js +98 -0
  112. package/dist/serialization/codec.d.ts +106 -0
  113. package/dist/serialization/codec.d.ts.map +1 -0
  114. package/dist/serialization/codec.js +17 -0
  115. package/dist/serialization/compression.d.ts +104 -0
  116. package/dist/serialization/compression.d.ts.map +1 -0
  117. package/dist/serialization/compression.js +260 -0
  118. package/dist/serialization/encryption.d.ts +33 -0
  119. package/dist/serialization/encryption.d.ts.map +1 -0
  120. package/dist/serialization/encryption.js +70 -0
  121. package/dist/serialization/errors.d.ts +34 -0
  122. package/dist/serialization/errors.d.ts.map +1 -0
  123. package/dist/serialization/errors.js +59 -0
  124. package/dist/serialization/format.d.ts +60 -0
  125. package/dist/serialization/format.d.ts.map +1 -0
  126. package/dist/serialization/format.js +97 -0
  127. package/dist/serialization/index.d.ts +19 -0
  128. package/dist/serialization/index.d.ts.map +1 -0
  129. package/dist/serialization/index.js +22 -0
  130. package/dist/serialization/reducers/class.d.ts +11 -0
  131. package/dist/serialization/reducers/class.d.ts.map +1 -0
  132. package/dist/serialization/reducers/class.js +68 -0
  133. package/dist/serialization/reducers/common.d.ts +16 -0
  134. package/dist/serialization/reducers/common.d.ts.map +1 -0
  135. package/dist/serialization/reducers/common.js +417 -0
  136. package/dist/serialization/reducers/step-function.d.ts +35 -0
  137. package/dist/serialization/reducers/step-function.d.ts.map +1 -0
  138. package/dist/serialization/reducers/step-function.js +88 -0
  139. package/dist/serialization/step.d.ts +17 -0
  140. package/dist/serialization/step.d.ts.map +1 -0
  141. package/dist/serialization/step.js +48 -0
  142. package/dist/serialization/types.d.ts +253 -0
  143. package/dist/serialization/types.d.ts.map +1 -0
  144. package/dist/serialization/types.js +25 -0
  145. package/dist/serialization/workflow.d.ts +29 -0
  146. package/dist/serialization/workflow.d.ts.map +1 -0
  147. package/dist/serialization/workflow.js +54 -0
  148. package/dist/serialization-format.d.ts +15 -0
  149. package/dist/serialization-format.d.ts.map +1 -1
  150. package/dist/serialization-format.js +209 -11
  151. package/dist/serialization.d.ts +313 -214
  152. package/dist/serialization.d.ts.map +1 -1
  153. package/dist/serialization.js +1430 -658
  154. package/dist/set-attributes.d.ts +13 -0
  155. package/dist/set-attributes.d.ts.map +1 -0
  156. package/dist/set-attributes.js +42 -0
  157. package/dist/sleep.d.ts.map +1 -1
  158. package/dist/sleep.js +3 -2
  159. package/dist/source-map.d.ts +5 -0
  160. package/dist/source-map.d.ts.map +1 -1
  161. package/dist/source-map.js +102 -10
  162. package/dist/step/context-storage.d.ts +43 -0
  163. package/dist/step/context-storage.d.ts.map +1 -1
  164. package/dist/step/context-storage.js +1 -1
  165. package/dist/step/get-closure-vars.d.ts.map +1 -1
  166. package/dist/step/get-closure-vars.js +3 -2
  167. package/dist/step/get-step-metadata.d.ts.map +1 -1
  168. package/dist/step/get-step-metadata.js +3 -2
  169. package/dist/step/get-workflow-metadata.d.ts.map +1 -1
  170. package/dist/step/get-workflow-metadata.js +3 -2
  171. package/dist/step/writable-stream.d.ts.map +1 -1
  172. package/dist/step/writable-stream.js +49 -5
  173. package/dist/step-hydration-cache.d.ts +148 -0
  174. package/dist/step-hydration-cache.d.ts.map +1 -0
  175. package/dist/step-hydration-cache.js +171 -0
  176. package/dist/step.d.ts.map +1 -1
  177. package/dist/step.js +115 -21
  178. package/dist/symbols.d.ts +28 -0
  179. package/dist/symbols.d.ts.map +1 -1
  180. package/dist/symbols.js +29 -1
  181. package/dist/telemetry/semantic-conventions.d.ts +30 -2
  182. package/dist/telemetry/semantic-conventions.d.ts.map +1 -1
  183. package/dist/telemetry/semantic-conventions.js +21 -1
  184. package/dist/telemetry.d.ts +60 -0
  185. package/dist/telemetry.d.ts.map +1 -1
  186. package/dist/telemetry.js +82 -4
  187. package/dist/types.d.ts +6 -0
  188. package/dist/types.d.ts.map +1 -1
  189. package/dist/types.js +23 -1
  190. package/dist/util.d.ts +16 -6
  191. package/dist/util.d.ts.map +1 -1
  192. package/dist/util.js +25 -16
  193. package/dist/version.d.ts +1 -1
  194. package/dist/version.d.ts.map +1 -1
  195. package/dist/version.js +2 -2
  196. package/dist/vm/index.d.ts.map +1 -1
  197. package/dist/vm/index.js +3 -2
  198. package/dist/vm/script-cache.d.ts +28 -0
  199. package/dist/vm/script-cache.d.ts.map +1 -0
  200. package/dist/vm/script-cache.js +140 -0
  201. package/dist/workflow/abort-controller.d.ts +65 -0
  202. package/dist/workflow/abort-controller.d.ts.map +1 -0
  203. package/dist/workflow/abort-controller.js +277 -0
  204. package/dist/workflow/attribute-dispatcher.d.ts +6 -0
  205. package/dist/workflow/attribute-dispatcher.d.ts.map +1 -0
  206. package/dist/workflow/attribute-dispatcher.js +48 -0
  207. package/dist/workflow/create-hook.d.ts.map +1 -1
  208. package/dist/workflow/create-hook.js +23 -2
  209. package/dist/workflow/define-hook.d.ts +1 -1
  210. package/dist/workflow/define-hook.d.ts.map +1 -1
  211. package/dist/workflow/define-hook.js +8 -4
  212. package/dist/workflow/get-workflow-metadata.d.ts.map +1 -1
  213. package/dist/workflow/get-workflow-metadata.js +14 -3
  214. package/dist/workflow/hook.d.ts.map +1 -1
  215. package/dist/workflow/hook.js +207 -33
  216. package/dist/workflow/index.d.ts +1 -0
  217. package/dist/workflow/index.d.ts.map +1 -1
  218. package/dist/workflow/index.js +5 -3
  219. package/dist/workflow/set-attributes.d.ts +73 -0
  220. package/dist/workflow/set-attributes.d.ts.map +1 -0
  221. package/dist/workflow/set-attributes.js +65 -0
  222. package/dist/workflow/sleep.d.ts.map +1 -1
  223. package/dist/workflow/sleep.js +45 -8
  224. package/dist/workflow/world-init-stub.d.ts +15 -0
  225. package/dist/workflow/world-init-stub.d.ts.map +1 -0
  226. package/dist/workflow/world-init-stub.js +15 -0
  227. package/dist/workflow.d.ts +10 -1
  228. package/dist/workflow.d.ts.map +1 -1
  229. package/dist/workflow.js +168 -49
  230. package/docs/api-reference/create-hook.mdx +38 -0
  231. package/docs/api-reference/create-webhook.mdx +1 -0
  232. package/docs/api-reference/experimental-set-attributes.mdx +65 -0
  233. package/docs/api-reference/fetch.mdx +5 -0
  234. package/docs/api-reference/index.mdx +3 -0
  235. package/docs/foundations/cancellation.mdx +459 -0
  236. package/docs/foundations/errors-and-retries.mdx +7 -3
  237. package/docs/foundations/hooks.mdx +29 -0
  238. package/docs/foundations/idempotency.mdx +236 -11
  239. package/docs/foundations/index.mdx +3 -3
  240. package/docs/foundations/meta.json +3 -2
  241. package/docs/foundations/serialization.mdx +78 -42
  242. package/docs/foundations/starting-workflows.mdx +6 -2
  243. package/docs/foundations/streaming.mdx +14 -23
  244. package/docs/foundations/versioning.mdx +263 -0
  245. package/docs/how-it-works/cancellation.mdx +287 -0
  246. package/docs/how-it-works/code-transform.mdx +2 -2
  247. package/docs/how-it-works/encryption.mdx +2 -2
  248. package/docs/how-it-works/event-sourcing.mdx +2 -2
  249. package/docs/how-it-works/meta.json +2 -1
  250. package/package.json +22 -10
  251. package/docs/foundations/common-patterns.mdx +0 -265
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workflow/core",
3
- "version": "5.0.0-beta.2",
3
+ "version": "5.0.0-beta.21",
4
4
  "description": "Core runtime and engine for Workflow SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -48,6 +48,11 @@
48
48
  "types": "./dist/runtime/resume-hook.d.ts",
49
49
  "default": "./dist/runtime/resume-hook.js"
50
50
  },
51
+ "./runtime/world-init": {
52
+ "types": "./dist/runtime/world-init.d.ts",
53
+ "workflow": "./dist/workflow/world-init-stub.js",
54
+ "default": "./dist/runtime/world-init.js"
55
+ },
51
56
  "./class-serialization": {
52
57
  "types": "./dist/class-serialization.d.ts",
53
58
  "default": "./dist/class-serialization.js"
@@ -64,35 +69,42 @@
64
69
  "types": "./dist/encryption.d.ts",
65
70
  "default": "./dist/encryption.js"
66
71
  },
72
+ "./describe-error": {
73
+ "types": "./dist/describe-error.d.ts",
74
+ "default": "./dist/describe-error.js"
75
+ },
67
76
  "./_workflow": "./dist/workflow/index.js"
68
77
  },
69
78
  "dependencies": {
70
- "@aws-sdk/credential-provider-web-identity": "3.972.13",
79
+ "@aws-sdk/credential-provider-web-identity": "3.972.49",
71
80
  "@jridgewell/trace-mapping": "0.3.31",
72
81
  "@standard-schema/spec": "1.0.0",
73
82
  "@types/ms": "2.1.0",
74
83
  "@vercel/functions": "^3.4.3",
75
84
  "debug": "4.4.3",
76
- "devalue": "5.6.3",
85
+ "devalue": "5.8.1",
77
86
  "ms": "2.1.3",
78
87
  "nanoid": "5.1.6",
79
88
  "seedrandom": "3.0.5",
80
89
  "semver": "7.7.4",
81
90
  "ulid": "~3.0.1",
82
91
  "zod": "4.3.6",
83
- "@workflow/errors": "5.0.0-beta.1",
84
- "@workflow/serde": "5.0.0-beta.1",
85
- "@workflow/utils": "5.0.0-beta.1",
86
- "@workflow/world": "5.0.0-beta.1",
87
- "@workflow/world-local": "5.0.0-beta.2",
88
- "@workflow/world-vercel": "5.0.0-beta.2"
92
+ "@workflow/errors": "5.0.0-beta.8",
93
+ "@workflow/serde": "5.0.0-beta.2",
94
+ "@workflow/utils": "5.0.0-beta.4",
95
+ "@workflow/world": "5.0.0-beta.12",
96
+ "@workflow/world-local": "5.0.0-beta.20",
97
+ "@workflow/world-vercel": "5.0.0-beta.19"
89
98
  },
90
99
  "devDependencies": {
91
100
  "@opentelemetry/api": "1.9.0",
101
+ "@opentelemetry/context-async-hooks": "1.30.1",
102
+ "@opentelemetry/core": "1.30.1",
103
+ "@opentelemetry/sdk-trace-base": "1.30.1",
92
104
  "@types/debug": "4.1.12",
93
- "@types/semver": "7.7.1",
94
105
  "@types/node": "22.19.0",
95
106
  "@types/seedrandom": "3.0.8",
107
+ "@types/semver": "7.7.1",
96
108
  "cross-env": "10.1.0",
97
109
  "genversion": "3.2.0",
98
110
  "@workflow/tsconfig": "5.0.0-beta.0"
@@ -1,265 +0,0 @@
1
- ---
2
- title: Common Patterns
3
- description: Implement distributed patterns using familiar async/await syntax with no new APIs to learn.
4
- type: guide
5
- summary: Apply sequential, parallel, timeout, and composition patterns in workflows.
6
- prerequisites:
7
- - /docs/foundations/workflows-and-steps
8
- related:
9
- - /docs/foundations/errors-and-retries
10
- - /docs/foundations/hooks
11
- ---
12
-
13
- Common distributed patterns are simple to implement in workflows and require learning no new syntax. You can just use familiar async/await patterns.
14
-
15
- ## Sequential Execution
16
-
17
- The simplest way to orchestrate steps is to execute them one after another, where each step can be dependent on the previous step.
18
-
19
- ```typescript lineNumbers
20
- declare function validateData(data: unknown): Promise<string>; // @setup
21
- declare function processData(data: string): Promise<string>; // @setup
22
- declare function storeData(data: string): Promise<string>; // @setup
23
-
24
- export async function dataPipelineWorkflow(data: unknown) {
25
- "use workflow";
26
-
27
- const validated = await validateData(data);
28
- const processed = await processData(validated);
29
- const stored = await storeData(processed);
30
-
31
- return stored;
32
- }
33
- ```
34
-
35
- ## Parallel Execution
36
-
37
- When you need to execute multiple steps in parallel, you can use `Promise.all` to run them all at the same time.
38
-
39
- ```typescript lineNumbers
40
- declare function fetchUser(userId: string): Promise<{ name: string }>; // @setup
41
- declare function fetchOrders(userId: string): Promise<{ items: string[] }>; // @setup
42
- declare function fetchPreferences(userId: string): Promise<{ theme: string }>; // @setup
43
-
44
- export async function fetchUserData(userId: string) {
45
- "use workflow";
46
-
47
- const [user, orders, preferences] = await Promise.all([ // [!code highlight]
48
- fetchUser(userId), // [!code highlight]
49
- fetchOrders(userId), // [!code highlight]
50
- fetchPreferences(userId) // [!code highlight]
51
- ]); // [!code highlight]
52
-
53
- return { user, orders, preferences };
54
- }
55
- ```
56
-
57
- This not only applies to steps - since [`sleep()`](/docs/api-reference/workflow/sleep) and [`webhook`](/docs/api-reference/workflow/create-webhook) are also just promises, we can await those in parallel too.
58
- We can also use `Promise.race` instead of `Promise.all` to stop executing promises after the first one completes.
59
-
60
- ```typescript lineNumbers
61
- import { sleep, createWebhook } from "workflow";
62
- declare function executeExternalTask(webhookUrl: string): Promise<void>; // @setup
63
-
64
- export async function runExternalTask(userId: string) {
65
- "use workflow";
66
-
67
- const webhook = createWebhook();
68
- await executeExternalTask(webhook.url); // Send the webhook somewhere
69
-
70
- // Wait for the external webhook to be hit, with a timeout of 1 day,
71
- // whichever comes first
72
- await Promise.race([ // [!code highlight]
73
- webhook, // [!code highlight]
74
- sleep("1 day"), // [!code highlight]
75
- ]); // [!code highlight]
76
-
77
- console.log("Done")
78
- }
79
- ```
80
-
81
- ## A Full Example
82
-
83
- Here's a simplified example taken from the [birthday card generator demo](https://github.com/vercel/workflow-examples/tree/main/birthday-card-generator), to illustrate how sequential and parallel execution can be combined.
84
-
85
- ```typescript lineNumbers
86
- import { createWebhook, sleep, type Webhook } from "workflow"
87
- declare function makeCardText(prompt: string): Promise<string>; // @setup
88
- declare function makeCardImage(text: string): Promise<string>; // @setup
89
- declare function sendRSVPEmail(friend: string, webhook: Webhook): Promise<void>; // @setup
90
- declare function sendBirthdayCard(text: string, image: string, rsvps: unknown[], email: string): Promise<void>; // @setup
91
-
92
- async function birthdayWorkflow(
93
- prompt: string,
94
- email: string,
95
- friends: string[],
96
- birthday: Date
97
- ) {
98
- "use workflow";
99
-
100
- // Generate a birthday card with sequential steps
101
- const text = await makeCardText(prompt)
102
- const image = await makeCardImage(text)
103
-
104
- // Create webhooks for each friend who's invited to the birthday party
105
- const webhooks = friends.map(_ => createWebhook())
106
-
107
- // Send out all the RSVP invites in parallel steps
108
- await Promise.all(
109
- friends.map(
110
- (friend, i) => sendRSVPEmail(friend, webhooks[i])
111
- )
112
- )
113
-
114
- // Collect RSVPs as they are made without blocking the workflow
115
- let rsvps = []
116
- webhooks.map(
117
- webhook => webhook
118
- .then(req => req.json())
119
- .then(( { rsvp } ) => rsvps.push(rsvp))
120
- )
121
-
122
- // Wait until the birthday
123
- await sleep(birthday)
124
-
125
- // Send birthday card with as many rsvps were collected
126
- await sendBirthdayCard(text, image, rsvps, email)
127
-
128
- return { text, image, status: "Sent" }
129
- }
130
- ```
131
-
132
- ## Timeout Pattern
133
-
134
- A common requirement is adding timeouts to operations that might take too long. Use `Promise.race` with `sleep()` to implement this pattern.
135
-
136
- ```typescript lineNumbers
137
- import { sleep } from "workflow";
138
- declare function processData(data: string): Promise<string>; // @setup
139
-
140
- export async function processWithTimeout(data: string) {
141
- "use workflow";
142
-
143
- const result = await Promise.race([ // [!code highlight]
144
- processData(data), // [!code highlight]
145
- sleep("30s").then(() => "timeout" as const), // [!code highlight]
146
- ]); // [!code highlight]
147
-
148
- if (result === "timeout") {
149
- // In workflows, any thrown error exits the workflow (FatalError is for steps)
150
- throw new Error("Processing timed out after 30 seconds");
151
- }
152
-
153
- return result;
154
- }
155
- ```
156
-
157
- This pattern works with any promise-returning operation including steps, hooks, and webhooks. For example, you can add a timeout to a webhook that waits for external input:
158
-
159
- ```typescript lineNumbers
160
- import { sleep, createWebhook } from "workflow";
161
- declare function sendApprovalRequest(requestId: string, webhookUrl: string): Promise<void>; // @setup
162
-
163
- export async function waitForApproval(requestId: string) {
164
- "use workflow";
165
-
166
- const webhook = createWebhook<{ approved: boolean }>();
167
- await sendApprovalRequest(requestId, webhook.url);
168
-
169
- const result = await Promise.race([ // [!code highlight]
170
- webhook.then((req) => req.json()), // [!code highlight]
171
- sleep("7 days").then(() => ({ timedOut: true }) as const), // [!code highlight]
172
- ]); // [!code highlight]
173
-
174
- if ("timedOut" in result) {
175
- throw new Error("Approval request expired after 7 days");
176
- }
177
-
178
- return result.approved;
179
- }
180
- ```
181
-
182
- ## Workflow Composition
183
-
184
- Workflows can call other workflows, enabling you to break complex processes into reusable building blocks. There are two approaches depending on your needs.
185
-
186
- ### Direct Await (Flattening)
187
-
188
- Call a child workflow directly using `await`. This "flattens" the child workflow into the parent - the child's steps execute inline within the parent workflow's context.
189
-
190
- ```typescript lineNumbers
191
- declare function sendEmail(userId: string): Promise<void>; // @setup
192
- declare function sendPushNotification(userId: string): Promise<void>; // @setup
193
- declare function createAccount(userId: string): Promise<void>; // @setup
194
- declare function setupPreferences(userId: string): Promise<void>; // @setup
195
-
196
- // Child workflow
197
- export async function sendNotifications(userId: string) {
198
- "use workflow";
199
-
200
- await sendEmail(userId);
201
- await sendPushNotification(userId);
202
- return { notified: true };
203
- }
204
-
205
- // Parent workflow calls child directly
206
- export async function onboardUser(userId: string) {
207
- "use workflow";
208
-
209
- await createAccount(userId);
210
- await sendNotifications(userId); // [!code highlight]
211
- await setupPreferences(userId);
212
-
213
- return { userId, status: "onboarded" };
214
- }
215
- ```
216
-
217
- With direct await, the parent workflow waits for the child to complete before continuing. The child's steps appear in the parent's event log as if they were called directly from the parent.
218
-
219
- ### Background Execution via Step
220
-
221
- To run a child workflow independently without blocking the parent, use a step that calls [`start()`](/docs/api-reference/workflow-api/start). This launches the child workflow in the background.
222
-
223
- ```typescript lineNumbers
224
- import { start } from "workflow/api";
225
- declare function generateReport(reportId: string): Promise<void>; // @setup
226
- declare function fulfillOrder(orderId: string): Promise<{ id: string }>; // @setup
227
- declare function sendConfirmation(orderId: string): Promise<void>; // @setup
228
-
229
- // Step that starts a workflow in the background
230
- async function triggerReportGeneration(reportId: string) {
231
- "use step";
232
-
233
- const run = await start(generateReport, [reportId]); // [!code highlight]
234
- return run.runId;
235
- }
236
-
237
- // Parent workflow
238
- export async function processOrder(orderId: string) {
239
- "use workflow";
240
-
241
- const order = await fulfillOrder(orderId);
242
-
243
- // Fire off report generation without waiting
244
- const reportRunId = await triggerReportGeneration(orderId); // [!code highlight]
245
-
246
- // Continue immediately - report generates in background
247
- await sendConfirmation(orderId);
248
-
249
- return { orderId, reportRunId };
250
- }
251
- ```
252
-
253
- With background execution, the parent workflow continues immediately after starting the child. The child workflow runs independently with its own event log and can be monitored separately using the returned `runId`.
254
-
255
- <Callout type="info">
256
- If you want the child workflow to run on the latest deployment rather than the current one, you can pass [`deploymentId: "latest"`](/docs/api-reference/workflow-api/start#using-deploymentid-latest) in the `start()` options. This is currently a Vercel-specific feature. Be aware that the child workflow's function name, file path, argument types, and return type must remain compatible across deployments — renaming the function or changing its location will change the workflow ID, and modifying expected inputs or outputs can cause serialization failures.
257
- </Callout>
258
-
259
- **Choose direct await when:**
260
- - The parent needs the child's result before continuing
261
- - You want a single, unified event log
262
-
263
- **Choose background execution when:**
264
- - The parent doesn't need to wait for the result
265
- - You want separate workflow runs for observability