workflow 5.0.0-beta.9 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/README.md +68 -23
  2. package/dist/api-workflow.d.ts +3 -1
  3. package/dist/api-workflow.d.ts.map +1 -1
  4. package/dist/api-workflow.js +2 -1
  5. package/dist/api.d.ts +5 -4
  6. package/dist/api.d.ts.map +1 -1
  7. package/dist/api.js +6 -7
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +6 -1
  11. package/dist/internal/builtins.d.ts +4 -4
  12. package/dist/internal/builtins.js +6 -6
  13. package/dist/internal/errors.d.ts +1 -1
  14. package/dist/internal/errors.d.ts.map +1 -1
  15. package/dist/internal/errors.js +2 -2
  16. package/dist/nest-builder.d.ts +2 -0
  17. package/dist/nest-builder.d.ts.map +1 -0
  18. package/dist/nest-builder.js +2 -0
  19. package/dist/nest-vercel-builder.d.ts +2 -0
  20. package/dist/nest-vercel-builder.d.ts.map +1 -0
  21. package/dist/nest-vercel-builder.js +2 -0
  22. package/dist/runtime.d.ts +2 -1
  23. package/dist/runtime.d.ts.map +1 -1
  24. package/dist/runtime.js +4 -1
  25. package/docs/advanced/dynamic-workflows.mdx +224 -0
  26. package/docs/ai/chat-session-modeling.mdx +176 -422
  27. package/docs/ai/defining-tools.mdx +6 -7
  28. package/docs/ai/human-in-the-loop.mdx +11 -11
  29. package/docs/ai/index.mdx +67 -72
  30. package/docs/ai/message-queueing.mdx +71 -110
  31. package/docs/ai/meta.json +1 -0
  32. package/docs/ai/resumable-streams.mdx +40 -28
  33. package/docs/ai/sleep-and-delays.mdx +10 -10
  34. package/docs/ai/streaming-updates-from-tools.mdx +6 -6
  35. package/docs/api-reference/index.mdx +25 -1
  36. package/docs/api-reference/meta.json +8 -0
  37. package/docs/api-reference/vitest/index.mdx +68 -15
  38. package/docs/api-reference/workflow/create-hook.mdx +166 -10
  39. package/docs/api-reference/workflow/create-webhook.mdx +16 -15
  40. package/docs/api-reference/workflow/define-hook.mdx +37 -33
  41. package/docs/api-reference/workflow/fatal-error.mdx +30 -8
  42. package/docs/api-reference/workflow/fetch.mdx +14 -10
  43. package/docs/api-reference/workflow/get-step-metadata.mdx +2 -2
  44. package/docs/api-reference/workflow/get-workflow-metadata.mdx +3 -3
  45. package/docs/api-reference/workflow/get-writable.mdx +7 -7
  46. package/docs/api-reference/workflow/index.mdx +3 -3
  47. package/docs/api-reference/workflow/retryable-error.mdx +1 -1
  48. package/docs/api-reference/workflow/set-attributes.mdx +63 -0
  49. package/docs/api-reference/workflow/sleep.mdx +4 -4
  50. package/docs/api-reference/workflow-ai/durable-agent.mdx +63 -101
  51. package/docs/api-reference/workflow-ai/index.mdx +5 -5
  52. package/docs/api-reference/workflow-ai/workflow-chat-transport.mdx +67 -24
  53. package/docs/api-reference/workflow-api/get-hook-by-token.mdx +28 -12
  54. package/docs/api-reference/workflow-api/get-run.mdx +43 -8
  55. package/docs/api-reference/workflow-api/index.mdx +8 -9
  56. package/docs/api-reference/workflow-api/register-lifecycle-hooks.mdx +87 -0
  57. package/docs/api-reference/workflow-api/resume-hook.mdx +73 -12
  58. package/docs/api-reference/workflow-api/resume-webhook.mdx +11 -9
  59. package/docs/api-reference/workflow-api/start.mdx +107 -12
  60. package/docs/api-reference/workflow-astro/index.mdx +18 -0
  61. package/docs/api-reference/workflow-astro/meta.json +4 -0
  62. package/docs/api-reference/workflow-astro/workflow.mdx +45 -0
  63. package/docs/api-reference/workflow-errors/entity-conflict-error.mdx +4 -4
  64. package/docs/api-reference/workflow-errors/hook-conflict-error.mdx +60 -0
  65. package/docs/api-reference/workflow-errors/hook-force-claimed-error.mdx +70 -0
  66. package/docs/api-reference/workflow-errors/hook-not-found-error.mdx +8 -8
  67. package/docs/api-reference/workflow-errors/index.mdx +91 -0
  68. package/docs/api-reference/workflow-errors/meta.json +7 -0
  69. package/docs/api-reference/workflow-errors/precondition-failed-error.mdx +68 -0
  70. package/docs/api-reference/workflow-errors/run-expired-error.mdx +2 -2
  71. package/docs/api-reference/workflow-errors/run-not-supported-error.mdx +58 -0
  72. package/docs/api-reference/workflow-errors/step-not-registered-error.mdx +5 -5
  73. package/docs/api-reference/workflow-errors/throttle-error.mdx +2 -2
  74. package/docs/api-reference/workflow-errors/too-early-error.mdx +2 -2
  75. package/docs/api-reference/workflow-errors/workflow-error.mdx +52 -0
  76. package/docs/api-reference/workflow-errors/workflow-not-registered-error.mdx +5 -6
  77. package/docs/api-reference/workflow-errors/workflow-run-cancelled-error.mdx +13 -6
  78. package/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx +12 -5
  79. package/docs/api-reference/workflow-errors/workflow-run-not-completed-error.mdx +58 -0
  80. package/docs/api-reference/workflow-errors/workflow-run-not-found-error.mdx +4 -4
  81. package/docs/api-reference/workflow-errors/workflow-runtime-error.mdx +58 -0
  82. package/docs/api-reference/workflow-errors/workflow-world-error.mdx +8 -8
  83. package/docs/api-reference/workflow-globals.mdx +15 -11
  84. package/docs/api-reference/workflow-nest/configure-workflow-controller.mdx +37 -0
  85. package/docs/api-reference/workflow-nest/index.mdx +31 -0
  86. package/docs/api-reference/workflow-nest/meta.json +9 -0
  87. package/docs/api-reference/workflow-nest/nest-local-builder.mdx +64 -0
  88. package/docs/api-reference/workflow-nest/workflow-controller.mdx +46 -0
  89. package/docs/api-reference/workflow-nest/workflow-module.mdx +134 -0
  90. package/docs/api-reference/workflow-next/with-workflow.mdx +39 -17
  91. package/docs/api-reference/workflow-nitro/index.mdx +60 -0
  92. package/docs/api-reference/workflow-nuxt/index.mdx +48 -0
  93. package/docs/api-reference/workflow-observability/hydrate-data.mdx +35 -0
  94. package/docs/api-reference/workflow-observability/hydrate-resource-io.mdx +62 -0
  95. package/docs/api-reference/workflow-observability/index.mdx +62 -0
  96. package/docs/api-reference/workflow-observability/meta.json +11 -0
  97. package/docs/api-reference/workflow-observability/observability-revivers.mdx +50 -0
  98. package/docs/api-reference/workflow-observability/parse-class-name.mdx +41 -0
  99. package/docs/api-reference/workflow-observability/parse-step-name.mdx +40 -0
  100. package/docs/api-reference/workflow-observability/parse-workflow-name.mdx +55 -0
  101. package/docs/api-reference/workflow-runtime/create-world.mdx +39 -0
  102. package/docs/api-reference/workflow-runtime/get-world-handlers.mdx +44 -0
  103. package/docs/api-reference/{workflow-api → workflow-runtime}/get-world.mdx +11 -14
  104. package/docs/api-reference/workflow-runtime/health-check.mdx +51 -0
  105. package/docs/api-reference/workflow-runtime/index.mdx +41 -0
  106. package/docs/api-reference/workflow-runtime/meta.json +12 -0
  107. package/docs/api-reference/workflow-runtime/set-world.mdx +51 -0
  108. package/docs/api-reference/workflow-runtime/workflow-entrypoint.mdx +43 -0
  109. package/docs/api-reference/workflow-runtime/world/analytics.mdx +315 -0
  110. package/docs/api-reference/workflow-runtime/world/index.mdx +60 -0
  111. package/docs/api-reference/workflow-runtime/world/meta.json +4 -0
  112. package/docs/api-reference/workflow-runtime/world/queue.mdx +88 -0
  113. package/docs/api-reference/{workflow-api → workflow-runtime}/world/storage.mdx +104 -35
  114. package/docs/api-reference/{workflow-api → workflow-runtime}/world/streams.mdx +8 -8
  115. package/docs/api-reference/workflow-serde/index.mdx +1 -2
  116. package/docs/api-reference/workflow-serde/workflow-deserialize.mdx +3 -4
  117. package/docs/api-reference/workflow-serde/workflow-serialize.mdx +8 -8
  118. package/docs/api-reference/workflow-sveltekit/index.mdx +18 -0
  119. package/docs/api-reference/workflow-sveltekit/meta.json +4 -0
  120. package/docs/api-reference/workflow-sveltekit/workflow-plugin.mdx +42 -0
  121. package/docs/api-reference/workflow-vite/index.mdx +18 -0
  122. package/docs/api-reference/workflow-vite/meta.json +4 -0
  123. package/docs/api-reference/workflow-vite/workflow.mdx +48 -0
  124. package/docs/changelog/attributes-mvp.mdx +53 -41
  125. package/docs/changelog/batched-event-writes.mdx +79 -0
  126. package/docs/changelog/eager-processing.mdx +110 -436
  127. package/docs/changelog/index.mdx +4 -2
  128. package/docs/changelog/lazy-event-creation.md +127 -0
  129. package/docs/changelog/lazy-hook-resume.mdx +78 -0
  130. package/docs/changelog/meta.json +11 -1
  131. package/docs/changelog/resilient-resume.mdx +32 -0
  132. package/docs/changelog/resilient-start.mdx +33 -285
  133. package/docs/changelog/step-message-ownership.mdx +360 -0
  134. package/docs/changelog/turbo-mode.md +87 -0
  135. package/docs/comparisons/index.mdx +66 -0
  136. package/docs/comparisons/meta.json +11 -0
  137. package/docs/comparisons/workflow-sdk-vs-aws-agentcore.mdx +55 -0
  138. package/docs/comparisons/workflow-sdk-vs-aws-step-functions.mdx +111 -0
  139. package/docs/comparisons/workflow-sdk-vs-cloudflare-workflows.mdx +71 -0
  140. package/docs/comparisons/workflow-sdk-vs-inngest.mdx +102 -0
  141. package/docs/comparisons/workflow-sdk-vs-temporal.mdx +123 -0
  142. package/docs/comparisons/workflow-sdk-vs-trigger-dev.mdx +104 -0
  143. package/docs/configuration/build-and-diagnostics.mdx +79 -0
  144. package/docs/configuration/cli-and-web-ui.mdx +241 -0
  145. package/docs/configuration/framework-options.mdx +165 -0
  146. package/docs/configuration/index.mdx +32 -0
  147. package/docs/configuration/meta.json +12 -0
  148. package/docs/configuration/runtime-tuning.mdx +399 -0
  149. package/docs/configuration/worlds.mdx +315 -0
  150. package/docs/cookbook/advanced/child-workflows.mdx +33 -25
  151. package/docs/cookbook/advanced/publishing-libraries.mdx +65 -56
  152. package/docs/cookbook/advanced/serializable-steps.mdx +48 -68
  153. package/docs/cookbook/advanced/upgrading-workflows.mdx +35 -31
  154. package/docs/cookbook/agent-patterns/agent-cancellation.mdx +78 -60
  155. package/docs/cookbook/agent-patterns/durable-agent.mdx +23 -135
  156. package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +180 -195
  157. package/docs/cookbook/common-patterns/batching.mdx +20 -14
  158. package/docs/cookbook/common-patterns/idempotency.mdx +41 -53
  159. package/docs/cookbook/common-patterns/rate-limiting.mdx +8 -4
  160. package/docs/cookbook/common-patterns/saga.mdx +23 -19
  161. package/docs/cookbook/common-patterns/scheduling.mdx +30 -22
  162. package/docs/cookbook/common-patterns/sequential-and-parallel.mdx +29 -25
  163. package/docs/cookbook/common-patterns/timeouts.mdx +26 -21
  164. package/docs/cookbook/common-patterns/webhooks.mdx +10 -6
  165. package/docs/cookbook/common-patterns/workflow-composition.mdx +27 -17
  166. package/docs/cookbook/index.mdx +22 -22
  167. package/docs/cookbook/integrations/ai-sdk.mdx +63 -48
  168. package/docs/cookbook/integrations/chat-sdk.mdx +46 -33
  169. package/docs/cookbook/integrations/sandbox.mdx +58 -45
  170. package/docs/deploying.mdx +106 -0
  171. package/docs/errors/abort-signal-timeout-in-workflow.mdx +16 -12
  172. package/docs/errors/corrupted-event-log.mdx +39 -18
  173. package/docs/errors/deployment-mismatch.mdx +71 -0
  174. package/docs/errors/fetch-in-workflow.mdx +15 -14
  175. package/docs/errors/hook-conflict.mdx +38 -11
  176. package/docs/errors/hook-force-claimed.mdx +96 -0
  177. package/docs/errors/index.mdx +24 -37
  178. package/docs/errors/node-js-module-in-workflow.mdx +9 -5
  179. package/docs/errors/replay-divergence.mdx +27 -0
  180. package/docs/errors/run-expired.mdx +85 -0
  181. package/docs/errors/runtime-decryption-failed.mdx +77 -0
  182. package/docs/errors/serialization-failed.mdx +44 -12
  183. package/docs/errors/start-invalid-workflow-function.mdx +9 -5
  184. package/docs/errors/step-executed-multiple-times.mdx +23 -0
  185. package/docs/errors/step-not-registered.mdx +6 -6
  186. package/docs/errors/timeout-in-workflow.mdx +12 -8
  187. package/docs/errors/webhook-invalid-respond-with-value.mdx +18 -18
  188. package/docs/errors/webhook-response-not-sent.mdx +20 -16
  189. package/docs/errors/workflow-not-registered.mdx +5 -5
  190. package/docs/foundations/cancellation.mdx +31 -32
  191. package/docs/foundations/errors-and-retries.mdx +54 -11
  192. package/docs/foundations/hooks.mdx +98 -35
  193. package/docs/foundations/idempotency.mdx +267 -12
  194. package/docs/foundations/index.mdx +1 -26
  195. package/docs/foundations/serialization.mdx +22 -22
  196. package/docs/foundations/starting-workflows.mdx +104 -30
  197. package/docs/foundations/streaming.mdx +108 -60
  198. package/docs/foundations/versioning.mdx +4 -4
  199. package/docs/foundations/workflows-and-steps.mdx +9 -9
  200. package/docs/getting-started/astro.mdx +22 -18
  201. package/docs/getting-started/express.mdx +15 -11
  202. package/docs/getting-started/fastify.mdx +15 -11
  203. package/docs/getting-started/hono.mdx +15 -11
  204. package/docs/getting-started/index.mdx +10 -3
  205. package/docs/getting-started/meta.json +3 -1
  206. package/docs/getting-started/nestjs.mdx +264 -21
  207. package/docs/getting-started/next.mdx +18 -14
  208. package/docs/getting-started/nitro.mdx +22 -18
  209. package/docs/getting-started/nuxt.mdx +15 -11
  210. package/docs/getting-started/python.mdx +190 -41
  211. package/docs/getting-started/react-router/index.mdx +33 -0
  212. package/docs/getting-started/react-router/meta.json +5 -0
  213. package/docs/getting-started/react-router/v7.mdx +237 -0
  214. package/docs/getting-started/react-router/v8.mdx +232 -0
  215. package/docs/getting-started/sveltekit.mdx +20 -16
  216. package/docs/getting-started/tanstack-start.mdx +17 -13
  217. package/docs/getting-started/vite.mdx +15 -11
  218. package/docs/how-it-works/cancellation.mdx +63 -63
  219. package/docs/how-it-works/code-transform.mdx +82 -66
  220. package/docs/how-it-works/encryption.mdx +30 -26
  221. package/docs/how-it-works/event-sourcing.mdx +132 -35
  222. package/docs/how-it-works/framework-integrations.mdx +96 -337
  223. package/docs/how-it-works/understanding-directives.mdx +22 -22
  224. package/docs/internal/index.mdx +6 -4
  225. package/docs/internal/meta.json +6 -1
  226. package/docs/internal/nitro-native-build.mdx +38 -0
  227. package/docs/internal/nitro-web-ui.mdx +24 -0
  228. package/docs/internal/serializable-abort-controller.mdx +7 -7
  229. package/docs/meta.json +4 -2
  230. package/docs/observability/attributes.mdx +91 -21
  231. package/docs/observability/index.mdx +29 -15
  232. package/docs/observability/lifecycle-hooks.mdx +95 -0
  233. package/docs/observability/meta.json +1 -1
  234. package/docs/observability/retention.mdx +95 -0
  235. package/docs/observability/tracing.mdx +124 -0
  236. package/docs/testing/index.mdx +120 -38
  237. package/docs/testing/server-based.mdx +10 -10
  238. package/docs/whats-new.mdx +190 -0
  239. package/docs/worlds/building-a-world.mdx +538 -0
  240. package/docs/worlds/local.mdx +129 -0
  241. package/docs/worlds/meta.json +10 -0
  242. package/docs/worlds/postgres.mdx +424 -0
  243. package/docs/worlds/upgrading-to-v5.mdx +162 -0
  244. package/docs/worlds/vercel.mdx +345 -0
  245. package/package.json +17 -14
  246. package/docs/api-reference/workflow/experimental-set-attributes.mdx +0 -63
  247. package/docs/api-reference/workflow-api/world/index.mdx +0 -58
  248. package/docs/api-reference/workflow-api/world/meta.json +0 -4
  249. package/docs/api-reference/workflow-api/world/observability.mdx +0 -164
  250. package/docs/api-reference/workflow-api/world/queue.mdx +0 -86
  251. package/docs/deploying/building-a-world.mdx +0 -251
  252. package/docs/deploying/index.mdx +0 -95
  253. package/docs/deploying/meta.json +0 -4
  254. package/docs/deploying/world/local-world.mdx +0 -84
  255. package/docs/deploying/world/meta.json +0 -4
  256. package/docs/deploying/world/postgres-world.mdx +0 -224
  257. package/docs/deploying/world/vercel-world.mdx +0 -181
  258. package/docs/migration-guides/index.mdx +0 -34
  259. package/docs/migration-guides/meta.json +0 -9
  260. package/docs/migration-guides/migrating-from-aws-step-functions.mdx +0 -358
  261. package/docs/migration-guides/migrating-from-inngest.mdx +0 -304
  262. package/docs/migration-guides/migrating-from-temporal.mdx +0 -313
  263. package/docs/migration-guides/migrating-from-trigger-dev.mdx +0 -328
@@ -0,0 +1,162 @@
1
+ ---
2
+ title: Upgrading a World to v5
3
+ description: Port a custom World implementation from the v4 spec to v5, including the interface delta, the contract changes, and the new event ID allocation rules.
4
+ type: guide
5
+ summary: What changed in the World spec between v4 and v5, and how to update a custom World.
6
+ prerequisites:
7
+ - /worlds/building-a-world
8
+ related:
9
+ - /docs/whats-new
10
+ - /worlds/building-a-world
11
+ - /worlds/postgres
12
+ - /worlds/vercel
13
+ ---
14
+
15
+ This page is for people who implement the `World` interface themselves, or who maintain a build integration that compiles workflow files. It covers what changed between the v4 and v5 spec, which of those changes break an existing implementation, and what new surface is worth adopting.
16
+
17
+ Three things are required to be a v5 World: the [interface changes](#interface-changes), the [contract changes](#contract-changes), and [event ID allocation](#event-id-allocation). The last one is the largest piece of work and the only one that is not visible from the type signatures. Everything under [new optional surface](#new-optional-surface) can wait.
18
+
19
+ If your application runs on the [Vercel](/worlds/vercel), [Local](/worlds/local), or [Postgres](/worlds/postgres) World, you need nothing from this page. Those implementations ship with the SDK and are already on the v5 spec. For the application-facing changes, see [What's new in v5](/docs/whats-new).
20
+
21
+ The fastest way to start is to install the World migration skill and hand the job to an agent:
22
+
23
+ ```bash
24
+ npx skills add https://github.com/vercel/workflow --skill migrating-world-v4-to-v5
25
+ ```
26
+
27
+ <CopyPrompt text="Upgrade this custom Workflow SDK World from the v4 spec to v5 using the migrating-world-v4-to-v5 skill. Start with event ID allocation, which is required and is not visible from the type signatures, then apply the interface and contract changes. Wire up the @workflow/world-testing conformance suite and report its output, along with anything the skill flags as a decision rather than an edit." />
28
+
29
+ This is a different skill from `migrating-workflow-v4-to-v5`, which upgrades the application code. If the same repository does both, run the application one first.
30
+
31
+ If you would rather work from the diff directly:
32
+
33
+ <CopyPrompt text="My app uses a custom Workflow SDK World. Help me upgrade it from the v4 World spec to v5. Clone https://github.com/vercel/workflow and, on the main branch, study packages/world (the World interface and types) plus the first-party implementations in packages/world-vercel and packages/world-postgres. Run git diff stable...workflow@<the 5.x version I am upgrading to> -- packages/world packages/world-vercel packages/world-postgres (release tags are named like workflow@5.0.0; fall back to main if the tag does not exist) to see exactly what changed since the 4.x line, and use those diffs to identify the spec and implementation changes. Then port the same changes into my custom World and verify it by running a workflow end to end against it. Pay particular attention to event id allocation, which is required and is not visible from the type signatures: a v5 World assigns each event a dense, 1-based slot within its run, must settle races on a slot in the store rather than in process, and must never reject a create for a taken slot. It advances to the next free slot and returns the events it skipped." />
34
+
35
+ We're working on bringing back World compatibility tests and reporting on the [Worlds page](/worlds), to make it easier to see which Workflow versions each World is compatible with.
36
+
37
+ ## Spec versions
38
+
39
+ A World declares the protocol version it speaks on `specVersion`, and that number is stamped on every run it creates. Declare `SPEC_VERSION_CURRENT` from `@workflow/world`, not a literal:
40
+
41
+ {/* @skip-typecheck - partial World, the other members are elided */}
42
+ ```typescript
43
+ import { SPEC_VERSION_CURRENT } from '@workflow/world';
44
+
45
+ export function createWorld(): World {
46
+ return {
47
+ specVersion: SPEC_VERSION_CURRENT,
48
+ // ...
49
+ };
50
+ }
51
+ ```
52
+
53
+ In v4 the runtime required that number to equal its own current version exactly. In v5 it checks the declaration against a range, `[SPEC_VERSION_CURRENT, SPEC_VERSION_MAX_SUPPORTED]`, before it creates or replays anything, and refuses a World outside it with an error naming both the range and what your World declared.
54
+
55
+ The two bounds are the same version today, so exactly one is accepted. That is a consequence of [event ID allocation](#event-id-allocation) being a requirement rather than an option: a World declaring anything lower allocates IDs the runtime cannot read positions out of, and admitting it would only move the failure from startup into the middle of a run. The check is written as a range because the constants answer different questions and come apart while a version bump is staged. The ceiling rises when the runtime learns to read the next version, and the floor rises when that version becomes the one Worlds stamp.
56
+
57
+ Using the constant is what keeps the check passing across upgrades. It moves with the `@workflow/world` version your package resolves, so a bump raises your declaration and the runtime's floor together, while a hard-coded number leaves your World a version behind the next bump and gets it rejected by the runtime it ships alongside. This is worth re-checking if you followed earlier guidance: `SPEC_VERSION_SUPPORTS_SLOT_IDENTITY` names the version that introduced slot-numbered IDs and is equal to `SPEC_VERSION_CURRENT` today, but declaring it pins you to a literal by another name. `@workflow/world-vercel` declared it and now declares the current version instead. Keep `@workflow/world` in the same release channel as the `workflow` version your users install.
58
+
59
+ Runs carry a spec version too, and a run keeps the version it was created under for its whole life. Read the stamped version off the run rather than assuming every run matches what your World declares today. Bumping the constant does not reach runs already in your store: their version is persisted, every version test in the runtime is a lower bound, and a run's event ID scheme is resolved from what is stored.
60
+
61
+ ## Interface changes
62
+
63
+ These break an existing v4 implementation. Each one is a signature or module-shape change your World has to follow.
64
+
65
+ | Change | What to do |
66
+ | --- | --- |
67
+ | `getWorld()` and `createWorld()` are async | `await getWorld()`. This already worked in 4.x, so it is safe to write before upgrading. See [`getWorld`](/docs/api-reference/workflow-runtime/get-world). |
68
+ | Stream methods moved to `world.streams.*`, with `runId` first | `writeToStream(name, runId, chunk)` becomes `streams.write(runId, name, chunk)`; likewise `writeToStreamMulti` → `streams.writeMulti`, `closeStream` → `streams.close`, `readFromStream` → `streams.get`, `getStreamChunks` → `streams.getChunks`, `listStreamsByRunId` → `streams.list`. |
69
+ | `world.steps.get()` requires `runId` | The first argument is no longer `string \| undefined`. Pass the run ID that owns the step. |
70
+ | `events.listByCorrelationId()` requires `runId` | A correlation ID identifies a step, hook, or wait within its run, not across runs, so the lookup is scoped to one run. Pass the run that owns the correlation ID. The same applies to `analytics.events.listByCorrelationId()`. A World that paginates by event ID also needs the scope in its cursor comparison because two runs can hold the same correlation ID. |
71
+ | `createLocalWorld()` and `createVercelWorld()` removed | Export a `createWorld()` factory from your package instead, matching the first-party Worlds. The arguments are unchanged. |
72
+ | Worlds are injected into host bundles at build time | Selection is static rather than resolved dynamically at runtime. Verify your World still resolves after the upgrade, and that its module graph survives bundling. |
73
+ | `@workflow/world-local` stream chunks moved | Chunks live at `streams/chunks/<streamName>/`. Files written in the old flat layout are not read back, so local development state from 4.x can be deleted. Only relevant if your World inherited that layout. |
74
+
75
+ ## Contract changes
76
+
77
+ These do not change any signature, so an implementation ported by types alone will compile and then behave incorrectly.
78
+
79
+ **Suspension and dispatch.** The asymmetric `{ timeoutSeconds }` wait-return contract is gone. A wait is now an ordinary queue continuation with `delaySeconds`, and a suspension dispatches its waits and its steps as one parallel batch. A queue that assumed one message per suspension needs to handle the batch.
80
+
81
+ **Step queue topics are retired.** The `'step'` queue kind no longer exists. Queued steps travel on the workflow topic, carrying `stepId` and `stepName` in the payload, and execute in the combined flow handler. A World that provisioned separate `__wkf_step_*` topics can drop them.
82
+
83
+ **Capabilities fail closed.** The optional `capabilities` object advertises behavior the runtime otherwise assumes is absent. An unadvertised capability costs performance, never correctness, so a partial World stays correct while it catches up. The reverse is not true: advertising something you do not enforce removes a guard the runtime was relying on. Only set a flag once the behavior is implemented.
84
+
85
+ **A stale replay no longer has to be refused.** v5 shipped with a `preconditionGuard` capability for a World that rejected an event creation whose snapshot was behind the log. It is gone, and nothing replaced it: allocating positions at the commit means a reader's log is a prefix rather than a prefix with a hole, replay is deterministic on a prefix, and a write reports the events it was pushed past. As a result, a stale replay costs a merge instead of a rejection. If you implemented the guard, you can delete it. `PreconditionFailedError` and the runtime's handling of it remain for a World that allocates positions away from the commit (see [Event ID allocation](#event-id-allocation)); no World in the SDK throws it.
86
+
87
+ **Replay reads the event log with `resolveData: 'skip-step-inputs'`.** A World may leave `input` out of `step_created` and `step_started` events for this value, and must otherwise treat it as `'all'`. A World that tests `resolveData === 'all'`, or validates against `['none', 'all']`, strips step results or rejects the read, and every replay fails. Test for `'none'` instead, or map with `entityResolveData()` from `@workflow/world`. `@workflow/world-testing` covers this case.
88
+
89
+ **Event creation can return a delta.** `events.create()` may return events alongside the one it created, in `events` with a matching `cursor` and `hasMore`. The runtime uses this to skip a follow-up `events.list` round trip on `run_started`, on step-terminal writes that carried a `sinceCursor`, and on `hook_received` writes that carried `preloadEvents`. All three are advisory: a World that returns only the created event stays correct and pays one more round trip.
90
+
91
+ ## Event ID allocation
92
+
93
+ This is the largest change for a World implementation, and it is required.
94
+
95
+ In v4 an event ID was a ULID your World minted however it liked. In v5 an event ID is its **slot**: `evnt_` followed by the event's 1-based position in that run's log, zero-padded to 26 characters, so a run's first event is `evnt_00000000000000000000000001`. Format one with `slotToEventId()` from `@workflow/world`.
96
+
97
+ There is no capability to declare and no fallback path. The runtime reads a position out of every ID it loads and fails the run when it cannot. A World whose IDs are not positions will pass a type check and start runs, but it cannot replay a single workflow. The first replay fails with `Event id is not slot-numbered`.
98
+
99
+ The scheme exists for what a reader can conclude from a log it just fetched: positions are dense, so a truncated log is distinguishable from a complete one by its length alone. The runtime relies on that, and it fails a run with [`CORRUPTED_EVENT_LOG`](/docs/errors/corrupted-event-log) rather than replay across a hole, so four rules bind an implementation:
100
+
101
+ - **Uniqueness.** Settle a race for a position where the store settles it, with a unique constraint on `(runId, eventId)` or a conditional write, not by reading the maximum in your own process and adding one.
102
+ - **Density.** Positions run from 1 with no holes. A writer that loses a race re-derives its position from the store instead of incrementing a local number, which would leave a permanent hole.
103
+ - **Bump and report.** `events.create()` params carry `eventCount`, so the expected position is `eventCount + 1`. When it is taken, do not reject the write: commit at the next free position and return the events you skipped on the success response. A stale count is the normal case for a parallel fan-out, and rejecting it would serialize writes the runtime deliberately issues concurrently.
104
+ - **Allocate at the commit.** Take the position in the same operation that appends the event, not earlier. This is what makes a reader's log a prefix of the run's log rather than a prefix with a hole in it: nothing can land behind a position a reader has already passed. A World that mints a position in a request handler and commits later breaks the property every replay depends on, and is the only kind that still has a use for a stale-write rejection.
105
+
106
+ [Event ID Allocation](/worlds/building-a-world#event-id-allocation) carries the full rules, and [Event IDs](/docs/how-it-works/event-sourcing#event-ids) covers what the format means for anything that reads an ID back.
107
+
108
+ One consequence is specific to an upgrade, and it is the thing to plan around.
109
+
110
+ <Callout type="warn">
111
+ **Runs already in your store cannot be replayed by the new code.** A ULID-numbered run is not readable as positions, and the runtime refuses it rather than guessing, so there is no mixed-scheme mode and no per-run fallback. Drain those runs on your 4.x build before deploying a v5 World, or accept that the ones still in flight will fail. On a platform such as Vercel, where a run executes on the deployment that created it, this resolves itself: those runs finish on the build that started them and never meet the new code. Anywhere a single deployment serves every run, sequencing matters.
112
+ </Callout>
113
+
114
+ ## New optional surface
115
+
116
+ None of this is required. Each entry is a hook the runtime uses if your World provides it, and routes around if it does not.
117
+
118
+ | Member | What it buys |
119
+ | --- | --- |
120
+ | `capabilities` | Advertises `hookRetention.active`, `hookResumeDedup`, `hookForceClaim`, `deploymentAffinity`, `maxConcurrency`, and `dynamicWorkflowCode`. See the contract note above about failing closed. Event ID allocation is *not* in here: it is a requirement, not a capability. |
121
+ | `analytics` | A metadata-only read namespace for observability surfaces. Payload-bearing reads stay on `runs`, `steps`, `events`, and `hooks`. |
122
+ | `runs.experimentalSetAttributes` | Backs `setAttributes()` from application code. Without it, run attributes are unavailable. |
123
+ | `runs.cancelMany` | Bulk cancellation: up to 500 unique run IDs per request (`BULK_CANCEL_MAX_RUN_IDS`), an optional `cancelReason` of at most 512 characters, and a per-run outcome for every ID. Without it, the runtime falls back to bounded-concurrency individual cancels. |
124
+ | `getRuntimeDeadline()` | The absolute time the current invocation will be terminated. The runtime derives its inline replay budget from this, so a host with a long function timeout gets more work per invocation. Without it, the budget is a flat two minutes. See [`WORKFLOW_V2_TIMEOUT_MS`](/docs/configuration/runtime-tuning#workflow_v2_timeout_ms). |
125
+ | `getEnvironment()` | The environment your World's writes are attributed to. Must be synchronous, side-effect free, and match what the backend will actually apply. A wrong answer is worse than `undefined`, because callers use it to detect cross-environment mismatches. Worlds with a single tenant should omit it. |
126
+ | `createRunId(options)` | Mints the bare run ID; the core adds the `wrun_` prefix. Return a valid ULID. You may embed World-specific metadata in it, as `@workflow/world-vercel` does with a region identifier. Read only the option keys you recognize and ignore the rest. |
127
+ | `describeRun(run)` | World-specific display fields for observability surfaces. |
128
+ | `getEncryptionKeyForRun()` | Returns a ready-to-use 32-byte AES-256 key. Without it, data is stored unencrypted. Two overloads: pass the `WorkflowRun` when you have it, or a `runId` plus opaque World context when the entity is not available locally. |
129
+ | `resolveLatestDeploymentId()` | Resolves `deploymentId: 'latest'`. Only meaningful for Worlds where deployment routing exists. |
130
+ | `close()` | Releases connection pools and listeners so CLI commands and short-lived processes can exit without `process.exit()`. |
131
+ | `streams.streamFlushIntervalMs` | Sets the stream flush window. The v5 default is `0`, so the first chunk flushes immediately; set a value to coalesce writes again. |
132
+ | Hook token retention | `Hook.tokenRetentionUntil` marks the earliest time a token may become available after its run ends. Keep the owning run readable at least that long, and honor `hook_disposed` as an immediate release. Declare `capabilities.hookRetention.active` only once this is implemented, since the runtime otherwise rejects retained hooks before registration. |
133
+ | Hook resume dedup | `resumeHook()` writes `hook_received` and dispatches the queue message in parallel when the backend collapses concurrent writes carrying the same `(runId, resumeId)` onto one committed event. Declare `capabilities.hookResumeDedup` only if you enforce that constraint **and** `events.list()` returns the committed event's top-level `resumeId`. A World that omits either guarantee must leave the flag unset, which keeps the sequential path. See [Durable hook resume](/docs/changelog/lazy-hook-resume). |
134
+ | Hook token takeover | Backs [`createHook({ experimental_force: true })`](/docs/api-reference/workflow/create-hook#take-over-a-token-another-run-holds). When a forced `hook_created` names a token another live run holds, append `hook_disposed` with `forceClaimedBy: { runId, hookId }` to the holder's log and refuse later `hook_received` writes to it, then re-point the token to the claimer and record `Hook.claimedFrom`. Refuse the takeover with an ordinary `hook_conflict` carrying `forceRefusedReason: 'victim-spec-version'` when the holder was stamped below spec version 8, and answer a `hook_received` refused by a takeover with `HookForceClaimedError` so `resumeHook()` can follow the token. Declare `capabilities.hookForceClaim` only once you give these guarantees. Without it, the runtime fails a forced `createHook()` at registration. |
135
+
136
+ ## If you also maintain a build integration
137
+
138
+ Compiling workflow files changed independently of the storage contract.
139
+
140
+ | Change | What to do |
141
+ | --- | --- |
142
+ | The `client` SWC transform mode was removed | It merged into `step` mode. Integrations passing `mode: 'client'` pass `mode: 'step'`. |
143
+ | `stepEntrypoint` removed from `workflow/runtime` | Steps execute through the combined workflow handler the framework integrations generate. Custom hosts use `getWorldHandlers()`. |
144
+ | Step, workflow and webhook bundles are ESM | Generated output moved from CJS to ESM, with a `createRequire` banner for CJS dependencies. The VM-executed workflow bundle stays CJS. The CLI's standalone output is renamed to match: `flow.mjs`, `webhook.mjs`, and `__step_registrations.mjs` in place of `flow.js`, `webhook.js`, and `step.js`. Consumers import the namespace rather than a default. |
145
+ | `workflow/internal/private` and `@workflow/core/private` removed | These were never public API. The compiler no longer emits imports from them, so regenerate build output rather than importing them yourself. |
146
+ | Duplicate step or workflow IDs fail the build | 4.x resolved collisions across non-exported workspace files last-write-wins. A build integration that derived IDs from a partial path may now produce build failures. |
147
+
148
+ ## Verifying the upgrade
149
+
150
+ Run a workflow end to end against your World, not just the type checker. The contract changes above compile cleanly and fail at runtime.
151
+
152
+ The cases worth covering explicitly:
153
+
154
+ - A run that suspends on a step and one that suspends on a wait, to exercise the batched dispatch.
155
+ - A parallel fan-out, so concurrent `events.create()` calls race on the same position or the same precondition.
156
+ - A hook resumed after its run has already progressed, and a hook whose token is retained past the end of its run.
157
+ - A stream written and read back, including a stream closed before the reader attaches.
158
+ - A run created under an older spec version, if your World has any, read back by the new code.
159
+
160
+ `@workflow/world-testing` is the shared suite the first-party Worlds run, and it now covers event ID allocation directly: `numbers events by position` fails a World whose IDs do not decode to slots, whose run is not dense from 1, or whose IDs are not in canonical form. A World padding to a different width sorts its own log incorrectly past ten events. Run it against your World before the end-to-end cases above; it turns the failure that would otherwise appear on a first replay into one line of test output.
161
+
162
+ The first-party implementations in `packages/world-local` and `packages/world-postgres` are the reference for everything else, and their test suites are the closest thing to full conformance while the compatibility tests are being rebuilt.
@@ -0,0 +1,345 @@
1
+ ---
2
+ title: Vercel World
3
+ description: Fully managed World for Vercel deployments with automatic storage, queuing, and authentication.
4
+ type: integration
5
+ summary: Deploy workflows to Vercel with fully managed storage, queuing, and authentication.
6
+ prerequisites:
7
+ - /docs/deploying
8
+ related:
9
+ - /docs/how-it-works/encryption
10
+ - /worlds/local
11
+ - /worlds/postgres
12
+ ---
13
+
14
+ The Vercel World is a fully managed workflow backend for applications deployed on Vercel. It provides scalable storage, distributed queuing, and automatic authentication without configuration.
15
+
16
+ When you deploy to Vercel, workflows automatically use the Vercel World without requiring setup.
17
+
18
+ ## Usage
19
+
20
+ Deploy your application to Vercel:
21
+
22
+ ```bash
23
+ vercel deploy
24
+ ```
25
+
26
+ Vercel automatically:
27
+
28
+ - Selects the Vercel World backend
29
+ - Configures authentication using OIDC tokens
30
+ - Provisions storage and queuing infrastructure
31
+ - Isolates data per environment (production, preview, development)
32
+
33
+ <FluidComputeCallout />
34
+
35
+ ## System environment variables
36
+
37
+ Workflow recognizes a Vercel deployment by `VERCEL_DEPLOYMENT_ID`. Vercel exposes that variable, along with the rest of its [system environment variables](https://vercel.com/docs/environment-variables/system-environment-variables), only when the project opts in. Check this before your first deployment:
38
+
39
+ 1. Open your project in the [Vercel dashboard](https://vercel.com/dashboard).
40
+ 2. Go to **Settings**, then **Environment Variables**.
41
+ 3. Select the **Enable access to System Environment Variables** checkbox.
42
+ 4. Redeploy. The setting applies to new deployments; it does not change deployments that already exist.
43
+
44
+ `VERCEL_DEPLOYMENT_ID` is available at both build time and runtime, and Workflow reads it in both. The build uses it to decide whether to emit Vercel Function output, including the queue-triggered flow handler, or local development output. The runtime uses it to [select the World](/docs/configuration/worlds#workflow_target_world).
45
+
46
+ <Callout type="warn">
47
+ With the checkbox cleared, a Vercel deployment is indistinguishable from a
48
+ non-Vercel environment as far as Workflow is concerned: the build emits local
49
+ output and, unless `WORKFLOW_TARGET_WORLD` is set, the runtime selects the
50
+ [Local World](/worlds/local), which stores workflow state on the filesystem.
51
+ A deployment's filesystem is read-only, so with this default selection,
52
+ every run fails before its first step.
53
+ </Callout>
54
+
55
+ ### Diagnosing a missing deployment ID
56
+
57
+ With no `WORKFLOW_TARGET_WORLD` override, nothing in the deployment announces the wrong World, so the failure is easy to mistake for a bug in the SDK:
58
+
59
+ - Runs fail with the Local World reporting that it cannot create its data directory, naming a filesystem code such as `EROFS`.
60
+ - No runs appear in the Vercel dashboard, because none were ever created in the Vercel World.
61
+ - The SDK's "the local (filesystem) world is running inside a Vercel deployment" warning does **not** appear. That check keys off `VERCEL_DEPLOYMENT_ID` as well, so the same missing variable hides it.
62
+
63
+ To confirm, log `process.env.VERCEL_DEPLOYMENT_ID` from a deployed function. If it is undefined (as is `VERCEL`), the setting is off.
64
+
65
+ Setting `WORKFLOW_TARGET_WORLD=vercel` is not a workaround. The Vercel World needs the deployment ID itself, to address queue messages to the deployment that created each run and to derive that deployment's encryption keys, so it fails with an error naming the missing `VERCEL_DEPLOYMENT_ID`. Enable the checkbox and redeploy.
66
+
67
+ ## Vercel platform documentation
68
+
69
+ For complete details on pricing, usage limits, and included allotments on Vercel, see the official Vercel documentation:
70
+
71
+ - **[Vercel Workflow](https://vercel.com/docs/workflows)**: Pricing details, concepts, and observability for Workflow on Vercel
72
+ - **[Vercel limits](https://vercel.com/docs/limits)**: Platform-wide limits, including Workflow-specific constraints
73
+ - **[Vercel Hobby plan](https://vercel.com/docs/plans/hobby)**: Free-tier included usage for Workflow and other resources
74
+
75
+ For self-hosted deployments, use the [Postgres World](/worlds/postgres). For local development, use the [Local World](/worlds/local).
76
+
77
+ ## Per-run limits
78
+
79
+ The Vercel World caps how large a single run can get. It caps the run's [event log](/docs/how-it-works/event-sourcing), and separately caps how many steps one run may create; see [Workflow run limits](https://vercel.com/docs/workflows/pricing#workflow-run-limits) for the current values. A run that exhausts its event budget fails with `MAX_EVENTS_EXCEEDED` and cannot be continued.
80
+
81
+ Treat both caps as backstops, not budgets. Replay cost grows with the log, so a run slows down well before it fails. We recommend splitting a run into [child workflows](/cookbook/advanced/child-workflows) once a run would grow past a few thousand events. Size for that recommendation rather than for the cap.
82
+
83
+ Vercel raises the per-run event and step limits on request, so an unusually large run that genuinely cannot be split is worth raising with support rather than working around.
84
+
85
+ ## Multi-region
86
+
87
+ The Vercel World runs in every [Vercel Function region](https://vercel.com/docs/regions). Each workflow run is pinned to a single region at creation time. Its stored state, queue dispatch, and streams are all served from that region without cross-region round trips on the hot path. When your application is deployed in the run's region, as described below, step execution is also region-local.
88
+
89
+ <Callout type="info">
90
+ Multi-region requires `workflow` version **5.0.0** or later. The 4.x
91
+ release line does not support region pinning. Runs created by 4.x always
92
+ live in `iad1`.
93
+ </Callout>
94
+
95
+ ### Automatic region pinning
96
+
97
+ No configuration is needed. A run is pinned to the region of the function that creates it:
98
+
99
+ - Deploy your app to a single region (via [`regions`](https://vercel.com/docs/project-configuration/vercel-json#regions) in `vercel.json` or the project settings), and every run lives there.
100
+ - Deploy to multiple regions for a globally distributed audience, and each run is pinned to the region that served the user who triggered it. Workflow data and streaming stay close to that user.
101
+
102
+ ### Explicit region selection
103
+
104
+ To pin a specific run somewhere else, pass the `region` option to [`start()`](/docs/api-reference/workflow-api/start):
105
+
106
+ ```typescript
107
+ import { start } from "workflow/api";
108
+ import { myWorkflow } from "@/workflows/my-workflow";
109
+
110
+ const run = await start(myWorkflow, [input], { region: "sfo1" });
111
+ ```
112
+
113
+ <Callout type="warn">
114
+ The `region` option controls where the run's **data is stored** and where
115
+ its **queue messages are dispatched from**. It does not deploy your code
116
+ there. Your workflow and step functions execute in the regions your
117
+ application is deployed to. For execution to actually happen in the
118
+ specified region, your app must be deployed there through
119
+ [`regions`](https://vercel.com/docs/project-configuration/vercel-json#regions) in
120
+ `vercel.json` or the Function Regions setting in your project settings.
121
+ If it isn't, the run's data lives in the requested region but its steps
122
+ execute in the nearest region your app is deployed to.
123
+ </Callout>
124
+
125
+ ### Good to know
126
+
127
+ - Reads, hook resumes, and stream consumers can come from anywhere. The platform routes them to the run's region automatically.
128
+ - Runs created by 4.x SDKs, including runs that existed before you upgraded, live in `iad1` and are unaffected by an upgrade. There is no migration.
129
+ - **Hook tokens are currently stored in `iad1`** for every run, regardless of the run's region. The token-to-run mapping that powers [`getHookByToken()`](/docs/api-reference/workflow-api/get-hook-by-token) and [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook) lives there so tokens without region information can always be resolved. Hook *payloads* are not affected. A received payload is recorded on the run's event log, which lives in the run's region like all other run data. This token placement may become a project-level setting in the future.
130
+
131
+ ## Limitations
132
+
133
+ - **No run migration**: A run's region is fixed at creation. Existing runs cannot be moved to a different region.
134
+ - **Hook minimum retention**: [`experimental_minRetention`](/docs/api-reference/workflow/create-hook#keep-a-token-unavailable-after-the-run-ends) cannot exceed 30 days.
135
+
136
+ ## Observability
137
+
138
+ Workflow observability is built into the Vercel dashboard on your project page. It respects your existing authentication and project permission settings.
139
+
140
+ The Vercel World implements the optional [`world.analytics`](/docs/api-reference/workflow-runtime/world/analytics) interface, backed by the same observability data pipeline as the dashboard. Two behaviors are specific to this implementation. Listings scan faster when bounded with a `startTime`/`endTime` window, and your plan's observability lookback caps the queryable window. Requesting an older window fails with `observability-upgrade-required`, and responses include `pageInfo` with the current and maximum lookback so tools can size date ranges.
141
+
142
+ The `workflow` CLI commands open a browser window deeplinked to the Vercel dashboard:
143
+
144
+ ```bash
145
+ # List workflow runs (opens Vercel dashboard)
146
+ npx workflow inspect runs --backend vercel
147
+
148
+ # Launch the web UI (opens Vercel dashboard)
149
+ npx workflow web --backend vercel
150
+ ```
151
+
152
+ The CLI automatically retrieves authentication from the Vercel CLI (`vercel login`) and infers project/team IDs from your local Vercel project linking.
153
+
154
+ To use the local observability UI instead of the Vercel dashboard:
155
+
156
+ ```bash
157
+ npx workflow web --backend vercel --localUi
158
+ ```
159
+
160
+ To override the automatic configuration:
161
+
162
+ ```bash
163
+ npx workflow inspect runs \
164
+ --backend vercel \
165
+ --env production \
166
+ --project my-project \
167
+ --team my-team \
168
+ --authToken <your-token>
169
+ ```
170
+
171
+ Learn more in the [Observability](/docs/observability) documentation.
172
+
173
+ ## Testing & compatibility
174
+
175
+ <WorldTestingPerformance worldId="vercel" />
176
+
177
+ ## Configuration
178
+
179
+ In a Vercel deployment, you do not configure the Vercel World yourself. The platform injects everything the runtime needs, including `VERCEL_DEPLOYMENT_ID`, `VERCEL_PROJECT_ID`, per-request OIDC tokens, and `VERCEL_DEPLOYMENT_KEY` for encryption.
180
+
181
+ Do not set those platform-provided values yourself. You do have to leave [system environment variables](#system-environment-variables) enabled for the project, though: that setting is what makes `VERCEL_DEPLOYMENT_ID` available in the first place.
182
+
183
+ Most users never need to set the `WORKFLOW_VERCEL_*` variables below. They are only overrides for tools running outside Vercel, such as your laptop or CI, when those tools need to inspect or test a remote Vercel Workflow project and cannot infer the project, team, token, or target environment automatically.
184
+
185
+ For example, you might set them when running `workflow inspect runs --backend vercel` from CI without an interactive `vercel login`, or when running tests against a specific preview deployment. In normal local development, the CLI infers these values from your `.vercel` directory and Vercel CLI login. In a deployed Vercel function, these variables have no effect on runtime configuration, and the runtime warns if they are set there.
186
+
187
+ ### `WORKFLOW_VERCEL_ENV`
188
+
189
+ The Vercel environment to target. Options: `production`, `preview`. Default: `production`.
190
+
191
+ ### `WORKFLOW_VERCEL_AUTH_TOKEN`
192
+
193
+ Vercel API authentication token. Keep this secret in your environment, not in code. Falls back to `VERCEL_TOKEN`, then to your Vercel CLI login.
194
+
195
+ ### `WORKFLOW_VERCEL_PROJECT`
196
+
197
+ Vercel project ID (`prj_...`).
198
+
199
+ ### `WORKFLOW_VERCEL_PROJECT_NAME`
200
+
201
+ Vercel project name/slug, used for dashboard links.
202
+
203
+ ### `WORKFLOW_VERCEL_TEAM`
204
+
205
+ Vercel team ID.
206
+
207
+ ### `WORKFLOW_VERCEL_BACKEND_URL`
208
+
209
+ Custom base URL for the Vercel workflow API proxy. Default: `https://api.vercel.com/v1/workflow`.
210
+
211
+ ### `VERCEL_WORKFLOW_SERVER_URL`
212
+
213
+ Custom workflow-server URL for direct runtime requests, or for the proxy to forward to via `WORKFLOW_VERCEL_BACKEND_URL`. Default: unset; normal deployments should not need this.
214
+
215
+ ### `WORKFLOW_SEQUENTIAL_REPLAYS`
216
+
217
+ Set `WORKFLOW_SEQUENTIAL_REPLAYS=1` to guarantee that **at most one orchestrator (flow) invocation runs at a time per workflow run**. This behavior is off by default; without it, the runtime relies on idempotency and the event log to tolerate concurrent flow invocations of the same run.
218
+
219
+ When enabled, each run's orchestrator messages are given their own queue topic and the flow trigger is configured with `maxConcurrency: 1`, so [Vercel Queues](https://vercel.com/docs/queues) processes replays for a given run strictly one at a time. Step executions (which ride the flow topic in the combined handler model) get a per-step topic, so steps keep full parallelism.
220
+
221
+ <Callout type="warn">
222
+ This variable is read at **both build time and runtime**, so it must be set as a project-level environment variable that applies to your build and your deployed functions. Setting it for only one will produce an inconsistent configuration. The same applies to framework integrations that write their own queue trigger configuration instead of using `getWorkflowQueueTrigger()` from `@workflow/builders`: they only get the runtime half (per-run topics) unless they also emit `maxConcurrency: 1` on their flow trigger.
223
+ </Callout>
224
+
225
+ Because it routes each run's flow invocations through a dedicated `maxConcurrency: 1` queue topic, enabling this might lead to higher queue performance overhead.
226
+
227
+ ### `VERCEL_QUEUE_MAX_DELAY_SECONDS`
228
+
229
+ Maximum delay, in seconds, that Workflow uses for one Vercel Queues continuation message when implementing `sleep()`. If a workflow sleeps longer than this, the runtime schedules another continuation message when the first one fires, repeating until the sleep's target time is reached. Default: `82800` (23 hours).
230
+
231
+ Vercel Queues can delay messages for up to 7 days, capped by the message TTL. Because the default TTL is 24 hours, Workflow uses a 23-hour continuation hop to stay safely inside that default.
232
+
233
+ ### `WORKFLOW_REQUEST_TIMEOUT_MS`
234
+
235
+ Per-request timeout, in milliseconds, for Vercel World HTTP calls to workflow-server. Default: `60000`. Clamped to `10000`-`120000`; a value outside that range is pulled into it and warns once.
236
+
237
+ Below the floor the deadline starts canceling requests that would have succeeded, since a cold route or a large event page can take seconds, and a canceled request is redriven through the queue rather than making progress. The ceiling matches the longest a backend route holds a response. Requests that legitimately outlast it (stream reads and writes, event batches) opt out of the deadline entirely rather than relying on this value.
238
+
239
+ ### `WORKFLOW_MAX_CHUNKS_PER_REQUEST`
240
+
241
+ Maximum stream chunks written in one Vercel World request. Larger batches are split across multiple requests. Default: `1000`. Minimum: `1`.
242
+
243
+ ### `WORKFLOW_STREAMS_TRANSPORT`
244
+
245
+ Experimental stream-write transport capability. Default: `http`. Set `WORKFLOW_STREAMS_TRANSPORT=ws` to attempt `workflow-stream-ws/v1`. The server authoritatively accepts or declines every upgrade; a decline goes directly to HTTP for that writer lifetime. Reads remain HTTP and demand-driven.
246
+
247
+ This setting does not own tenant rollout policy and does not infer server support from package versions. HTTP remains the compatibility path. The protocol route (`/websockets/v1`) is versioned independently from REST v2/v4 and persisted workflow `specVersion` values.
248
+
249
+ Writes and close requests execute serially. The first operation waits up to 250 ms for an opted-in socket to open; this is an implementation-level measurement knob, not protocol semantics. If the budget expires, or the upgrade fails or is declined before acceptance, the writer uses HTTP without racing the same operation over both transports. After the socket accepts a write, a missing acknowledgement has an unknown outcome: the writer fails rather than replaying the write over HTTP and risking a duplicate append.
250
+
251
+ Before routine authentication expiry or server max duration, the server sends a v1 `drain` control. The client stops sending, lets an already-admitted request receive its reply, and reconnects after the server closes with code 1001. Authentication drains re-resolve a fresh bearer. A request that was sent but receives no reply before close still has an unknown outcome and is never replayed.
252
+
253
+ ### `WORKFLOW_EVENTS_TRANSPORT`
254
+
255
+ Opt-in WebSocket transport for workflow run events, which ships them to the Vercel World over one socket per run instead of one HTTP request each. Default: `http`.
256
+
257
+ Set `WORKFLOW_EVENTS_TRANSPORT=ws` to opt in. Only that value (case-insensitive) enables the WebSocket — any other value, including unset, empty, or `http`, keeps HTTP — so a typo fails toward the default rather than enabling a transport nobody asked for.
258
+
259
+ The setting is ignored when the World is configured with `projectConfig` and therefore routes through the `api-workflow` proxy: that endpoint is an HTTP-only REST gateway and does not forward a WebSocket upgrade, so events stay on HTTP. The fallback is silent, because the variable is typically set deployment-wide and a `projectConfig` World (such as the CLI) cannot act on it, and is reported once per process under `DEBUG=workflow:*`. `workflow.events.transport` on the per-write span records which transport actually carried a run.
260
+
261
+ Tracing is unaffected by the choice. Each event write emits an `http POST` client span against the same `url.full`, regardless of which transport carries it. On the WebSocket path, the span is synthesized around the frame because no HTTP request is made. Attributes distinguish the transports:
262
+
263
+ | Attribute | HTTP | WebSocket |
264
+ | --- | --- | --- |
265
+ | `workflow.events.transport` | `http` | `ws` |
266
+ | `workflow.event.type` | the event type, e.g. `step_started` | same |
267
+ | `network.protocol.name` | — | `websocket` |
268
+ | `workflow.events.ws.url` | — | the socket the frame went over |
269
+ | `workflow.events.ws.req_id` | — | per-connection request id, matching the server's log line |
270
+
271
+ The WebSocket handshake is itself a span, `workflow.events.ws.connect`, so the cost of opening (or eagerly reopening) a connection is attributable rather than showing up as unexplained time inside the first write.
272
+
273
+ ### Programmatic configuration
274
+
275
+ `createWorld()` accepts explicit API configuration. It does not read `WORKFLOW_VERCEL_*` automatically, so pass the environment values yourself when you want a configured World module:
276
+
277
+ {/*@skip-typecheck: incomplete code sample*/}
278
+
279
+ ```typescript title="my-world.ts" lineNumbers
280
+ import { createWorld } from "@workflow/world-vercel";
281
+
282
+ export default createWorld({
283
+ token: process.env.WORKFLOW_VERCEL_AUTH_TOKEN,
284
+ projectConfig: {
285
+ projectId: "prj_...",
286
+ teamId: "team_...",
287
+ environment: "production",
288
+ },
289
+ });
290
+ ```
291
+
292
+ ```bash title=".env"
293
+ WORKFLOW_TARGET_WORLD="./my-world.ts"
294
+ ```
295
+
296
+ ## Versioning
297
+
298
+ On Vercel, workflow runs are pegged to the deployment that started them. This means:
299
+
300
+ - Existing workflow runs continue executing on their original deployment, even as new code is deployed
301
+ - New workflow runs start on the latest deployment
302
+ - Code changes won't break in-flight workflows
303
+
304
+ This ensures long-running workflows complete reliably without being affected by subsequent deployments.
305
+
306
+ For the full model, including rerunning on latest and explicit upgrade boundaries, see [Versioning](/docs/foundations/versioning).
307
+
308
+ ## Security
309
+
310
+ ### Consumer function security
311
+
312
+ Workflow handler functions on Vercel are not accessible through public endpoints. During the build step, the Workflow SDK registers the combined flow handler as only reachable by [Vercel Queue](https://vercel.com/docs/queues), using the `experimentalTriggers` configuration in `.vc-config.json`:
313
+
314
+ ```json title=".vc-config.json (flow handler)"
315
+ {
316
+ "experimentalTriggers": [
317
+ {
318
+ "type": "queue/v2beta",
319
+ "topic": "__wkf_workflow_*",
320
+ "consumer": "default",
321
+ }
322
+ ]
323
+ }
324
+ ```
325
+
326
+ Practically, this means:
327
+
328
+ - You don't need to add authentication or authorization logic to workflow handlers
329
+ - Unauthorized requests can never reach workflow or step execution
330
+ - Only messages delivered through Vercel Queues can trigger execution
331
+ - Handlers receive only a message ID that must be retrieved from Vercel's backend, making it impossible to craft custom payloads
332
+
333
+ <Callout>
334
+ The Workflow SDK build step manages this configuration. If you are writing a custom integration, see [Framework integrations: Security](/docs/how-it-works/framework-integrations#security) for more details.
335
+ </Callout>
336
+
337
+ ## How it works
338
+
339
+ The Vercel World uses Vercel's infrastructure for workflow execution:
340
+
341
+ - **Storage**: Workflow data is stored in Vercel's cloud with automatic replication and [end-to-end encryption](/docs/how-it-works/encryption)
342
+ - **Queuing**: Steps are distributed across Vercel Functions through [Vercel Queues](https://vercel.com/docs/queues) with automatic retries and [consumer function security](#consumer-function-security)
343
+ - **Authentication**: OIDC tokens provide secure, automatic authentication
344
+
345
+ For more details, see the [Vercel Workflow documentation](https://vercel.com/docs/workflows).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workflow",
3
- "version": "5.0.0-beta.9",
3
+ "version": "5.0.0",
4
4
  "description": "Workflow SDK - Build durable, resilient, and observable workflows",
5
5
  "main": "dist/typescript-plugin.cjs",
6
6
  "type": "module",
@@ -49,6 +49,8 @@
49
49
  "./astro": "./dist/astro.js",
50
50
  "./vite": "./dist/vite.js",
51
51
  "./nest": "./dist/nest.js",
52
+ "./nest/builder": "./dist/nest-builder.js",
53
+ "./nest/vercel-builder": "./dist/nest-vercel-builder.js",
52
54
  "./runtime": "./dist/runtime.js",
53
55
  "./observability": {
54
56
  "types": "./dist/observability.d.ts",
@@ -56,24 +58,25 @@
56
58
  }
57
59
  },
58
60
  "dependencies": {
61
+ "@workflow/astro": "5.0.0",
62
+ "@workflow/cli": "5.0.0",
63
+ "@workflow/core": "5.0.0",
64
+ "@workflow/errors": "5.0.0",
65
+ "@workflow/typescript-plugin": "5.0.0",
66
+ "@workflow/utils": "5.0.0",
59
67
  "ms": "2.1.3",
60
- "@workflow/astro": "5.0.0-beta.9",
61
- "@workflow/cli": "5.0.0-beta.9",
62
- "@workflow/core": "5.0.0-beta.9",
63
- "@workflow/errors": "5.0.0-beta.5",
64
- "@workflow/typescript-plugin": "5.0.0-beta.4",
65
- "@workflow/utils": "5.0.0-beta.3",
66
- "@workflow/next": "5.0.0-beta.9",
67
- "@workflow/nest": "5.0.0-beta.9",
68
- "@workflow/nitro": "5.0.0-beta.9",
69
- "@workflow/nuxt": "5.0.0-beta.9",
70
- "@workflow/sveltekit": "5.0.0-beta.9",
71
- "@workflow/rollup": "5.0.0-beta.9"
68
+ "@workflow/next": "5.0.0",
69
+ "@workflow/nest": "5.0.0",
70
+ "@workflow/nitro": "5.0.0",
71
+ "@workflow/nuxt": "5.0.0",
72
+ "@workflow/sveltekit": "5.0.0",
73
+ "@workflow/rollup": "5.0.0"
72
74
  },
73
75
  "devDependencies": {
74
76
  "@types/ms": "2.1.0",
75
77
  "@types/node": "22.19.0",
76
- "@workflow/tsconfig": "5.0.0-beta.0"
78
+ "@workflow/tsconfig": "5.0.0",
79
+ "typescript": "^6.0.3"
77
80
  },
78
81
  "peerDependencies": {
79
82
  "@opentelemetry/api": "1"
@@ -1,63 +0,0 @@
1
- ---
2
- title: experimental_setAttributes
3
- description: Attach string metadata to workflow run for observability.
4
- type: reference
5
- summary: Use experimental_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
- <Callout>
16
- This API is experimental and may change before the stable attributes API is released.
17
- </Callout>
18
-
19
- ```typescript lineNumbers
20
- import { experimental_setAttributes } from "workflow"
21
-
22
- export async function orderWorkflow(orderId: string) {
23
- "use workflow"
24
-
25
- await experimental_setAttributes({
26
- phase: "received",
27
- orderId,
28
- })
29
- }
30
- ```
31
-
32
- ## API Signature
33
-
34
- ### Parameters
35
-
36
- <TSDoc
37
- definition={`
38
- import { experimental_setAttributes } from "workflow";
39
- export default experimental_setAttributes;`}
40
- showSections={['parameters']}
41
- />
42
-
43
- ## Usage
44
-
45
- Call `experimental_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.
46
-
47
- Attribute values must be strings. Pass `undefined` to remove an attribute:
48
-
49
- ```typescript lineNumbers
50
- import { experimental_setAttributes } from "workflow"
51
-
52
- export async function cleanupAttributes() {
53
- "use workflow"
54
-
55
- await experimental_setAttributes({ staleKey: undefined })
56
- }
57
- ```
58
-
59
- 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.
60
-
61
- Validation errors throw [`FatalError`](/docs/api-reference/workflow/fatal-error) and fail the run before an attribute write is attempted.
62
-
63
- When called from a workflow body, the write is recorded through an internal step. When called from a step body, the step posts the attributes directly to the World. Storage errors from step-body calls throw from `experimental_setAttributes`, so catch them inside the step if the write should be best-effort.