workflow 4.2.0-beta.75 → 4.2.0-beta.76

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.
@@ -0,0 +1,408 @@
1
+ ---
2
+ title: Storage
3
+ description: Query workflow runs, steps, hooks, and the underlying event log via the World storage interface.
4
+ type: reference
5
+ summary: "Interfaces: world.events, world.runs, world.steps, world.hooks. Events are the source of truth; runs, steps, and hooks are materialized views."
6
+ prerequisites:
7
+ - /docs/api-reference/workflow-api/get-world
8
+ related:
9
+ - /docs/api-reference/workflow-api/get-run
10
+ - /docs/how-it-works/event-sourcing
11
+ - /docs/api-reference/workflow-api/world/observability
12
+ keywords:
13
+ - world.events
14
+ - world.runs
15
+ - world.steps
16
+ - world.hooks
17
+ - event log
18
+ - event sourcing
19
+ - materialized views
20
+ - WorkflowRun
21
+ - Step
22
+ - Hook
23
+ - Event
24
+ - cursor pagination
25
+ - resolveData
26
+ - run_cancelled
27
+ - correlation ID
28
+ - parseStepName
29
+ - parseWorkflowName
30
+ ---
31
+
32
+ The World storage interface exposes four sub-interfaces for querying workflow data:
33
+
34
+ - **`world.events`** — The append-only event log. This is the source of truth for all workflow state. See [Event Sourcing](/docs/how-it-works/event-sourcing) for background.
35
+ - **`world.runs`**, **`world.steps`**, **`world.hooks`** — Materialized views derived from the event log, provided as convenience accessors for the most common query patterns.
36
+
37
+ ```typescript lineNumbers
38
+ import { getWorld } from "workflow/runtime";
39
+
40
+ const world = getWorld(); // [!code highlight]
41
+ ```
42
+
43
+ ---
44
+
45
+ ## world.events
46
+
47
+ The event log drives all workflow state. Use it for audit trails, debugging, and programmatic run cancellation.
48
+
49
+ ### events.create()
50
+
51
+ Create a new event for a workflow run. Most commonly used to cancel a run.
52
+
53
+ ```typescript lineNumbers
54
+ await world.events.create(runId, { // [!code highlight]
55
+ eventType: "run_cancelled", // [!code highlight]
56
+ }); // [!code highlight]
57
+ ```
58
+
59
+ | Parameter | Type | Description |
60
+ |-----------|------|-------------|
61
+ | `runId` | `string \| null` | The workflow run ID (`null` only for `run_created` events, where the server generates an ID) |
62
+ | `data` | `CreateEventRequest` | Event data including `eventType` |
63
+ | `params` | `object` | Optional parameters |
64
+
65
+ **Returns:** `EventResult` — The created event and the affected entity (run/step/hook)
66
+
67
+ ### events.get()
68
+
69
+ Retrieve a single event by run ID and event ID.
70
+
71
+ ```typescript lineNumbers
72
+ const event = await world.events.get(runId, eventId); // [!code highlight]
73
+ ```
74
+
75
+ | Parameter | Type | Description |
76
+ |-----------|------|-------------|
77
+ | `runId` | `string` | The workflow run ID |
78
+ | `eventId` | `string` | The event ID |
79
+
80
+ **Returns:** `Event`
81
+
82
+ ### events.list()
83
+
84
+ List events for a run with cursor pagination.
85
+
86
+ ```typescript lineNumbers
87
+ const result = await world.events.list({ runId, pagination: { cursor } }); // [!code highlight]
88
+ ```
89
+
90
+ | Parameter | Type | Description |
91
+ |-----------|------|-------------|
92
+ | `params.runId` | `string` | Filter events by run ID |
93
+ | `params.pagination.cursor` | `string` | Cursor for the next page |
94
+
95
+ **Returns:** `{ data: Event[], cursor?: string }`
96
+
97
+ ### events.listByCorrelationId()
98
+
99
+ List events that share a correlation ID, useful for tracing related events across runs.
100
+
101
+ ```typescript lineNumbers
102
+ const result = await world.events.listByCorrelationId({ // [!code highlight]
103
+ correlationId: "order-123",
104
+ }); // [!code highlight]
105
+ ```
106
+
107
+ | Parameter | Type | Description |
108
+ |-----------|------|-------------|
109
+ | `params.correlationId` | `string` | The correlation ID to filter by |
110
+ | `params.pagination.cursor` | `string` | Cursor for the next page |
111
+
112
+ **Returns:** `{ data: Event[], cursor?: string }`
113
+
114
+ ### Event Types
115
+
116
+ | Category | Types |
117
+ |----------|-------|
118
+ | Run | `run_created`, `run_started`, `run_completed`, `run_failed`, `run_cancelled` |
119
+ | Step | `step_created`, `step_started`, `step_completed`, `step_failed`, `step_retrying` |
120
+ | Hook | `hook_created`, `hook_received`, `hook_disposed`, `hook_conflict` |
121
+ | Wait | `wait_created`, `wait_completed` |
122
+
123
+ ---
124
+
125
+ ## world.runs
126
+
127
+ Materialized from run events. Use it to list and inspect workflow runs.
128
+
129
+ ### runs.get()
130
+
131
+ ```typescript lineNumbers
132
+ const run = await world.runs.get(runId); // [!code highlight]
133
+ ```
134
+
135
+ | Parameter | Type | Description |
136
+ |-----------|------|-------------|
137
+ | `runId` | `string` | The workflow run ID |
138
+ | `params.resolveData` | `'all' \| 'none'` | Whether to include input/output data. Default: `'all'` |
139
+
140
+ **Returns:** `WorkflowRun` (or `WorkflowRunWithoutData` when `resolveData: 'none'`)
141
+
142
+ ### runs.list()
143
+
144
+ ```typescript lineNumbers
145
+ const result = await world.runs.list({ // [!code highlight]
146
+ pagination: { cursor },
147
+ }); // [!code highlight]
148
+ ```
149
+
150
+ | Parameter | Type | Description |
151
+ |-----------|------|-------------|
152
+ | `params.pagination.cursor` | `string` | Cursor for the next page |
153
+ | `params.resolveData` | `'all' \| 'none'` | Whether to include input/output data |
154
+
155
+ **Returns:** `{ data: WorkflowRun[], cursor?: string }`
156
+
157
+ ### Cancelling Runs
158
+
159
+ To cancel a run, create a `run_cancelled` event via `world.events.create()` (see [world.events](#worldevents) above), or use the CLI or Web UI helpers.
160
+
161
+ ### WorkflowRun Type
162
+
163
+ | Field | Type | Description |
164
+ |-------|------|-------------|
165
+ | `runId` | `string` | Unique run identifier |
166
+ | `status` | `string` | `'running'`, `'completed'`, `'failed'`, `'cancelled'` |
167
+ | `workflowName` | `string` | Machine-readable workflow identifier |
168
+ | `input` | `any` | Workflow input data (when `resolveData: 'all'`) |
169
+ | `output` | `any` | Workflow output data (when `resolveData: 'all'`) |
170
+ | `error` | `any` | Error data if the run failed |
171
+ | `startedAt` | `string` | ISO timestamp when the run started |
172
+ | `completedAt` | `string \| null` | ISO timestamp when the run completed |
173
+
174
+ <Callout type="warn">
175
+ `workflowName` is a machine-readable identifier like `workflow//./src/workflows/order//processOrder`. Use `parseWorkflowName()` from `workflow/observability` to extract a display-friendly name.
176
+ </Callout>
177
+
178
+ ---
179
+
180
+ ## world.steps
181
+
182
+ Materialized from step events. Use it to list steps, inspect their input/output, and build progress dashboards.
183
+
184
+ ### steps.get()
185
+
186
+ ```typescript lineNumbers
187
+ const step = await world.steps.get(runId, stepId); // [!code highlight]
188
+ ```
189
+
190
+ | Parameter | Type | Description |
191
+ |-----------|------|-------------|
192
+ | `runId` | `string \| undefined` | The workflow run ID |
193
+ | `stepId` | `string` | The step ID |
194
+ | `params.resolveData` | `'all' \| 'none'` | Whether to include input/output data. Default: `'all'` |
195
+
196
+ **Returns:** `Step` (or `StepWithoutData` when `resolveData: 'none'`)
197
+
198
+ ### steps.list()
199
+
200
+ ```typescript lineNumbers
201
+ const result = await world.steps.list({ // [!code highlight]
202
+ runId,
203
+ pagination: { cursor },
204
+ }); // [!code highlight]
205
+ ```
206
+
207
+ | Parameter | Type | Description |
208
+ |-----------|------|-------------|
209
+ | `params.runId` | `string` | Filter steps by run ID |
210
+ | `params.pagination.cursor` | `string` | Cursor for the next page |
211
+ | `params.resolveData` | `'all' \| 'none'` | Whether to include input/output data |
212
+
213
+ **Returns:** `{ data: Step[], cursor?: string }`
214
+
215
+ ### Step Type
216
+
217
+ | Field | Type | Description |
218
+ |-------|------|-------------|
219
+ | `runId` | `string` | Parent workflow run ID |
220
+ | `stepId` | `string` | Unique step identifier |
221
+ | `stepName` | `string` | Machine-readable step identifier |
222
+ | `status` | `string` | `'running'`, `'completed'`, `'failed'` |
223
+ | `input` | `any` | Step input data (when `resolveData: 'all'`) |
224
+ | `output` | `any` | Step output data (when `resolveData: 'all'`) |
225
+ | `error` | `any` | Error data if the step failed |
226
+ | `attempt` | `number` | Current retry attempt number |
227
+ | `startedAt` | `string` | ISO timestamp when the step started |
228
+ | `completedAt` | `string \| null` | ISO timestamp when the step completed |
229
+ | `retryAfter` | `string \| null` | ISO timestamp for next retry attempt |
230
+
231
+ <Callout type="info">
232
+ Step I/O is serialized using the [devalue](https://github.com/Rich-Harris/devalue) format. Use `hydrateResourceIO()` from `workflow/observability` to deserialize it for display. See [Observability Utilities](/docs/api-reference/workflow-api/world/observability).
233
+ </Callout>
234
+
235
+ <Callout type="warn">
236
+ `stepName` is a machine-readable identifier like `step//./src/workflows/order//processPayment`. Use `parseStepName()` from `workflow/observability` to extract the `shortName` for UI display.
237
+ </Callout>
238
+
239
+ ---
240
+
241
+ ## world.hooks
242
+
243
+ Materialized from hook events. Hooks are pause points in workflows that wait for external input. Use this interface to look up hooks by ID or token, inspect metadata, and build UIs for pending approvals.
244
+
245
+ ### hooks.get()
246
+
247
+ ```typescript lineNumbers
248
+ const hook = await world.hooks.get(hookId); // [!code highlight]
249
+ ```
250
+
251
+ | Parameter | Type | Description |
252
+ |-----------|------|-------------|
253
+ | `hookId` | `string` | The hook ID |
254
+
255
+ **Returns:** `Hook`
256
+
257
+ ### hooks.getByToken()
258
+
259
+ Look up a hook by its token. Useful in webhook resume flows where you receive a token in the callback URL.
260
+
261
+ ```typescript lineNumbers
262
+ const hook = await world.hooks.getByToken(token); // [!code highlight]
263
+ ```
264
+
265
+ | Parameter | Type | Description |
266
+ |-----------|------|-------------|
267
+ | `token` | `string` | The hook token |
268
+
269
+ **Returns:** `Hook`
270
+
271
+ ### hooks.list()
272
+
273
+ ```typescript lineNumbers
274
+ const result = await world.hooks.list({ // [!code highlight]
275
+ pagination: { cursor },
276
+ }); // [!code highlight]
277
+ ```
278
+
279
+ | Parameter | Type | Description |
280
+ |-----------|------|-------------|
281
+ | `params.pagination.cursor` | `string` | Cursor for the next page |
282
+
283
+ **Returns:** `{ data: Hook[], cursor?: string }`
284
+
285
+ ### Hook Type
286
+
287
+ | Field | Type | Description |
288
+ |-------|------|-------------|
289
+ | `runId` | `string` | Parent workflow run ID |
290
+ | `hookId` | `string` | Unique hook identifier |
291
+ | `token` | `string` | Hook token for resuming |
292
+ | `ownerId` | `string` | Owner (team/user) ID |
293
+ | `projectId` | `string` | Project ID |
294
+ | `environment` | `string` | Deployment environment |
295
+ | `metadata` | `object` | Custom metadata attached to the hook |
296
+ | `isWebhook` | `boolean` | Whether this is a webhook-style hook |
297
+
298
+ ---
299
+
300
+ ## Examples
301
+
302
+ ### List Runs with Pagination
303
+
304
+ ```typescript lineNumbers
305
+ import { getWorld } from "workflow/runtime";
306
+
307
+ const world = getWorld();
308
+ let cursor: string | undefined;
309
+
310
+ const runs = await world.runs.list({ // [!code highlight]
311
+ pagination: { cursor },
312
+ }); // [!code highlight]
313
+
314
+ cursor = runs.cursor; // pass to next call for pagination
315
+ ```
316
+
317
+ ### Get a Run — Full Data vs. Metadata Only
318
+
319
+ ```typescript lineNumbers
320
+ import { getWorld } from "workflow/runtime";
321
+
322
+ const world = getWorld();
323
+
324
+ // Full data (default) — includes serialized input/output
325
+ const run = await world.runs.get(runId); // [!code highlight]
326
+
327
+ // Metadata only — lighter, no I/O loaded
328
+ const lightweight = await world.runs.get(runId, { // [!code highlight]
329
+ resolveData: "none", // [!code highlight]
330
+ }); // [!code highlight]
331
+ ```
332
+
333
+ ### List Steps for a Progress Dashboard
334
+
335
+ ```typescript lineNumbers
336
+ import { getWorld } from "workflow/runtime";
337
+ import { parseStepName } from "workflow/observability"; // [!code highlight]
338
+
339
+ const world = getWorld();
340
+ const steps = await world.steps.list({ // [!code highlight]
341
+ runId,
342
+ resolveData: "none",
343
+ }); // [!code highlight]
344
+
345
+ const progress = steps.data.map((step) => {
346
+ const parsed = parseStepName(step.stepName); // [!code highlight]
347
+ return {
348
+ stepId: step.stepId,
349
+ displayName: parsed?.shortName ?? step.stepName, // [!code highlight]
350
+ status: step.status,
351
+ };
352
+ });
353
+ ```
354
+
355
+ ### Hydrate Step I/O
356
+
357
+ ```typescript lineNumbers
358
+ import { getWorld } from "workflow/runtime";
359
+ import { hydrateResourceIO, observabilityRevivers } from "workflow/observability"; // [!code highlight]
360
+
361
+ const world = getWorld();
362
+ const step = await world.steps.get(runId, stepId); // [!code highlight]
363
+ const hydrated = hydrateResourceIO(step, observabilityRevivers); // [!code highlight]
364
+ console.log(hydrated.input, hydrated.output);
365
+ ```
366
+
367
+ ### Cancel a Run
368
+
369
+ ```typescript lineNumbers
370
+ import { getWorld } from "workflow/runtime";
371
+
372
+ const world = getWorld();
373
+ await world.events.create(runId, { // [!code highlight]
374
+ eventType: "run_cancelled", // [!code highlight]
375
+ }); // [!code highlight]
376
+ ```
377
+
378
+ ### Look Up Hook by Token
379
+
380
+ ```typescript lineNumbers
381
+ import { getWorld } from "workflow/runtime";
382
+
383
+ const world = getWorld();
384
+ const hook = await world.hooks.getByToken(token); // [!code highlight]
385
+ console.log(hook.runId, hook.metadata); // [!code highlight]
386
+ ```
387
+
388
+ ### List Events for Audit Trail
389
+
390
+ ```typescript lineNumbers
391
+ import { getWorld } from "workflow/runtime";
392
+
393
+ const world = getWorld();
394
+ const events = await world.events.list({ runId }); // [!code highlight]
395
+
396
+ for (const event of events.data) {
397
+ console.log(event.eventType, event.createdAt);
398
+ }
399
+ ```
400
+
401
+ ## Related
402
+
403
+ - [Event Sourcing](/docs/how-it-works/event-sourcing) — How the event log powers workflow replay and state
404
+ - [getRun()](/docs/api-reference/workflow-api/get-run) — Higher-level API for working with individual runs
405
+ - [Observability Utilities](/docs/api-reference/workflow-api/world/observability) — Hydrate step I/O, parse display names, decrypt data
406
+ - [resumeHook()](/docs/api-reference/workflow-api/resume-hook) — Resume a workflow by sending a payload to a hook
407
+ - [Hooks](/docs/foundations/hooks) — Core concepts for hooks and pause points
408
+ - [Workflows and Steps](/docs/foundations/workflows-and-steps) — Core concepts for steps
@@ -2,7 +2,7 @@
2
2
  title: Streams
3
3
  description: Read, write, and manage real-time data streams for workflow runs.
4
4
  type: reference
5
- summary: "Methods: writeToStream(), readFromStream(), closeStream(), listStreamsByRunId(). Stream methods live directly on the world object."
5
+ summary: "Methods: writeToStream(), writeToStreamMulti(), readFromStream(), closeStream(), listStreamsByRunId(), getStreamChunks(), getStreamInfo(). Stream methods live directly on the world object."
6
6
  prerequisites:
7
7
  - /docs/api-reference/workflow-api/get-world
8
8
  related:
@@ -10,9 +10,12 @@ related:
10
10
  - /docs/api-reference/workflow/get-writable
11
11
  keywords:
12
12
  - writeToStream
13
+ - writeToStreamMulti
13
14
  - readFromStream
14
15
  - closeStream
15
16
  - listStreamsByRunId
17
+ - getStreamChunks
18
+ - getStreamInfo
16
19
  - Streamer interface
17
20
  - real-time streaming
18
21
  - stream lifecycle
@@ -37,7 +40,7 @@ const world = getWorld(); // [!code highlight]
37
40
 
38
41
  ### writeToStream()
39
42
 
40
- Write a data chunk to a named stream for a workflow run.
43
+ Write a data chunk to a named stream.
41
44
 
42
45
  ```typescript lineNumbers
43
46
  await world.writeToStream("default", runId, chunk); // [!code highlight]
@@ -49,11 +52,27 @@ await world.writeToStream("default", runId, chunk); // [!code highlight]
49
52
  |-----------|------|-------------|
50
53
  | `name` | `string` | The stream name |
51
54
  | `runId` | `string` | The workflow run ID |
52
- | `chunk` | `string \| Uint8Array` | Data to write to the stream |
55
+ | `chunk` | `string \| Uint8Array` | Data to write |
56
+
57
+ ### writeToStreamMulti()
58
+
59
+ Write multiple chunks in a single operation. Optional optimization — not all World implementations support it. Falls back to sequential `writeToStream()` calls if unavailable.
60
+
61
+ ```typescript lineNumbers
62
+ await world.writeToStreamMulti?.("default", runId, [chunk1, chunk2]); // [!code highlight]
63
+ ```
64
+
65
+ **Parameters:**
66
+
67
+ | Parameter | Type | Description |
68
+ |-----------|------|-------------|
69
+ | `name` | `string` | The stream name |
70
+ | `runId` | `string` | The workflow run ID |
71
+ | `chunks` | `(string \| Uint8Array)[]` | Chunks to write, in order |
53
72
 
54
73
  ### readFromStream()
55
74
 
56
- Read data from a named stream as a `ReadableStream`. Returns a live stream that waits for new chunks in real time.
75
+ Read data from a named stream as a live `ReadableStream` that waits for new chunks in real time.
57
76
 
58
77
  ```typescript lineNumbers
59
78
  const readable = await world.readFromStream("default"); // [!code highlight]
@@ -64,7 +83,7 @@ const readable = await world.readFromStream("default"); // [!code highlight]
64
83
  | Parameter | Type | Description |
65
84
  |-----------|------|-------------|
66
85
  | `name` | `string` | The stream name |
67
- | `startIndex` | `number` | Optional starting index for partial reads. Negative values read from the tail (e.g. `-3` starts 3 chunks from the end). |
86
+ | `startIndex` | `number` | Optional. Positive values skip chunks from the start (0-based). Negative values read from the tail (e.g. `-3` starts 3 chunks from the end). Clamped to 0. |
68
87
 
69
88
  **Returns:** `ReadableStream<Uint8Array>`
70
89
 
@@ -97,31 +116,62 @@ const streamNames = await world.listStreamsByRunId(runId); // [!code highlight]
97
116
  |-----------|------|-------------|
98
117
  | `runId` | `string` | The workflow run ID |
99
118
 
100
- **Returns:** `string[]` — Array of stream names
119
+ **Returns:** `string[]`
101
120
 
102
- ## Examples
121
+ ### getStreamChunks()
103
122
 
104
- ### List All Streams for a Workflow Run
123
+ Fetch stream chunks with cursor-based pagination. Unlike `readFromStream()` (which returns a live `ReadableStream`), this returns a snapshot of currently available chunks.
105
124
 
106
125
  ```typescript lineNumbers
107
- // app/api/workflow-streams/route.ts
108
- import { getWorld } from "workflow/runtime";
126
+ const result = await world.getStreamChunks("default", runId, { // [!code highlight]
127
+ limit: 50,
128
+ }); // [!code highlight]
129
+ // result.data: StreamChunk[], result.cursor, result.hasMore, result.done
130
+ ```
109
131
 
110
- export async function GET(req: Request) {
111
- const url = new URL(req.url);
112
- const runId = url.searchParams.get("runId");
132
+ **Parameters:**
113
133
 
114
- if (!runId) {
115
- return Response.json({ error: "runId required" }, { status: 400 });
116
- }
134
+ | Parameter | Type | Description |
135
+ |-----------|------|-------------|
136
+ | `name` | `string` | The stream name |
137
+ | `runId` | `string` | The workflow run ID |
138
+ | `options.limit` | `number` | Max chunks per page (default: 100, max: 1000) |
139
+ | `options.cursor` | `string` | Cursor from a previous response |
117
140
 
118
- const world = getWorld();
119
- const streamNames = await world.listStreamsByRunId(runId); // [!code highlight]
141
+ **Returns:** `StreamChunksResponse`
120
142
 
121
- return Response.json({ streams: streamNames });
122
- }
143
+ | Field | Type | Description |
144
+ |-------|------|-------------|
145
+ | `data` | `StreamChunk[]` | Chunks in index order. Each has `index` (0-based) and `data` (`Uint8Array`). |
146
+ | `cursor` | `string \| null` | Cursor for the next page |
147
+ | `hasMore` | `boolean` | Whether more pages of already-written chunks exist |
148
+ | `done` | `boolean` | Whether the stream is fully closed. When `false`, new chunks may appear in future requests even after `hasMore` is `false`. |
149
+
150
+ ### getStreamInfo()
151
+
152
+ Retrieve lightweight metadata about a stream without fetching chunks.
153
+
154
+ ```typescript lineNumbers
155
+ const info = await world.getStreamInfo("default", runId); // [!code highlight]
156
+ // info.tailIndex: last chunk index (-1 if empty), info.done: whether stream is closed
123
157
  ```
124
158
 
159
+ **Parameters:**
160
+
161
+ | Parameter | Type | Description |
162
+ |-----------|------|-------------|
163
+ | `name` | `string` | The stream name |
164
+ | `runId` | `string` | The workflow run ID |
165
+
166
+ **Returns:** `StreamInfoResponse`
167
+
168
+ | Field | Type | Description |
169
+ |-------|------|-------------|
170
+ | `tailIndex` | `number` | Index of the last known chunk (0-based). `-1` when no chunks have been written. |
171
+ | `done` | `boolean` | Whether the stream is fully complete (closed). |
172
+
173
+ ## Examples
174
+
125
175
  ### Read a Stream as a Response
126
176
 
127
177
  ```typescript lineNumbers
@@ -130,12 +180,7 @@ import { getWorld } from "workflow/runtime";
130
180
 
131
181
  export async function GET(req: Request) {
132
182
  const url = new URL(req.url);
133
- const streamName = url.searchParams.get("name");
134
-
135
- if (!streamName) {
136
- return Response.json({ error: "name required" }, { status: 400 });
137
- }
138
-
183
+ const streamName = url.searchParams.get("name") ?? "default";
139
184
  const world = getWorld();
140
185
  const readable = await world.readFromStream(streamName); // [!code highlight]
141
186
 
@@ -145,8 +190,25 @@ export async function GET(req: Request) {
145
190
  }
146
191
  ```
147
192
 
193
+ ### Paginate Through Stream Chunks
194
+
195
+ ```typescript lineNumbers
196
+ import { getWorld } from "workflow/runtime";
197
+
198
+ const world = getWorld();
199
+ let cursor: string | undefined;
200
+
201
+ do {
202
+ const result = await world.getStreamChunks("default", runId, { cursor }); // [!code highlight]
203
+ for (const chunk of result.data) {
204
+ console.log(`Chunk ${chunk.index}:`, chunk.data);
205
+ }
206
+ cursor = result.cursor ?? undefined;
207
+ } while (cursor);
208
+ ```
209
+
148
210
  ## Related
149
211
 
150
212
  - [Streaming](/docs/foundations/streaming) — Core concepts for streaming data from workflows
151
213
  - [getWritable()](/docs/api-reference/workflow/get-writable) — The standard way to write to streams from within steps
152
- - [world.runs](/docs/api-reference/workflow-api/world/runs) — List runs that may have associated streams
214
+ - [Storage](/docs/api-reference/workflow-api/world/storage) — Query runs, steps, hooks, and events
@@ -190,7 +190,7 @@ async function doublePoint(point: Point) {
190
190
  ### Requirements
191
191
 
192
192
  <Callout type="warn">
193
- Both methods must be implemented as **static** methods on the class. Instance methods are not supported.
193
+ `WORKFLOW_SERIALIZE` and `WORKFLOW_DESERIALIZE` must be implemented as **static** methods on the class. Defining them as instance methods is not supported.
194
194
  </Callout>
195
195
 
196
196
  - The data returned by `WORKFLOW_SERIALIZE` must itself be serializable (see [Supported Serializable Types](#supported-serializable-types))
@@ -205,9 +205,9 @@ The `WORKFLOW_SERIALIZE` and `WORKFLOW_DESERIALIZE` methods run inside the workf
205
205
  Keep these methods simple and focused on data transformation only.
206
206
  </Callout>
207
207
 
208
- ### Complex Example
208
+ ### Instance Methods as Steps
209
209
 
210
- A class that uses Node.js APIs or other non-deterministic operations cannot be used directly inside a workflow function. The recommended approach is to make the class workflow-compatible by adding `"use step"` to its instance methods. The SWC compiler will strip the method bodies from the workflow bundle and replace them with proxy functions that invoke the method as a step — with full Node.js runtime access. The `this` context (the class instance) is automatically serialized and deserialized across the workflow/step boundary.
210
+ In practice, many classes have methods that need Node.js APIs, perform network calls, or interact with databases — operations that are not allowed in the `"use workflow"` execution context. You can make these methods workflow-compatible by adding `"use step"` to them. The SWC compiler will strip the method bodies from the workflow bundle and replace them with proxy functions that invoke the method as a step — with full Node.js runtime access. The `this` context (the class instance) is automatically serialized and deserialized across the workflow/step boundary.
211
211
 
212
212
  This requires the class to implement `WORKFLOW_SERIALIZE` and `WORKFLOW_DESERIALIZE`, so that the instance can be passed to the step execution context.
213
213
 
@@ -9,7 +9,7 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
- This guide will walk through setting up your first workflow in an Astro app. Along the way, you'll learn more about the concepts that are fundamental to using the development kit in your own projects.
12
+ This guide will walk through setting up your first workflow in an Astro app. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.
13
13
 
14
14
  ---
15
15
 
@@ -9,7 +9,7 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
- This guide will walk through setting up your first workflow in a Express app. Along the way, you'll learn more about the concepts that are fundamental to using the development kit in your own projects.
12
+ This guide will walk through setting up your first workflow in an Express app. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.
13
13
 
14
14
  ---
15
15
 
@@ -9,7 +9,7 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
- This guide will walk through setting up your first workflow in a Fastify app. Along the way, you'll learn more about the concepts that are fundamental to using the development kit in your own projects.
12
+ This guide will walk through setting up your first workflow in a Fastify app. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.
13
13
 
14
14
  ---
15
15
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Hono
3
- description: This guide will walk through setting up your first workflow in a Hono app. Along the way, you'll learn more about the concepts that are fundamental to using the development kit in your own projects.
3
+ description: This guide will walk through setting up your first workflow in a Hono app. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.
4
4
  type: guide
5
5
  summary: Set up Workflow SDK in a Hono app.
6
6
  prerequisites:
@@ -9,7 +9,7 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
- This guide will walk through setting up your first workflow in a NestJS app. Along the way, you'll learn more about the concepts that are fundamental to using the development kit in your own projects.
12
+ This guide will walk through setting up your first workflow in a NestJS app. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.
13
13
 
14
14
  <Callout>
15
15
  NestJS integration is experimental and not yet supported for deployment to Vercel.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Next.js
3
- description: This guide will walk through setting up your first workflow in a Next.js app. Along the way, you'll learn more about the concepts that are fundamental to using the development kit in your own projects.
3
+ description: This guide will walk through setting up your first workflow in a Next.js app. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.
4
4
  type: guide
5
5
  summary: Set up Workflow SDK in a Next.js app.
6
6
  prerequisites:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Nitro
3
- description: This guide will walk through setting up your first workflow in a Nitro v3 project. Along the way, you'll learn more about the concepts that are fundamental to using the development kit in your own projects.
3
+ description: This guide will walk through setting up your first workflow in a Nitro v3 project. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.
4
4
  type: guide
5
5
  summary: Set up Workflow SDK in a Nitro app.
6
6
  prerequisites: