@workflow/core 5.0.0-beta.43 → 5.0.0-beta.44

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 (217) hide show
  1. package/dist/capabilities.d.ts +4 -4
  2. package/dist/capabilities.js +5 -5
  3. package/dist/capture-stack.d.ts +1 -1
  4. package/dist/capture-stack.js +2 -2
  5. package/dist/class-serialization.d.ts +8 -8
  6. package/dist/class-serialization.js +9 -9
  7. package/dist/classify-error.js +5 -5
  8. package/dist/context-violation-error.d.ts +5 -5
  9. package/dist/context-violation-error.d.ts.map +1 -1
  10. package/dist/context-violation-error.js +5 -5
  11. package/dist/create-hook.d.ts +2 -2
  12. package/dist/create-hook.js +1 -1
  13. package/dist/define-hook.js +2 -2
  14. package/dist/describe-error.d.ts +7 -7
  15. package/dist/describe-error.js +8 -8
  16. package/dist/encryption.d.ts +5 -5
  17. package/dist/encryption.js +7 -7
  18. package/dist/events-consumer.d.ts +19 -7
  19. package/dist/events-consumer.d.ts.map +1 -1
  20. package/dist/events-consumer.js +33 -12
  21. package/dist/flushable-stream.d.ts +4 -4
  22. package/dist/flushable-stream.js +12 -12
  23. package/dist/global.d.ts +1 -1
  24. package/dist/global.js +1 -1
  25. package/dist/index.d.ts +1 -1
  26. package/dist/index.js +2 -2
  27. package/dist/log-format.d.ts +2 -2
  28. package/dist/log-format.js +5 -5
  29. package/dist/logger.js +4 -4
  30. package/dist/private.d.ts +21 -65
  31. package/dist/private.d.ts.map +1 -1
  32. package/dist/private.js +160 -38
  33. package/dist/replay-payload-cache.d.ts +1 -1
  34. package/dist/replay-payload-cache.js +4 -4
  35. package/dist/runtime/compute-instance.d.ts +4 -3
  36. package/dist/runtime/compute-instance.d.ts.map +1 -1
  37. package/dist/runtime/compute-instance.js +5 -4
  38. package/dist/runtime/constants.d.ts +24 -24
  39. package/dist/runtime/constants.d.ts.map +1 -1
  40. package/dist/runtime/constants.js +49 -43
  41. package/dist/runtime/count-step-started-events.d.ts +7 -7
  42. package/dist/runtime/count-step-started-events.js +4 -4
  43. package/dist/runtime/deployment-guard.d.ts +7 -7
  44. package/dist/runtime/deployment-guard.js +8 -8
  45. package/dist/runtime/get-port-lazy.d.ts +2 -2
  46. package/dist/runtime/get-port-lazy.d.ts.map +1 -1
  47. package/dist/runtime/get-port-lazy.js +37 -30
  48. package/dist/runtime/get-world-lazy.d.ts +2 -2
  49. package/dist/runtime/get-world-lazy.js +3 -3
  50. package/dist/runtime/helpers.d.ts +18 -18
  51. package/dist/runtime/helpers.js +27 -27
  52. package/dist/runtime/quickjs-entrypoint.d.ts +21 -10
  53. package/dist/runtime/quickjs-entrypoint.d.ts.map +1 -1
  54. package/dist/runtime/quickjs-entrypoint.js +199 -81
  55. package/dist/runtime/quickjs-runtime.d.ts +31 -15
  56. package/dist/runtime/quickjs-runtime.d.ts.map +1 -1
  57. package/dist/runtime/quickjs-runtime.js +126 -77
  58. package/dist/runtime/quickjs-serde.d.ts +10 -10
  59. package/dist/runtime/quickjs-serde.js +20 -20
  60. package/dist/runtime/replay-budget.d.ts +4 -4
  61. package/dist/runtime/replay-budget.js +5 -5
  62. package/dist/runtime/replay-recovery-reporter.d.ts +1 -1
  63. package/dist/runtime/replay-recovery-reporter.js +2 -2
  64. package/dist/runtime/resume-hook.d.ts +2 -2
  65. package/dist/runtime/resume-hook.js +27 -27
  66. package/dist/runtime/resume-latency.d.ts +25 -25
  67. package/dist/runtime/resume-latency.d.ts.map +1 -1
  68. package/dist/runtime/resume-latency.js +4 -4
  69. package/dist/runtime/run-id-time.d.ts +2 -2
  70. package/dist/runtime/run-id-time.js +5 -5
  71. package/dist/runtime/run.d.ts +21 -4
  72. package/dist/runtime/run.d.ts.map +1 -1
  73. package/dist/runtime/run.js +104 -10
  74. package/dist/runtime/runs.d.ts +4 -4
  75. package/dist/runtime/runs.js +2 -2
  76. package/dist/runtime/start.d.ts +3 -3
  77. package/dist/runtime/start.d.ts.map +1 -1
  78. package/dist/runtime/start.js +27 -23
  79. package/dist/runtime/step-executor.d.ts +14 -14
  80. package/dist/runtime/step-executor.d.ts.map +1 -1
  81. package/dist/runtime/step-executor.js +54 -39
  82. package/dist/runtime/step-latency.d.ts +14 -14
  83. package/dist/runtime/step-latency.js +11 -11
  84. package/dist/runtime/step-ownership.d.ts +6 -6
  85. package/dist/runtime/step-ownership.js +8 -8
  86. package/dist/runtime/step-single-flight.d.ts +1 -1
  87. package/dist/runtime/step-single-flight.d.ts.map +1 -1
  88. package/dist/runtime/step-single-flight.js +17 -11
  89. package/dist/runtime/suspension-handler.d.ts +29 -17
  90. package/dist/runtime/suspension-handler.d.ts.map +1 -1
  91. package/dist/runtime/suspension-handler.js +206 -71
  92. package/dist/runtime/unserializable-step.d.ts +34 -0
  93. package/dist/runtime/unserializable-step.d.ts.map +1 -0
  94. package/dist/runtime/unserializable-step.js +52 -0
  95. package/dist/runtime/vm-mode.d.ts +1 -1
  96. package/dist/runtime/vm-mode.js +2 -2
  97. package/dist/runtime/wait-continuation.d.ts +31 -9
  98. package/dist/runtime/wait-continuation.d.ts.map +1 -1
  99. package/dist/runtime/wait-continuation.js +44 -13
  100. package/dist/runtime/wait-until.d.ts +1 -1
  101. package/dist/runtime/wait-until.js +2 -2
  102. package/dist/runtime/world-compatibility.d.ts +20 -9
  103. package/dist/runtime/world-compatibility.d.ts.map +1 -1
  104. package/dist/runtime/world-compatibility.js +24 -13
  105. package/dist/runtime/world-init.d.ts +5 -5
  106. package/dist/runtime/world-init.js +6 -6
  107. package/dist/runtime/world.d.ts.map +1 -1
  108. package/dist/runtime/world.js +13 -3
  109. package/dist/runtime.d.ts.map +1 -1
  110. package/dist/runtime.js +300 -206
  111. package/dist/sealed-box.d.ts +7 -7
  112. package/dist/sealed-box.js +15 -15
  113. package/dist/serialization/client.js +2 -2
  114. package/dist/serialization/codec-devalue-vm.d.ts +1 -1
  115. package/dist/serialization/codec-devalue-vm.js +4 -4
  116. package/dist/serialization/codec-devalue.d.ts +1 -1
  117. package/dist/serialization/codec-devalue.js +2 -2
  118. package/dist/serialization/codec.d.ts +4 -4
  119. package/dist/serialization/codec.js +2 -2
  120. package/dist/serialization/compression.d.ts +12 -11
  121. package/dist/serialization/compression.d.ts.map +1 -1
  122. package/dist/serialization/compression.js +16 -15
  123. package/dist/serialization/encryption.d.ts +7 -7
  124. package/dist/serialization/encryption.d.ts.map +1 -1
  125. package/dist/serialization/encryption.js +8 -8
  126. package/dist/serialization/errors.js +2 -2
  127. package/dist/serialization/format.d.ts +6 -6
  128. package/dist/serialization/format.js +7 -7
  129. package/dist/serialization/hardened.d.ts +18 -18
  130. package/dist/serialization/hardened.d.ts.map +1 -1
  131. package/dist/serialization/hardened.js +41 -32
  132. package/dist/serialization/index.d.ts +1 -1
  133. package/dist/serialization/index.js +2 -2
  134. package/dist/serialization/reducers/class-vm.d.ts +1 -1
  135. package/dist/serialization/reducers/class-vm.js +2 -2
  136. package/dist/serialization/reducers/class.js +2 -2
  137. package/dist/serialization/reducers/common-vm.js +12 -12
  138. package/dist/serialization/reducers/common.js +12 -12
  139. package/dist/serialization/reducers/step-function-vm.d.ts +1 -1
  140. package/dist/serialization/reducers/step-function-vm.js +2 -2
  141. package/dist/serialization/reducers/step-function.js +2 -2
  142. package/dist/serialization/step.js +2 -2
  143. package/dist/serialization/types.d.ts +5 -5
  144. package/dist/serialization/types.js +2 -2
  145. package/dist/serialization/workflow-vm.d.ts +1 -1
  146. package/dist/serialization/workflow-vm.d.ts.map +1 -1
  147. package/dist/serialization/workflow-vm.js +13 -10
  148. package/dist/serialization-format.d.ts +11 -11
  149. package/dist/serialization-format.d.ts.map +1 -1
  150. package/dist/serialization-format.js +29 -23
  151. package/dist/serialization.d.ts +10 -10
  152. package/dist/serialization.js +98 -98
  153. package/dist/set-attributes.js +2 -2
  154. package/dist/source-map.d.ts +1 -1
  155. package/dist/source-map.d.ts.map +1 -1
  156. package/dist/source-map.js +18 -12
  157. package/dist/step/context-storage.d.ts +5 -5
  158. package/dist/step/context-storage.js +1 -1
  159. package/dist/step/writable-stream.d.ts +3 -3
  160. package/dist/step/writable-stream.js +7 -7
  161. package/dist/step.js +12 -12
  162. package/dist/symbols.d.ts +2 -2
  163. package/dist/symbols.js +3 -3
  164. package/dist/telemetry/semantic-conventions.d.ts +21 -13
  165. package/dist/telemetry/semantic-conventions.d.ts.map +1 -1
  166. package/dist/telemetry/semantic-conventions.js +21 -15
  167. package/dist/telemetry.d.ts +3 -3
  168. package/dist/telemetry.d.ts.map +1 -1
  169. package/dist/telemetry.js +19 -10
  170. package/dist/test-support/orchestrator-context.d.ts.map +1 -1
  171. package/dist/test-support/orchestrator-context.js +12 -2
  172. package/dist/version.d.ts +1 -1
  173. package/dist/version.js +2 -2
  174. package/dist/vm/index.js +4 -4
  175. package/dist/vm/script-cache.d.ts.map +1 -1
  176. package/dist/vm/script-cache.js +23 -17
  177. package/dist/vm/uint8array-base64.js +1 -1
  178. package/dist/workflow/abort-controller.d.ts +1 -1
  179. package/dist/workflow/abort-controller.js +12 -12
  180. package/dist/workflow/create-hook.js +2 -2
  181. package/dist/workflow/get-workflow-metadata.js +2 -2
  182. package/dist/workflow/hook.js +16 -16
  183. package/dist/workflow/set-attributes.d.ts +1 -1
  184. package/dist/workflow/set-attributes.js +2 -2
  185. package/dist/workflow/sleep.js +2 -2
  186. package/dist/workflow/world-init-stub.d.ts +1 -1
  187. package/dist/workflow/world-init-stub.js +2 -2
  188. package/dist/workflow.d.ts +2 -2
  189. package/dist/workflow.js +21 -7
  190. package/docs/api-reference/create-hook.mdx +15 -15
  191. package/docs/api-reference/create-webhook.mdx +15 -15
  192. package/docs/api-reference/define-hook.mdx +10 -10
  193. package/docs/api-reference/fatal-error.mdx +2 -2
  194. package/docs/api-reference/fetch.mdx +7 -7
  195. package/docs/api-reference/get-step-metadata.mdx +2 -2
  196. package/docs/api-reference/get-workflow-metadata.mdx +3 -3
  197. package/docs/api-reference/get-writable.mdx +7 -7
  198. package/docs/api-reference/index.mdx +1 -1
  199. package/docs/api-reference/retryable-error.mdx +1 -1
  200. package/docs/api-reference/set-attributes.mdx +2 -2
  201. package/docs/api-reference/sleep.mdx +3 -3
  202. package/docs/foundations/cancellation.mdx +31 -31
  203. package/docs/foundations/errors-and-retries.mdx +42 -11
  204. package/docs/foundations/hooks.mdx +35 -35
  205. package/docs/foundations/idempotency.mdx +9 -9
  206. package/docs/foundations/serialization.mdx +21 -22
  207. package/docs/foundations/starting-workflows.mdx +36 -37
  208. package/docs/foundations/streaming.mdx +46 -41
  209. package/docs/foundations/versioning.mdx +3 -3
  210. package/docs/foundations/workflows-and-steps.mdx +9 -9
  211. package/docs/how-it-works/cancellation.mdx +62 -62
  212. package/docs/how-it-works/code-transform.mdx +66 -54
  213. package/docs/how-it-works/encryption.mdx +25 -21
  214. package/docs/how-it-works/event-sourcing.mdx +53 -35
  215. package/docs/how-it-works/framework-integrations.mdx +12 -12
  216. package/docs/how-it-works/understanding-directives.mdx +21 -21
  217. package/package.json +7 -7
@@ -1,5 +1,5 @@
1
1
  ---
2
- title: How the Directives Work
2
+ title: How the directives work
3
3
  description: Deep dive into the internals of how Workflow SDK directives transform your code.
4
4
  type: conceptual
5
5
  summary: Learn how the compiler transforms directive-annotated code into three execution modes.
@@ -10,16 +10,15 @@ related:
10
10
  ---
11
11
 
12
12
  <Callout>
13
- This is an advanced guide that dives into internals of the Workflow SDK directive and is not required reading to use workflows. To simply use the Workflow SDK, check out the [getting started](/docs/getting-started) guides for your framework.
13
+ This advanced guide covers the internals of Workflow SDK directives. To start using Workflow SDK, see the [getting started](/docs/getting-started) guide for your framework.
14
14
  </Callout>
15
15
 
16
- Workflows use special directives to mark code for transformation by the Workflow SDK compiler. This page explains how `"use workflow"` and `"use step"` directives work, what transformations are applied, and why they're necessary for durable execution.
16
+ Workflows use special directives to mark code for transformation by the Workflow SDK compiler. The `"use workflow"` and `"use step"` directives apply the transformations required for durable execution.
17
17
 
18
- ## Directives Overview
18
+ ## Directives overview
19
19
 
20
20
  Workflows use two directives to mark functions for special handling:
21
21
 
22
- {/* @skip-typecheck: incomplete code sample */}
23
22
  ```typescript
24
23
  export async function handleUserSignup(email: string) {
25
24
  "use workflow"; // [!code highlight]
@@ -44,7 +43,7 @@ async function createUser(email: string) {
44
43
 
45
44
  These directives trigger the `@workflow/swc-plugin` compiler to transform your code in different ways depending on the execution context.
46
45
 
47
- ## The Three Transformation Modes
46
+ ## The three transformation modes
48
47
 
49
48
  The compiler operates in three distinct modes, transforming the same source code differently for each execution context:
50
49
 
@@ -52,39 +51,47 @@ The compiler operates in three distinct modes, transforming the same source code
52
51
  flowchart LR
53
52
  A["Source Code<br/>with directives"] --> B["Step Mode"]
54
53
  A --> C["Workflow Mode"]
55
- A --> D["Client Mode"]
54
+ A --> D["Detect Mode"]
56
55
  B --> E["Step registration bundle"]
56
+ B --> G["Your App Code<br/>(Enables `start`)"]
57
57
  C --> F["Workflow bundle"]
58
58
  E --> H["Combined flow handler"]
59
59
  F --> H
60
- D --> G["Your App Code<br/>(Enables `start`)"]
60
+ D --> I["Build manifest<br/>(discovery)"]
61
61
  ```
62
62
 
63
- ### Comparison Table
63
+ ### Comparison table
64
64
 
65
65
  | Mode | Used In | Purpose | Runtime role | Required? |
66
66
  |----------|------------|--------------------------------|--------------|-----------|
67
- | Step | Build time | Registers executable step functions | Imported by the combined flow handler | Yes |
67
+ | Step | Build time + your app code | Registers executable step functions; gives app code workflow IDs for `start()` | Imported by the combined flow handler, and applied to application code by the framework loader | Yes |
68
68
  | Workflow | Build time | Bundles workflow orchestrators | Executed by `.well-known/workflow/v1/flow` | Yes |
69
- | Client | Build/Runtime | Provides workflow IDs and types to `start` | Your application code | Optional* |
69
+ | Detect | Build time | Discovers workflows, steps, and serialization classes without transforming code | Feeds the build's discovery phase and manifest | Yes (build-internal) |
70
70
 
71
- \* Client mode is **recommended** for better developer experience—it provides automatic ID generation and type safety. Without it, you must manually construct workflow IDs or use the build manifest.
71
+ <Callout type="info">
72
+ Earlier releases had a separate **client mode** for application code. In 5.0 it merged into step mode, which produces the same app-code behavior (workflow functions throw on direct calls and carry `workflowId` for `start()`) while also registering step functions. Build integrations that passed `mode: "client"` now pass `mode: "step"`.
73
+ </Callout>
72
74
 
73
- ## Detailed Transformation Examples
75
+ ## Detailed transformation examples
74
76
 
75
- <Tabs items={["Step Mode", "Workflow Mode", "Client Mode"]}>
77
+ <Tabs items={["Step Mode", "Workflow Mode", "Detect Mode"]}>
76
78
  <Tab value="Step Mode">
77
79
 
78
- **Step Mode** creates a registration bundle that the combined flow handler imports. It is not an HTTP route.
80
+ **Step Mode** creates the registration bundle that the combined flow handler imports (it is not an HTTP route), and is also the transform framework loaders apply to your application code.
79
81
 
80
82
  **Input:**
81
83
 
82
- {/* @skip-typecheck: incomplete code sample */}
83
84
  ```typescript
84
85
  export async function createUser(email: string) {
85
86
  "use step";
86
87
  return { id: crypto.randomUUID(), email };
87
88
  }
89
+
90
+ export async function handleUserSignup(email: string) {
91
+ "use workflow";
92
+ const user = await createUser(email);
93
+ return { userId: user.id };
94
+ }
88
95
  ```
89
96
 
90
97
  **Output:**
@@ -99,18 +106,25 @@ export async function createUser(email: string) {
99
106
  __wf_reg.set(__wf_id, __wf_fn); // [!code highlight]
100
107
  __wf_fn.stepId = __wf_id; // [!code highlight]
101
108
  })(createUser, "step//workflows/user.js//createUser"); // [!code highlight]
109
+
110
+ export async function handleUserSignup(email: string) {
111
+ throw new Error("You attempted to execute ..."); // [!code highlight]
112
+ }
113
+ handleUserSignup.workflowId = "workflow//workflows/user.js//handleUserSignup"; // [!code highlight]
102
114
  ```
103
115
 
104
116
  **What happens:**
105
117
 
106
118
  - The `"use step"` directive is removed
107
- - The function body is kept completely intact (no transformation)
108
- - The function is registered with the runtime via an inline IIFE (no imports needed)
119
+ - Step function bodies are kept completely intact (no transformation)
120
+ - Each step function is registered with the runtime via an inline IIFE (no imports needed)
109
121
  - Step functions run with full Node.js/Deno/Bun access
122
+ - Workflow function bodies are **replaced** with an error throw, and a `workflowId` property is attached. Launch workflow functions with [`start()`](/docs/api-reference/workflow-api/start) instead of calling them directly. The ID identifies the workflow to `start()`
123
+ - A dead-code-elimination pass removes code reachable only from the replaced workflow bodies
110
124
 
111
- **Why no transformation?** Step functions execute in your main runtime with full access to Node.js APIs, file system, databases, etc. They don't need any special handling—they just run normally.
125
+ **Why no step transformation?** Step functions execute in your main runtime with full access to Node.js APIs, the file system, databases, and other resources. They run normally without special handling.
112
126
 
113
- **ID Format:** Step IDs follow the pattern `step//{filepath}//{functionName}`, where the filepath is relative to your project root.
127
+ **ID format:** Step IDs follow the pattern `step//{filepath}//{functionName}`, where the file path is relative to your project root.
114
128
 
115
129
  </Tab>
116
130
  <Tab value="Workflow Mode">
@@ -119,7 +133,6 @@ export async function createUser(email: string) {
119
133
 
120
134
  **Input:**
121
135
 
122
- {/* @skip-typecheck: incomplete code sample */}
123
136
  ```typescript
124
137
  export async function createUser(email: string) {
125
138
  "use step";
@@ -151,7 +164,7 @@ handleUserSignup.workflowId = "workflow//workflows/user.js//handleUserSignup"; /
151
164
  **What happens:**
152
165
 
153
166
  - Step function bodies are **replaced** with calls to `globalThis[Symbol.for("WORKFLOW_USE_STEP")]`
154
- - Workflow function bodies remain **intact**—they execute deterministically during replay
167
+ - Workflow function bodies remain **intact**, so they execute deterministically during replay
155
168
  - The workflow function gets a `workflowId` property for runtime identification
156
169
  - The `"use workflow"` directive is removed
157
170
 
@@ -159,14 +172,14 @@ handleUserSignup.workflowId = "workflow//workflows/user.js//handleUserSignup"; /
159
172
 
160
173
  1. Checks if the step has already been executed (in the event log)
161
174
  2. If yes: Returns the cached result
162
- 3. If no: Triggers a suspension and enqueues the step for background execution
175
+ 3. If no: Suspends the replay and executes the step, usually inline in the same invocation. The runtime returns the run to the queue only when the invocation's inline budget is exhausted or its timeout approaches
163
176
 
164
- **ID Format:** Workflow IDs follow the pattern `workflow//{filepath}//{functionName}`. The `workflowId` property is attached to the function to allow [`start()`](/docs/api-reference/workflow-api/start) to work at runtime.
177
+ **ID format:** Workflow IDs follow the pattern `workflow//{filepath}//{functionName}`. The `workflowId` property is attached to the function so [`start()`](/docs/api-reference/workflow-api/start) works at runtime.
165
178
 
166
179
  </Tab>
167
- <Tab value="Client Mode">
180
+ <Tab value="Detect Mode">
168
181
 
169
- **Client Mode** transforms workflow functions in your application code to prevent direct execution.
182
+ **Detect Mode** is a lightweight, non-transforming mode used during the build's discovery phase.
170
183
 
171
184
  **Input:**
172
185
 
@@ -183,35 +196,30 @@ export async function handleUserSignup(email: string) {
183
196
 
184
197
  {/* @skip-typecheck: incomplete code sample */}
185
198
  ```typescript
199
+ /**__internal_workflows{"workflows":{"user.js":{"handleUserSignup":{"workflowId":"workflow//workflows/user.js//handleUserSignup"}}}}*/; // [!code highlight]
186
200
  export async function handleUserSignup(email: string) {
187
- throw new Error("You attempted to execute ..."); // [!code highlight]
201
+ "use workflow";
202
+ const user = await createUser(email);
203
+ return { userId: user.id };
188
204
  }
189
- handleUserSignup.workflowId = "workflow//workflows/user.js//handleUserSignup"; // [!code highlight]
190
205
  ```
191
206
 
192
207
  **What happens:**
193
208
 
194
- - Workflow function bodies are **replaced** with an error throw
195
- - The `workflowId` property is added (same as workflow mode)
196
- - Step functions are not transformed in client mode
197
-
198
- **Why this transformation?** Workflow functions cannot be called directly from application code—they must be started using [`start()`](/docs/api-reference/workflow-api/start). The error prevents accidental direct execution while the `workflowId` property allows the `start()` function to identify which workflow to launch.
209
+ - The code is **not modified**. Detect mode only walks the AST
210
+ - Discovered workflows, steps, and custom-serialization classes are emitted as a JSON manifest comment
211
+ - The build uses this to decide which files feed the step and workflow bundles
199
212
 
200
- The IDs are generated exactly like in workflow mode to ensure they can be directly referenced at runtime.
213
+ **Why a separate mode?** The build system first runs a fast regular expression pre-scan to find candidate files containing directive-like strings. It then runs detect mode on those candidates to validate them at the abstract syntax tree (AST) level. The plugin eliminates false positives, such as a directive-like string inside a template literal, because it only recognizes genuine directive statements.
201
214
 
202
215
  <Callout type="info">
203
- **Client mode is optional:** While recommended for better developer experience (automatic IDs and type safety), you can skip client mode and instead:
204
- - Manually construct workflow IDs using the pattern `workflow//{filepath}//{functionName}`
205
- - Use the workflow manifest file generated during build to lookup IDs
206
- - Pass IDs directly to `start()` as strings
207
-
208
- All framework integrations include client mode as a loader by default.
216
+ **Working without the app-code loader:** Frameworks apply the step-mode transform to application code by default, which is what gives `start(handleUserSignup)` its automatic IDs and type safety. If your setup can't run the loader, you can instead construct workflow IDs manually using the pattern `workflow//{filepath}//{functionName}`, look them up in the build manifest, and pass them to `start()` as strings.
209
217
  </Callout>
210
218
 
211
219
  </Tab>
212
220
  </Tabs>
213
221
 
214
- ## Generated Files
222
+ ## Generated files
215
223
 
216
224
  When you build your application, the Workflow SDK generates a combined flow handler, an internal step registration bundle, and a webhook handler. Exact filenames vary by framework.
217
225
 
@@ -224,7 +232,7 @@ Contains all workflow functions transformed in **workflow mode**. This file is i
224
232
  All workflow code is bundled together and embedded as a string inside `flow.js`. When a workflow needs to execute, this bundled code is run inside a **Node.js VM** (virtual machine) to ensure:
225
233
 
226
234
  - **Determinism**: The same inputs always produce the same outputs
227
- - **Side-effect prevention**: Direct access to Node.js APIs, file system, network, etc. is blocked
235
+ - **Side-effect prevention**: Direct access to Node.js APIs, the file system, the network, and other resources is blocked
228
236
  - **Sandboxed execution**: Workflow orchestration logic is isolated from the main runtime
229
237
 
230
238
  **Build-time validation:**
@@ -259,6 +267,10 @@ Contains all step functions transformed in **step mode**. The combined flow hand
259
267
 
260
268
  This module must not be exposed as an HTTP endpoint.
261
269
 
270
+ <Callout type="info">
271
+ **Changed in 5.0:** In 4.x, the step bundle was served as its own HTTP route at `POST /.well-known/workflow/v1/step`, with step messages delivered on a separate `__wkf_step_*` queue topic. v5 merged both into the combined flow handler. The step bundle became a registration module imported by `flow.js`, and step messages arrive on the shared workflow queue. Use the version picker to see the old layout on the v4 version of this page.
272
+ </Callout>
273
+
262
274
  ### `webhook.js`
263
275
 
264
276
  Contains webhook handling logic for delivering external data to running workflows via [`createWebhook()`](/docs/api-reference/workflow/create-webhook).
@@ -269,24 +281,24 @@ Contains webhook handling logic for delivering external data to running workflow
269
281
  - Validates tokens and routes data to the correct workflow run
270
282
  - Resumes workflow execution after webhook delivery
271
283
 
272
- **Note:** The webhook file structure varies by framework. Next.js generates `webhook/[token]/route.js` to leverage App Router's dynamic routing, while other frameworks generate a single `webhook.js` or `webhook.mjs` handler.
284
+ The webhook file structure varies by framework. Next.js generates `webhook/[token]/route.js` to use App Router's dynamic routing, while other frameworks generate a single `webhook.js` or `webhook.mjs` handler.
273
285
 
274
- ## Why Three Modes?
286
+ ## Why three modes?
275
287
 
276
288
  The multi-mode transformation enables the Workflow SDK's durable execution model:
277
289
 
278
- 1. **Step Mode** (required) - Bundles executable step functions that can access the full runtime
279
- 2. **Workflow Mode** (required) - Creates orchestration logic that can replay from event logs
280
- 3. **Client Mode** (optional) - Prevents direct execution and enables type-safe workflow references
290
+ 1. **Step mode** (required): Bundles executable step functions that can access the full runtime and serves as the application-code transform that prevents direct workflow execution and enables type-safe `start()` references
291
+ 2. **Workflow mode** (required): Creates orchestration logic that can replay from event logs
292
+ 3. **Detect mode** (build-internal): Discovers directive-marked functions for the build without changing the code
281
293
 
282
294
  This separation allows:
283
295
 
284
296
  - **Deterministic replay**: Workflows can be safely replayed from event logs without re-executing side effects
285
297
  - **Sandboxed orchestration**: Workflow logic runs in a controlled VM without direct runtime access
286
298
  - **Stateless execution**: Your compute can scale to zero and resume from any point in the workflow
287
- - **Type safety**: TypeScript works seamlessly with workflow references (when using client mode)
299
+ - **Type safety**: TypeScript supports workflow references passed to `start()`
288
300
 
289
- ## Determinism and Replay
301
+ ## Determinism and replay
290
302
 
291
303
  A key aspect of the transformation is maintaining **deterministic replay** for workflow functions.
292
304
 
@@ -296,7 +308,7 @@ A key aspect of the transformation is maintaining **deterministic replay** for w
296
308
  - No direct side effects (no API calls, no database writes, no file I/O)
297
309
  - Can use seeded random/time APIs provided by the VM (`Math.random()`, `Date.now()`, etc.)
298
310
 
299
- Because workflow functions are deterministic and have no side effects, they can be safely re-run multiple times to calculate what the next step should be. This is why workflow function bodies remain intact in workflow modethey're pure orchestration logic.
311
+ Because workflow functions are deterministic and have no side effects, they can be safely rerun multiple times to calculate the next step. Workflow function bodies remain intact in workflow mode because they're pure orchestration logic.
300
312
 
301
313
  **Step functions can be non-deterministic:**
302
314
 
@@ -306,7 +318,7 @@ Because workflow functions are deterministic and have no side effects, they can
306
318
 
307
319
  Learn more about [Workflows and Steps](/docs/foundations/workflows-and-steps).
308
320
 
309
- ## ID Generation
321
+ ## ID generation
310
322
 
311
323
  The compiler generates stable IDs for workflows and steps based on file paths and function names:
312
324
 
@@ -328,15 +340,15 @@ The compiler generates stable IDs for workflows and steps based on file paths an
328
340
  Although IDs can change when files are moved or functions are renamed, Workflow SDK functions assume [atomic versioning](/docs/foundations/versioning) in the World. This means changing IDs won't break old workflows from running, but will prevent runs from being upgraded and will cause your workflow/step names to change in observability across deployments.
329
341
  </Callout>
330
342
 
331
- ## Framework Integration
343
+ ## Framework integration
332
344
 
333
- These transformations are framework-agnostic—they output standard JavaScript that works anywhere.
345
+ These transformations are framework-agnostic. They output standard JavaScript that works anywhere.
334
346
 
335
347
  **For users**: Your framework handles all transformations automatically. See the [Getting Started](/docs/getting-started) guide for your framework.
336
348
 
337
349
  **For framework authors**: Learn how to integrate these transformations into your framework in [Building Framework Integrations](/docs/how-it-works/framework-integrations).
338
350
 
339
- ## Debugging Transformed Code
351
+ ## Debugging transformed code
340
352
 
341
353
  If you need to debug transformation issues, you can inspect the generated files:
342
354
 
@@ -11,54 +11,58 @@ related:
11
11
  ---
12
12
 
13
13
  <Callout>
14
- This guide explains how Workflow SDK encrypts user data in the event log. Understanding these details is not required to use workflows — encryption is automatic and requires no code changes. For getting started, see the [getting started](/docs/getting-started) guides for your framework.
14
+ Workflow SDK automatically encrypts user data in the event log without requiring code changes. To start using workflows, see the [getting started](/docs/getting-started) guide for your framework.
15
15
  </Callout>
16
16
 
17
- Workflow SDK supports automatic end-to-end encryption of all user data before it is written to the event log. When a `World` implementation provides encryption support, it is safe to pass sensitive data such as API keys, tokens, or user credentials as workflow inputs, step arguments, and return values. The storage backend only ever sees ciphertext.
17
+ Workflow SDK supports automatic end-to-end encryption of all user data before writing it to the event log. When a `World` implementation provides encryption support, you can pass sensitive data, such as API keys, tokens, or user credentials, as workflow inputs, step arguments, and return values. The storage backend only sees ciphertext.
18
18
 
19
- Encryption support varies by `World` implementation. See the [Worlds](/worlds) page to check which worlds support this feature. `World` implementations opt into encryption by providing a `getEncryptionKeyForRun()` method the core runtime will use it automatically when present.
19
+ Encryption support varies by `World` implementation. See the [Worlds](/worlds) page to check which Worlds support this feature. `World` implementations opt into encryption by providing a `getEncryptionKeyForRun()` method. The core runtime uses it automatically when present.
20
20
 
21
- ## What Is Encrypted
21
+ ## What is encrypted
22
22
 
23
23
  All user data flowing through the event log is encrypted:
24
24
 
25
- - **Workflow inputs** arguments passed when starting a workflow
26
- - **Workflow return values** the final output of a workflow
27
- - **Step inputs** arguments passed to step functions
28
- - **Step return values** the result returned by step functions
29
- - **Hook metadata** data attached when creating a hook
30
- - **Hook payloads** data received by hooks and webhooks
31
- - **Stream data** each frame in a `ReadableStream` or `WritableStream`
25
+ - **Workflow inputs**: Arguments passed when starting a workflow
26
+ - **Workflow return values**: The final output of a workflow
27
+ - **Step inputs**: Arguments passed to step functions
28
+ - **Step return values**: The result returned by step functions
29
+ - **Hook metadata**: Data attached when creating a hook
30
+ - **Hook payloads**: Data received by hooks and webhooks
31
+ - **Stream data**: Each frame in a `ReadableStream` or `WritableStream`
32
32
 
33
33
  Metadata such as workflow names, step names, entity IDs, timestamps, and lifecycle states are **not** encrypted. This allows the observability tools to display run structure and timelines without requiring decryption.
34
34
 
35
- ## How It Works
35
+ ## How it works
36
36
 
37
- ### Key Management
37
+ ### Compression
38
+
39
+ Payloads are compressed before encryption. A format prefix on the stored value records the compression codec (gzip, with zstd support in the format), and the inner payload keeps its own serialization format prefix after decompression. Repetitive payloads compress heavily. AI token streams average around 80% smaller, reducing storage and network transfer. Like encryption, compression is automatic and requires no code changes.
40
+
41
+ ### Key management
38
42
 
39
43
  Each workflow run is encrypted with its own unique key, provided by the `World` implementation via `getEncryptionKeyForRun()`. How the key is generated and stored is up to the `World`.
40
44
 
41
45
  For example, the [Vercel World](/worlds/vercel) provides unique keys per run and execution environment, ensuring that a given run can only decrypt data from that run itself.
42
46
 
43
- ### Encryption Algorithm
47
+ ### Encryption algorithm
44
48
 
45
49
  Data is encrypted using **AES-256-GCM** via the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API):
46
50
 
47
51
  - A random 12-byte nonce is generated for each encryption operation
48
- - The GCM authentication tag provides integrity verification any tampering with the ciphertext is detected
52
+ - The GCM authentication tag provides integrity verification and detects any ciphertext tampering
49
53
  - The same plaintext produces different ciphertext each time due to the random nonce
50
54
 
51
- ## Decrypting Data
55
+ ## Decrypting data
52
56
 
53
57
  When viewing workflow runs through the observability tools, encrypted fields display as locked placeholders until you explicitly choose to decrypt them.
54
58
 
55
59
  ### Permissions
56
60
 
57
- Decryption access is controlled by the `World` implementation. On Vercel, decryption follows the same permissions model as project environment variables if you don't have permission to view environment variable values for a project, you won't be able to decrypt workflow data either. Each decryption request is recorded in your [Vercel audit log](https://vercel.com/docs/audit-log), giving your team full visibility into when and by whom workflow data was accessed.
61
+ The `World` implementation controls decryption access. On Vercel, decryption follows the same permissions model as project environment variables. If you don't have permission to view environment variable values for a project, you can't decrypt its workflow data. Vercel records each decryption request in your [Vercel audit log](https://vercel.com/docs/audit-log), so your team can see when someone accessed workflow data and who accessed it.
58
62
 
59
- ### Web Dashboard
63
+ ### Web dashboard
60
64
 
61
- Click the **Decrypt** button in the run detail panel to decrypt all data fields. Decryption happens entirely in the browser via the Web Crypto API the observability server retrieves the encryption key but never sees your plaintext data.
65
+ Click the **Decrypt** button in the run detail panel to decrypt all data fields. Decryption happens entirely in the browser through the Web Crypto API. The observability server retrieves the encryption key but never sees your plaintext data.
62
66
 
63
67
  ### CLI
64
68
 
@@ -80,7 +84,7 @@ npx workflow inspect stream <stream-id> --run <run-id> --decrypt
80
84
 
81
85
  Without `--decrypt`, encrypted fields display as `🔒 Encrypted` placeholders.
82
86
 
83
- ## Custom World Implementations
87
+ ## Custom World implementations
84
88
 
85
89
  The core runtime encrypts data automatically when the `World` implementation provides a `getEncryptionKeyForRun()` method. The core runtime can call this method in two forms:
86
90
 
@@ -98,7 +102,7 @@ Use `getEncryptionKeyForRun(run)` when the run entity already exists. Use `getEn
98
102
  To add encryption support to a custom `World`:
99
103
 
100
104
  1. Implement `getEncryptionKeyForRun()` on your `World` class, handling both call shapes
101
- 2. Return the raw 32-byte key as a `Uint8Array` the core runtime uses it for AES-256-GCM operations
105
+ 2. Return the raw 32-byte key as a `Uint8Array`. The core runtime uses it for AES-256-GCM operations
102
106
  3. Ensure the same key is returned for the same run ID across invocations (for decryption during replay)
103
107
 
104
108
  ```typescript