workflow 5.0.0-beta.5 → 5.0.0-beta.50
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.
- package/README.md +68 -23
- package/dist/api-workflow.d.ts +1 -1
- package/dist/api-workflow.d.ts.map +1 -1
- package/dist/api-workflow.js +1 -1
- package/dist/api.d.ts +3 -3
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +5 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/internal/builtins.d.ts +20 -3
- package/dist/internal/builtins.d.ts.map +1 -1
- package/dist/internal/builtins.js +68 -4
- package/dist/internal/errors.d.ts +1 -1
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/errors.js +2 -2
- package/dist/nest-builder.d.ts +2 -0
- package/dist/nest-builder.d.ts.map +1 -0
- package/dist/nest-builder.js +2 -0
- package/dist/nest-vercel-builder.d.ts +2 -0
- package/dist/nest-vercel-builder.d.ts.map +1 -0
- package/dist/nest-vercel-builder.js +2 -0
- package/dist/observability.d.ts +1 -1
- package/dist/observability.js +2 -2
- package/dist/runtime.d.ts +2 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +4 -1
- package/docs/ai/chat-session-modeling.mdx +29 -26
- package/docs/ai/defining-tools.mdx +6 -7
- package/docs/ai/human-in-the-loop.mdx +11 -11
- package/docs/ai/index.mdx +50 -45
- package/docs/ai/message-queueing.mdx +16 -16
- package/docs/ai/meta.json +1 -0
- package/docs/ai/resumable-streams.mdx +40 -28
- package/docs/ai/sleep-and-delays.mdx +10 -10
- package/docs/ai/streaming-updates-from-tools.mdx +6 -6
- package/docs/api-reference/index.mdx +24 -0
- package/docs/api-reference/meta.json +8 -0
- package/docs/api-reference/vitest/index.mdx +9 -15
- package/docs/api-reference/workflow/create-hook.mdx +89 -10
- package/docs/api-reference/workflow/create-webhook.mdx +16 -15
- package/docs/api-reference/workflow/define-hook.mdx +35 -33
- package/docs/api-reference/workflow/fatal-error.mdx +30 -8
- package/docs/api-reference/workflow/fetch.mdx +14 -10
- package/docs/api-reference/workflow/get-step-metadata.mdx +2 -2
- package/docs/api-reference/workflow/get-workflow-metadata.mdx +3 -3
- package/docs/api-reference/workflow/get-writable.mdx +7 -7
- package/docs/api-reference/workflow/index.mdx +4 -1
- package/docs/api-reference/workflow/retryable-error.mdx +1 -1
- package/docs/api-reference/workflow/set-attributes.mdx +61 -0
- package/docs/api-reference/workflow/sleep.mdx +4 -4
- package/docs/api-reference/workflow-ai/durable-agent.mdx +48 -86
- package/docs/api-reference/workflow-ai/index.mdx +3 -3
- package/docs/api-reference/workflow-ai/workflow-chat-transport.mdx +67 -24
- package/docs/api-reference/workflow-api/get-hook-by-token.mdx +26 -12
- package/docs/api-reference/workflow-api/get-run.mdx +43 -8
- package/docs/api-reference/workflow-api/index.mdx +6 -10
- package/docs/api-reference/workflow-api/resume-hook.mdx +73 -12
- package/docs/api-reference/workflow-api/resume-webhook.mdx +11 -9
- package/docs/api-reference/workflow-api/start.mdx +60 -13
- package/docs/api-reference/workflow-astro/index.mdx +18 -0
- package/docs/api-reference/workflow-astro/meta.json +4 -0
- package/docs/api-reference/workflow-astro/workflow.mdx +45 -0
- package/docs/api-reference/workflow-errors/entity-conflict-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/hook-conflict-error.mdx +60 -0
- package/docs/api-reference/workflow-errors/hook-not-found-error.mdx +8 -8
- package/docs/api-reference/workflow-errors/index.mdx +88 -0
- package/docs/api-reference/workflow-errors/meta.json +6 -0
- package/docs/api-reference/workflow-errors/precondition-failed-error.mdx +68 -0
- package/docs/api-reference/workflow-errors/run-expired-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/run-not-supported-error.mdx +58 -0
- package/docs/api-reference/workflow-errors/step-not-registered-error.mdx +5 -5
- package/docs/api-reference/workflow-errors/throttle-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/too-early-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/workflow-error.mdx +52 -0
- package/docs/api-reference/workflow-errors/workflow-not-registered-error.mdx +5 -6
- package/docs/api-reference/workflow-errors/workflow-run-cancelled-error.mdx +6 -6
- package/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx +5 -5
- package/docs/api-reference/workflow-errors/workflow-run-not-completed-error.mdx +58 -0
- package/docs/api-reference/workflow-errors/workflow-run-not-found-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/workflow-runtime-error.mdx +58 -0
- package/docs/api-reference/workflow-errors/workflow-world-error.mdx +8 -8
- package/docs/api-reference/workflow-globals.mdx +14 -10
- package/docs/api-reference/workflow-nest/configure-workflow-controller.mdx +33 -0
- package/docs/api-reference/workflow-nest/index.mdx +31 -0
- package/docs/api-reference/workflow-nest/meta.json +9 -0
- package/docs/api-reference/workflow-nest/nest-local-builder.mdx +64 -0
- package/docs/api-reference/workflow-nest/workflow-controller.mdx +40 -0
- package/docs/api-reference/workflow-nest/workflow-module.mdx +74 -0
- package/docs/api-reference/workflow-next/with-workflow.mdx +39 -17
- package/docs/api-reference/workflow-nitro/index.mdx +60 -0
- package/docs/api-reference/workflow-nuxt/index.mdx +48 -0
- package/docs/api-reference/workflow-observability/hydrate-data.mdx +35 -0
- package/docs/api-reference/workflow-observability/hydrate-resource-io.mdx +62 -0
- package/docs/api-reference/workflow-observability/index.mdx +62 -0
- package/docs/api-reference/workflow-observability/meta.json +11 -0
- package/docs/api-reference/workflow-observability/observability-revivers.mdx +50 -0
- package/docs/api-reference/workflow-observability/parse-class-name.mdx +41 -0
- package/docs/api-reference/workflow-observability/parse-step-name.mdx +40 -0
- package/docs/api-reference/workflow-observability/parse-workflow-name.mdx +55 -0
- package/docs/api-reference/workflow-runtime/create-world.mdx +39 -0
- package/docs/api-reference/workflow-runtime/get-world-handlers.mdx +44 -0
- package/docs/api-reference/{workflow-api → workflow-runtime}/get-world.mdx +11 -14
- package/docs/api-reference/workflow-runtime/health-check.mdx +50 -0
- package/docs/api-reference/workflow-runtime/index.mdx +41 -0
- package/docs/api-reference/workflow-runtime/meta.json +12 -0
- package/docs/api-reference/workflow-runtime/set-world.mdx +51 -0
- package/docs/api-reference/workflow-runtime/workflow-entrypoint.mdx +43 -0
- package/docs/api-reference/workflow-runtime/world/analytics.mdx +315 -0
- package/docs/api-reference/workflow-runtime/world/index.mdx +60 -0
- package/docs/api-reference/workflow-runtime/world/meta.json +4 -0
- package/docs/api-reference/workflow-runtime/world/queue.mdx +88 -0
- package/docs/api-reference/{workflow-api → workflow-runtime}/world/storage.mdx +98 -34
- package/docs/api-reference/{workflow-api → workflow-runtime}/world/streams.mdx +8 -8
- package/docs/api-reference/workflow-serde/index.mdx +1 -2
- package/docs/api-reference/workflow-serde/workflow-deserialize.mdx +3 -4
- package/docs/api-reference/workflow-serde/workflow-serialize.mdx +8 -8
- package/docs/api-reference/workflow-sveltekit/index.mdx +18 -0
- package/docs/api-reference/workflow-sveltekit/meta.json +4 -0
- package/docs/api-reference/workflow-sveltekit/workflow-plugin.mdx +42 -0
- package/docs/api-reference/workflow-vite/index.mdx +18 -0
- package/docs/api-reference/workflow-vite/meta.json +4 -0
- package/docs/api-reference/workflow-vite/workflow.mdx +48 -0
- package/docs/changelog/attributes-mvp.mdx +380 -0
- package/docs/changelog/batched-event-writes.mdx +79 -0
- package/docs/changelog/eager-processing.mdx +110 -436
- package/docs/changelog/index.mdx +4 -2
- package/docs/changelog/lazy-event-creation.md +127 -0
- package/docs/changelog/lazy-hook-resume.mdx +78 -0
- package/docs/changelog/meta.json +11 -1
- package/docs/changelog/resilient-resume.mdx +32 -0
- package/docs/changelog/resilient-start.mdx +33 -285
- package/docs/changelog/step-message-ownership.mdx +360 -0
- package/docs/changelog/turbo-mode.md +87 -0
- package/docs/comparisons/index.mdx +66 -0
- package/docs/comparisons/meta.json +11 -0
- package/docs/comparisons/workflow-sdk-vs-aws-agentcore.mdx +55 -0
- package/docs/comparisons/workflow-sdk-vs-aws-step-functions.mdx +111 -0
- package/docs/comparisons/workflow-sdk-vs-cloudflare-workflows.mdx +71 -0
- package/docs/comparisons/workflow-sdk-vs-inngest.mdx +102 -0
- package/docs/comparisons/workflow-sdk-vs-temporal.mdx +123 -0
- package/docs/comparisons/workflow-sdk-vs-trigger-dev.mdx +104 -0
- package/docs/configuration/build-and-diagnostics.mdx +70 -0
- package/docs/configuration/cli-and-web-ui.mdx +241 -0
- package/docs/configuration/framework-options.mdx +165 -0
- package/docs/configuration/index.mdx +32 -0
- package/docs/configuration/meta.json +12 -0
- package/docs/configuration/runtime-tuning.mdx +376 -0
- package/docs/configuration/worlds.mdx +313 -0
- package/docs/cookbook/advanced/child-workflows.mdx +211 -264
- package/docs/cookbook/advanced/meta.json +6 -1
- package/docs/cookbook/advanced/publishing-libraries.mdx +65 -56
- package/docs/cookbook/advanced/serializable-steps.mdx +28 -20
- package/docs/cookbook/advanced/upgrading-workflows.mdx +199 -0
- package/docs/cookbook/agent-patterns/agent-cancellation.mdx +27 -19
- package/docs/cookbook/agent-patterns/durable-agent.mdx +14 -142
- package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +30 -22
- package/docs/cookbook/common-patterns/batching.mdx +18 -14
- package/docs/cookbook/common-patterns/idempotency.mdx +41 -53
- package/docs/cookbook/common-patterns/rate-limiting.mdx +8 -4
- package/docs/cookbook/common-patterns/saga.mdx +23 -19
- package/docs/cookbook/common-patterns/scheduling.mdx +34 -22
- package/docs/cookbook/common-patterns/sequential-and-parallel.mdx +29 -25
- package/docs/cookbook/common-patterns/timeouts.mdx +26 -21
- package/docs/cookbook/common-patterns/webhooks.mdx +10 -6
- package/docs/cookbook/common-patterns/workflow-composition.mdx +30 -27
- package/docs/cookbook/index.mdx +22 -21
- package/docs/cookbook/integrations/ai-sdk.mdx +85 -47
- package/docs/cookbook/integrations/chat-sdk.mdx +50 -33
- package/docs/cookbook/integrations/sandbox.mdx +62 -45
- package/docs/deploying.mdx +95 -0
- package/docs/errors/abort-signal-timeout-in-workflow.mdx +16 -12
- package/docs/errors/corrupted-event-log.mdx +29 -18
- package/docs/errors/deployment-mismatch.mdx +71 -0
- package/docs/errors/fetch-in-workflow.mdx +11 -7
- package/docs/errors/hook-conflict.mdx +69 -13
- package/docs/errors/index.mdx +2 -36
- package/docs/errors/node-js-module-in-workflow.mdx +9 -5
- package/docs/errors/replay-divergence.mdx +27 -0
- package/docs/errors/run-expired.mdx +85 -0
- package/docs/errors/runtime-decryption-failed.mdx +77 -0
- package/docs/errors/serialization-failed.mdx +44 -12
- package/docs/errors/start-invalid-workflow-function.mdx +9 -5
- package/docs/errors/step-executed-multiple-times.mdx +23 -0
- package/docs/errors/step-not-registered.mdx +6 -6
- package/docs/errors/timeout-in-workflow.mdx +12 -8
- package/docs/errors/webhook-invalid-respond-with-value.mdx +18 -18
- package/docs/errors/webhook-response-not-sent.mdx +20 -16
- package/docs/errors/workflow-not-registered.mdx +5 -5
- package/docs/foundations/cancellation.mdx +31 -32
- package/docs/foundations/errors-and-retries.mdx +42 -11
- package/docs/foundations/hooks.mdx +64 -35
- package/docs/foundations/idempotency.mdx +244 -12
- package/docs/foundations/index.mdx +1 -23
- package/docs/foundations/meta.json +2 -1
- package/docs/foundations/serialization.mdx +21 -22
- package/docs/foundations/starting-workflows.mdx +106 -30
- package/docs/foundations/streaming.mdx +107 -59
- package/docs/foundations/versioning.mdx +263 -0
- package/docs/foundations/workflows-and-steps.mdx +9 -9
- package/docs/getting-started/astro.mdx +22 -18
- package/docs/getting-started/express.mdx +15 -11
- package/docs/getting-started/fastify.mdx +15 -11
- package/docs/getting-started/hono.mdx +15 -11
- package/docs/getting-started/index.mdx +10 -3
- package/docs/getting-started/meta.json +3 -1
- package/docs/getting-started/nestjs.mdx +87 -20
- package/docs/getting-started/next.mdx +22 -16
- package/docs/getting-started/nitro.mdx +22 -18
- package/docs/getting-started/nuxt.mdx +15 -11
- package/docs/getting-started/python.mdx +135 -40
- package/docs/getting-started/react-router/index.mdx +33 -0
- package/docs/getting-started/react-router/meta.json +5 -0
- package/docs/getting-started/react-router/v7.mdx +237 -0
- package/docs/getting-started/react-router/v8.mdx +232 -0
- package/docs/getting-started/sveltekit.mdx +20 -16
- package/docs/getting-started/tanstack-start.mdx +17 -13
- package/docs/getting-started/vite.mdx +15 -11
- package/docs/how-it-works/cancellation.mdx +63 -63
- package/docs/how-it-works/code-transform.mdx +83 -67
- package/docs/how-it-works/encryption.mdx +30 -26
- package/docs/how-it-works/event-sourcing.mdx +98 -34
- package/docs/how-it-works/framework-integrations.mdx +96 -337
- package/docs/how-it-works/understanding-directives.mdx +22 -22
- package/docs/internal/index.mdx +6 -4
- package/docs/internal/meta.json +6 -1
- package/docs/internal/nitro-native-build.mdx +38 -0
- package/docs/internal/nitro-web-ui.mdx +24 -0
- package/docs/internal/serializable-abort-controller.mdx +7 -7
- package/docs/meta.json +3 -2
- package/docs/observability/attributes.mdx +134 -0
- package/docs/observability/index.mdx +32 -10
- package/docs/observability/meta.json +1 -1
- package/docs/observability/retention.mdx +93 -0
- package/docs/observability/tracing.mdx +124 -0
- package/docs/testing/index.mdx +36 -36
- package/docs/testing/server-based.mdx +10 -10
- package/docs/whats-new.mdx +186 -0
- package/package.json +17 -14
- package/docs/api-reference/workflow-api/world/index.mdx +0 -58
- package/docs/api-reference/workflow-api/world/meta.json +0 -4
- package/docs/api-reference/workflow-api/world/observability.mdx +0 -164
- package/docs/api-reference/workflow-api/world/queue.mdx +0 -86
- package/docs/deploying/building-a-world.mdx +0 -251
- package/docs/deploying/index.mdx +0 -95
- package/docs/deploying/meta.json +0 -4
- package/docs/deploying/world/local-world.mdx +0 -84
- package/docs/deploying/world/meta.json +0 -4
- package/docs/deploying/world/postgres-world.mdx +0 -224
- package/docs/deploying/world/vercel-world.mdx +0 -179
- package/docs/migration-guides/index.mdx +0 -34
- package/docs/migration-guides/meta.json +0 -9
- package/docs/migration-guides/migrating-from-aws-step-functions.mdx +0 -363
- package/docs/migration-guides/migrating-from-inngest.mdx +0 -314
- package/docs/migration-guides/migrating-from-temporal.mdx +0 -318
- package/docs/migration-guides/migrating-from-trigger-dev.mdx +0 -337
|
@@ -21,7 +21,7 @@ This page explores how directives enable this execution model and the design pri
|
|
|
21
21
|
|
|
22
22
|
To understand how directives work, let's first understand what workflows and steps are in the Workflow SDK.
|
|
23
23
|
|
|
24
|
-
## Workflows and
|
|
24
|
+
## Workflows and steps primer
|
|
25
25
|
|
|
26
26
|
The Workflow SDK has two types of functions:
|
|
27
27
|
|
|
@@ -58,7 +58,7 @@ export async function onboardUser(userId: string) {
|
|
|
58
58
|
**The key insight:** Workflows resume from suspension by replaying their code using cached step results from the [event log](/docs/how-it-works/event-sourcing). When a step like `await fetchUserData(userId)` is called:
|
|
59
59
|
|
|
60
60
|
- **If already executed:** Returns the cached result immediately from the event log
|
|
61
|
-
- **If not yet executed:** Suspends the workflow,
|
|
61
|
+
- **If not yet executed:** Suspends the workflow, executes the step (usually inline in the same invocation, or through the queue when the invocation runs out of inline budget or nears its timeout), then resumes with the result
|
|
62
62
|
|
|
63
63
|
This replay mechanism requires deterministic code. If `Math.random()` weren't seeded, the first execution might return `0.7` (sending the email) but replay might return `0.3` (skipping it), thus breaking resumption. The Workflow SDK sandbox provides seeded `Math.random()` and `Date` to ensure consistent behavior across replays.
|
|
64
64
|
|
|
@@ -66,9 +66,9 @@ This replay mechanism requires deterministic code. If `Math.random()` weren't se
|
|
|
66
66
|
For a deeper dive into workflows and steps, see [Workflows and Steps](/docs/foundations/workflows-and-steps).
|
|
67
67
|
</Callout>
|
|
68
68
|
|
|
69
|
-
## The
|
|
69
|
+
## The core challenge
|
|
70
70
|
|
|
71
|
-
This execution model
|
|
71
|
+
This execution model provides durability: workflows can suspend for days, survive restarts, and resume from any point. However, it also requires a semantic boundary in the code that tells **the compiler, runtime, and developer** that execution semantics have changed.
|
|
72
72
|
|
|
73
73
|
The challenge: how do we mark this boundary in a way that:
|
|
74
74
|
|
|
@@ -77,7 +77,7 @@ The challenge: how do we mark this boundary in a way that:
|
|
|
77
77
|
3. Allows static analysis of workflow structure
|
|
78
78
|
4. Feels natural to JavaScript developers
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
Directives have prior uses, and we considered several alternatives:
|
|
81
81
|
|
|
82
82
|
## Prior art on directives
|
|
83
83
|
|
|
@@ -104,11 +104,11 @@ The `"use workflow"` directive is also used by the Language Server Plugin shippe
|
|
|
104
104
|
|
|
105
105
|
But we didn't get here immediately. This took some discovery to arrive at:
|
|
106
106
|
|
|
107
|
-
## Alternatives
|
|
107
|
+
## Alternatives we explored
|
|
108
108
|
|
|
109
|
-
Before settling on directives, we prototyped several other approaches. Each had
|
|
109
|
+
Before settling on directives, we prototyped several other approaches. Each had limitations that made them unsuitable for production use.
|
|
110
110
|
|
|
111
|
-
### Runtime-
|
|
111
|
+
### Runtime-only "Suspense" API
|
|
112
112
|
|
|
113
113
|
Our first proof of concept used a wrapper-based API without a build step:
|
|
114
114
|
|
|
@@ -140,7 +140,7 @@ export const myWorkflow = workflow(async () => {
|
|
|
140
140
|
});
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
-
This was verbose and
|
|
143
|
+
This was verbose and developers could forget it. If a developer forgot to wrap something like `Date.now()`, it led to unstable runtime behavior.
|
|
144
144
|
|
|
145
145
|
For example:
|
|
146
146
|
|
|
@@ -200,7 +200,7 @@ export const myWorkflow = workflow(async () => {
|
|
|
200
200
|
});
|
|
201
201
|
```
|
|
202
202
|
|
|
203
|
-
### Generator-
|
|
203
|
+
### Generator-based API
|
|
204
204
|
|
|
205
205
|
We explored using generators for explicit suspension points, inspired by libraries like Effect.ts:
|
|
206
206
|
|
|
@@ -220,7 +220,7 @@ We're big fans of [Effect.ts](https://effect.website/) and the power of generato
|
|
|
220
220
|
|
|
221
221
|
**1. Syntax felt more like a DSL than JavaScript**
|
|
222
222
|
|
|
223
|
-
Generators require a custom mental model that differs
|
|
223
|
+
Generators require a custom mental model that differs from familiar async/await patterns. The `yield*` syntax and generator delegation were unfamiliar to many developers:
|
|
224
224
|
|
|
225
225
|
{/* @skip-typecheck: incomplete code sample */}
|
|
226
226
|
```typescript lineNumbers
|
|
@@ -267,7 +267,7 @@ export const myWorkflow = workflow(function*() {
|
|
|
267
267
|
|
|
268
268
|
The generator syntax addressed suspension but didn't solve the fundamental sandboxing problem.
|
|
269
269
|
|
|
270
|
-
### File
|
|
270
|
+
### File system-based conventions
|
|
271
271
|
|
|
272
272
|
We explored using file system conventions to identify workflows and steps, similar to how modern frameworks handle routing (Next.js, Hono, Nitro, SvelteKit):
|
|
273
273
|
|
|
@@ -282,7 +282,7 @@ We explored using file system conventions to identify workflows and steps, simil
|
|
|
282
282
|
</Folder>
|
|
283
283
|
</Files>
|
|
284
284
|
|
|
285
|
-
With this approach, any function in the `workflows/` directory would be transformed as a workflow, and any function in `steps/` would be a step.
|
|
285
|
+
With this approach, any function in the `workflows/` directory would be transformed as a workflow, and any function in `steps/` would be a step. File locations would replace directives.
|
|
286
286
|
|
|
287
287
|
**Why this could work:**
|
|
288
288
|
|
|
@@ -308,9 +308,9 @@ The directive approach solved all these issues: it works in any project structur
|
|
|
308
308
|
|
|
309
309
|
### Decorators
|
|
310
310
|
|
|
311
|
-
We considered decorators, but they presented
|
|
311
|
+
We considered decorators, but they presented technical and ergonomic challenges.
|
|
312
312
|
|
|
313
|
-
**Decorators are
|
|
313
|
+
**Decorators are not-yet-standard and class-focused**
|
|
314
314
|
|
|
315
315
|
Decorators are not yet a standard syntax ([TC39 proposal](https://github.com/tc39/proposal-decorators)) and they currently only work with classes. A class decorator approach could look like this:
|
|
316
316
|
|
|
@@ -349,7 +349,7 @@ While decorators can be handled at compile-time with build tool support, they pr
|
|
|
349
349
|
See the [Macro Wrapper](#macro-wrapper-approach) section below for a deeper dive into why this approach breaks down with concrete examples.
|
|
350
350
|
</Callout>
|
|
351
351
|
|
|
352
|
-
### Macro
|
|
352
|
+
### Macro wrapper approach
|
|
353
353
|
|
|
354
354
|
We also explored compile-time macro approaches - using a compiler to transform wrapper functions or decorators into directive-based code:
|
|
355
355
|
|
|
@@ -385,7 +385,7 @@ export const processOrder = async (orderId: string) => {
|
|
|
385
385
|
};
|
|
386
386
|
```
|
|
387
387
|
|
|
388
|
-
The benefit is that macros could enforce types and provide "Go To Definition" or other LSP features
|
|
388
|
+
The benefit is that macros could enforce types and provide "Go To Definition" or other LSP features without additional configuration.
|
|
389
389
|
|
|
390
390
|
However, **the core problem remains: Workflows aren't runtime values**
|
|
391
391
|
|
|
@@ -431,7 +431,7 @@ To detect that `processOrder` is actually a workflow, the compiler would need wh
|
|
|
431
431
|
|
|
432
432
|
This level of cross-function analysis is impractical for build tools - it would require analyzing every function call chain in your entire codebase and all dependencies. The compiler can only reliably detect direct `useWorkflow` calls, not calls hidden behind abstractions.
|
|
433
433
|
|
|
434
|
-
## How
|
|
434
|
+
## How directives solve these problems
|
|
435
435
|
|
|
436
436
|
Directives address all the issues we encountered with previous approaches:
|
|
437
437
|
|
|
@@ -527,7 +527,7 @@ export async function processOrder(orderId: string) {
|
|
|
527
527
|
|
|
528
528
|
The `"use step"` directive maintains consistency. While steps run in the full Node.js runtime and *could* work without a directive, they need some way to signal to the workflow runtime that they're steps.
|
|
529
529
|
|
|
530
|
-
We could have used a function wrapper
|
|
530
|
+
We could have used a function wrapper for steps:
|
|
531
531
|
|
|
532
532
|
{/* @skip-typecheck: incomplete code sample */}
|
|
533
533
|
```typescript lineNumbers
|
|
@@ -584,7 +584,7 @@ By requiring explicit `"use step"` directives, developers have fine-grained cont
|
|
|
584
584
|
To understand how directives are transformed at compile time, see [How the Code Transform Works](/docs/how-it-works/code-transform).
|
|
585
585
|
</Callout>
|
|
586
586
|
|
|
587
|
-
## What
|
|
587
|
+
## What directives enable
|
|
588
588
|
|
|
589
589
|
Because `"use workflow"` defines a compile-time semantic boundary, we can provide:
|
|
590
590
|
|
|
@@ -603,7 +603,7 @@ Because `"use workflow"` defines a compile-time semantic boundary, we can provid
|
|
|
603
603
|
</Card>
|
|
604
604
|
</Cards>
|
|
605
605
|
|
|
606
|
-
## Directives as a JavaScript
|
|
606
|
+
## Directives as a JavaScript pattern
|
|
607
607
|
|
|
608
608
|
Directives in JavaScript have always been contracts between the developer and the execution environment. `"use strict"` made this pattern familiar - it's a string literal that changes how code is interpreted.
|
|
609
609
|
|
|
@@ -611,7 +611,7 @@ While JavaScript doesn't yet have first-class support for custom directives (lik
|
|
|
611
611
|
|
|
612
612
|
As TC39 members, we at Vercel are actively working with the standards body and broader ecosystem to explore formal specifications for pragma-like syntax or macro annotations that can express execution semantics.
|
|
613
613
|
|
|
614
|
-
## Closing
|
|
614
|
+
## Closing thoughts
|
|
615
615
|
|
|
616
616
|
Directives aren't about syntax preference, they're about expressing semantic boundaries. `"use workflow"` tells the compiler, developer, and runtime that this code is deterministic, resumable, and sandboxed.
|
|
617
617
|
|
package/docs/internal/index.mdx
CHANGED
|
@@ -8,12 +8,14 @@ type: overview
|
|
|
8
8
|
This page is only visible on preview deployments and local development. It does not appear in production.
|
|
9
9
|
</Callout>
|
|
10
10
|
|
|
11
|
-
## Preview
|
|
11
|
+
## Preview package
|
|
12
12
|
|
|
13
13
|
<PreviewInstall />
|
|
14
14
|
|
|
15
|
-
## Draft
|
|
15
|
+
## Draft changelogs
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Review these changelog entries before publishing them to the Vercel website.
|
|
18
18
|
|
|
19
|
-
- [
|
|
19
|
+
- [Local web UI in Nitro dev](/docs/internal/nitro-web-ui): Unreleased (ships in 5.0.0)
|
|
20
|
+
- [Native Nitro v3 bundling for workflows](/docs/internal/nitro-native-build): May 22, 2026
|
|
21
|
+
- [Serializable AbortController and AbortSignal](/docs/internal/serializable-abort-controller): March 12, 2026
|
package/docs/internal/meta.json
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Native Nitro v3 bundling for workflows
|
|
3
|
+
description: Workflow routes are now bundled by Nitro v3, so steps run in your app's runtime and can call any server-side Nitro API.
|
|
4
|
+
type: overview
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Native Nitro v3 bundling for workflows
|
|
8
|
+
|
|
9
|
+
<span className="text-sm text-fd-muted-foreground">May 22, 2026</span>
|
|
10
|
+
|
|
11
|
+
Workflow routes are now handled as Nitro v3 handlers and bundled by Nitro, rather than built separately for the Vercel Build Output API. Your workflows are part of the same bundle as the rest of your app.
|
|
12
|
+
|
|
13
|
+
## What's new
|
|
14
|
+
|
|
15
|
+
- **Call any server-side Nitro API from a step.** Workflow steps now run inside the same bundled runtime as the rest of your Nitro app, so you can call `useStorage()`, `useDatabase()`, `useRuntimeConfig()`, virtual imports, and any other server-side Nitro API directly from a `"use step"` function.
|
|
16
|
+
- **Workflows are part of your Nitro bundle.** Workflow routes are bundled by Nitro alongside your application code, instead of being built into a separate output. There's nothing extra to configure.
|
|
17
|
+
- **Minimal runtime output.** During bundling, Nitro automatically traces native dependencies and tree-shakes unused code to produce a minimal runtime output.
|
|
18
|
+
- **Nitro v2 is unchanged.** This applies to Nitro v3. Apps on Nitro v2 keep their existing behavior.
|
|
19
|
+
|
|
20
|
+
## Using Nitro APIs from a step
|
|
21
|
+
|
|
22
|
+
Because steps run inside your Nitro runtime, server-side utilities work directly inside a `"use step"` function:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
export async function cacheResult(key: string, value: string) {
|
|
26
|
+
"use step";
|
|
27
|
+
|
|
28
|
+
const storage = useStorage("cache");
|
|
29
|
+
await storage.setItem(key, value);
|
|
30
|
+
|
|
31
|
+
return { cached: true };
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Learn more
|
|
36
|
+
|
|
37
|
+
- [Nitro](/docs/getting-started/nitro): Set up Workflow SDK in a Nitro v3 app
|
|
38
|
+
- [Deploying](/docs/deploying): How workflow bundles are deployed
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Local web UI in Nitro dev
|
|
3
|
+
description: Inspect, monitor, and debug your workflow runs from the /_workflow route during Nitro development.
|
|
4
|
+
type: overview
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Local web UI in Nitro dev
|
|
8
|
+
|
|
9
|
+
{/* TODO: unreleased. Changeset .changeset/nitro-dashboard-route.md is pending; ships in @workflow/nitro 5.0.0. Update this date on publish. */}
|
|
10
|
+
<span className="text-sm text-fd-muted-foreground">June 2, 2026</span>
|
|
11
|
+
|
|
12
|
+
The Workflow SDK web UI is now built into the Nitro dev server. During development, open `/_workflow` in your browser to inspect, monitor, and debug your workflow runs.
|
|
13
|
+
|
|
14
|
+
## What's new
|
|
15
|
+
|
|
16
|
+
- **Built-in `/_workflow` route in development.** The route starts the local web UI and redirects to it without requiring a separate command or process.
|
|
17
|
+
- **Inspect runs in place.** Inspect, monitor, and debug your workflow runs directly from the dev server you're already running.
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
## Learn more
|
|
22
|
+
|
|
23
|
+
- [Observability](/docs/observability): Inspect runs with the web UI and CLI
|
|
24
|
+
- [Nitro](/docs/getting-started/nitro): Set up Workflow SDK in a Nitro v3 app
|
|
@@ -8,7 +8,7 @@ type: overview
|
|
|
8
8
|
|
|
9
9
|
<span className="text-sm text-fd-muted-foreground">March 12, 2026</span>
|
|
10
10
|
|
|
11
|
-
`AbortController` and `AbortSignal` now work natively in workflow functions. Create a controller, pass its signal to steps, and call `abort()
|
|
11
|
+
`AbortController` and `AbortSignal` now work natively in workflow functions. Create a controller, pass its signal to steps, and call `abort()`; no special imports or wrapper functions needed.
|
|
12
12
|
|
|
13
13
|
## What's new
|
|
14
14
|
|
|
@@ -17,8 +17,8 @@ type: overview
|
|
|
17
17
|
- **Cooperative cancellation.** Steps receive the abort in real time and can respond by checking `signal.aborted`, calling `signal.throwIfAborted()`, or passing the signal to APIs like `fetch`.
|
|
18
18
|
- **Abort errors skip retries.** When a step throws due to an abort (e.g., `fetch` throws `AbortError`), the error is automatically wrapped in `FatalError` so it skips retries and bubbles up immediately.
|
|
19
19
|
- **`AbortSignal.timeout()` blocked in workflow VM.** Because it relies on real-time timers that break deterministic replay, `AbortSignal.timeout()` throws a helpful error pointing to the `sleep()` + `AbortController` pattern instead.
|
|
20
|
-
- **`Request.signal` preserved when it carries abort state.** A `Request`'s `.signal` is serialized when it's already aborted (so the cancellation that happened pre-serialization is preserved) or when it's a workflow-managed signal (so its hook + stream backing carries through). Plain non-aborted native signals
|
|
21
|
-
- **Pending queue items drain on completion.** If you call `abort()` (or `dispose` a hook, or kick off a `void sleep('1d')`, or fire a `void someStep()`) without a suspension point between that call and the workflow's return, the runtime now treats end-of-run as a final suspension and commits all pending operations before the run is marked terminal. This matches normal JS semantics
|
|
20
|
+
- **`Request.signal` preserved when it carries abort state.** A `Request`'s `.signal` is serialized when it's already aborted (so the cancellation that happened pre-serialization is preserved) or when it's a workflow-managed signal (so its hook + stream backing carries through). Plain non-aborted native signals (including the auto-generated signal on `new Request(url)`) are dropped to avoid minting stream infrastructure for every `Request`. To get cross-boundary cancellation through a `Request`, build it with the signal from a workflow-context `AbortController`.
|
|
21
|
+
- **Pending queue items drain on completion.** If you call `abort()` (or `dispose` a hook, or kick off a `void sleep('1d')`, or fire a `void someStep()`) without a suspension point between that call and the workflow's return, the runtime now treats end-of-run as a final suspension and commits all pending operations before the run is marked terminal. This matches normal JS semantics: `setTimeout` etc. continue running after the surrounding function returns. The most important case: `controller.abort()` called as the last statement of a workflow now actually propagates to in-flight steps on other compute instances.
|
|
22
22
|
|
|
23
23
|
## Timeout with cancellation
|
|
24
24
|
|
|
@@ -106,7 +106,7 @@ export async function userCancellableWorkflow(jobId: string) {
|
|
|
106
106
|
|
|
107
107
|
## Step-initiated abort
|
|
108
108
|
|
|
109
|
-
A step can receive the full `AbortController` and call `abort()` to cancel parallel work
|
|
109
|
+
A step can receive the full `AbortController` and call `abort()` to cancel parallel work, useful for watchdog patterns like quota monitoring:
|
|
110
110
|
|
|
111
111
|
```typescript
|
|
112
112
|
declare function processData(url: string, signal: AbortSignal): Promise<{ processed: boolean }>; // @setup
|
|
@@ -143,6 +143,6 @@ async function monitorQuota(userId: string, controller: AbortController) {
|
|
|
143
143
|
|
|
144
144
|
## Learn more
|
|
145
145
|
|
|
146
|
-
- [Cancellation](/docs/foundations/cancellation)
|
|
147
|
-
- [How Cancellation Works](/docs/how-it-works/cancellation)
|
|
148
|
-
- [AbortSignal.timeout() in Workflow](/docs/errors/abort-signal-timeout-in-workflow)
|
|
146
|
+
- [Cancellation](/docs/foundations/cancellation): Full guide with all usage patterns
|
|
147
|
+
- [How Cancellation Works](/docs/how-it-works/cancellation): Hook and stream internals
|
|
148
|
+
- [AbortSignal.timeout() in Workflow](/docs/errors/abort-signal-timeout-in-workflow): Why `AbortSignal.timeout()` is blocked and what to use instead
|
package/docs/meta.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"pages": [
|
|
3
|
-
"introduction",
|
|
4
3
|
"---",
|
|
4
|
+
"whats-new",
|
|
5
5
|
"getting-started",
|
|
6
6
|
"foundations",
|
|
7
7
|
"how-it-works",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"testing",
|
|
11
11
|
"deploying",
|
|
12
12
|
"errors",
|
|
13
|
-
"
|
|
13
|
+
"comparisons",
|
|
14
|
+
"configuration",
|
|
14
15
|
"api-reference"
|
|
15
16
|
]
|
|
16
17
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Attributes
|
|
3
|
+
description: Attach metadata to workflow runs for observability.
|
|
4
|
+
type: reference
|
|
5
|
+
summary: Add string attributes to a workflow run.
|
|
6
|
+
prerequisites:
|
|
7
|
+
- /docs/foundations/workflows-and-steps
|
|
8
|
+
related:
|
|
9
|
+
- /docs/observability
|
|
10
|
+
- /docs/api-reference/workflow/set-attributes
|
|
11
|
+
- /docs/api-reference/workflow-errors/workflow-world-error
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
[`setAttributes`](/docs/api-reference/workflow/set-attributes) attaches plaintext string metadata to the current workflow run. These attributes appear in the Workflow CLI and web UI, and you can search and filter runs by them from either the [CLI](#from-the-cli) or the [Analytics API](/docs/api-reference/workflow-runtime/world/analytics).
|
|
15
|
+
|
|
16
|
+
You can also seed any attributes directly when starting a run:
|
|
17
|
+
|
|
18
|
+
{/* @skip-typecheck: abbreviated usage; orderWorkflow is defined below */}
|
|
19
|
+
```typescript lineNumbers
|
|
20
|
+
const run = await start(orderWorkflow, ["ord_123"], {
|
|
21
|
+
attributes: { source: "checkout" }, // [!code highlight]
|
|
22
|
+
})
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```typescript lineNumbers
|
|
26
|
+
import { setAttributes } from "workflow"
|
|
27
|
+
|
|
28
|
+
export async function orderWorkflow(orderId: string) {
|
|
29
|
+
"use workflow"
|
|
30
|
+
|
|
31
|
+
await setAttributes({ // [!code highlight]
|
|
32
|
+
phase: "received", // [!code highlight]
|
|
33
|
+
orderId, // [!code highlight]
|
|
34
|
+
}) // [!code highlight]
|
|
35
|
+
|
|
36
|
+
// ...work...
|
|
37
|
+
|
|
38
|
+
await setAttributes({ phase: "complete" }) // [!code highlight]
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
Call [`setAttributes`](/docs/api-reference/workflow/set-attributes) from a `"use workflow"` function or a `"use step"` function. Plain application code is not supported because there is no active workflow run to attach attributes to.
|
|
45
|
+
|
|
46
|
+
Values must be strings. Pass `undefined` to remove a key:
|
|
47
|
+
|
|
48
|
+
```typescript lineNumbers
|
|
49
|
+
import { setAttributes } from "workflow"
|
|
50
|
+
|
|
51
|
+
export async function cleanupAttributes() {
|
|
52
|
+
"use workflow"
|
|
53
|
+
|
|
54
|
+
await setAttributes({ staleKey: undefined }) // [!code highlight]
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
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.
|
|
59
|
+
|
|
60
|
+
## Reserved keys
|
|
61
|
+
|
|
62
|
+
When `start()` is called from inside a running workflow or step, the new run is automatically tagged with two reserved attributes:
|
|
63
|
+
|
|
64
|
+
- `$parentRunId`: the run that started it.
|
|
65
|
+
- `$rootRunId`: the root of the chain. It is inherited, so every run in a daisy chain or fan-out shares one root id.
|
|
66
|
+
|
|
67
|
+
Top-level runs (started outside any workflow or step) are not tagged.
|
|
68
|
+
|
|
69
|
+
## Viewing attributes
|
|
70
|
+
|
|
71
|
+
The run details panel in the observability UI shows the run's current attributes as key-value rows. Reserved `$`-prefixed keys are marked with a badge and sorted after user keys:
|
|
72
|
+
|
|
73
|
+

|
|
74
|
+
|
|
75
|
+
Each `setAttributes` call appears on the trace timeline as a diamond marker at the moment the attributes were written:
|
|
76
|
+
|
|
77
|
+

|
|
78
|
+
|
|
79
|
+
Expanding an `attr_set` event (in the run sidebar or the Events tab) shows the changed keys, removed keys, and whether the write came from the workflow body or a step (with the attempt number):
|
|
80
|
+
|
|
81
|
+

|
|
82
|
+
|
|
83
|
+
## Searching and filtering by attributes
|
|
84
|
+
|
|
85
|
+
### From the CLI
|
|
86
|
+
|
|
87
|
+
`workflow inspect attributes` lists the keys recorded on this project's runs,
|
|
88
|
+
with how many runs carry each and when it was first and last seen:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
workflow inspect attributes
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Pass one or more `--attribute key=value` pairs to `inspect runs` to list the
|
|
95
|
+
runs carrying them. Repeatable up to 8 times:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
workflow inspect runs --attribute phase=received --status running
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Both require a backend with the analytics read path. `--attribute` is ignored
|
|
102
|
+
with a warning on backends without one, and `inspect attributes` reports that
|
|
103
|
+
it is unavailable.
|
|
104
|
+
|
|
105
|
+
### From the Analytics API
|
|
106
|
+
|
|
107
|
+
The [Analytics API](/docs/api-reference/workflow-runtime/world/analytics) can discover which attribute keys exist and filter run listings by them. The `analytics` namespace is optional on `World`, so feature-detect it before use; it is absent on local, Postgres, and other custom Worlds:
|
|
108
|
+
|
|
109
|
+
```typescript lineNumbers
|
|
110
|
+
import { getWorld } from "workflow/runtime";
|
|
111
|
+
|
|
112
|
+
const world = await getWorld();
|
|
113
|
+
if (!world.analytics) {
|
|
114
|
+
throw new Error("This World does not support analytics queries"); // [!code highlight]
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Which attribute keys exist, and on how many runs?
|
|
118
|
+
const keys = await world.analytics.attributes.list();
|
|
119
|
+
|
|
120
|
+
// List runs whose latest attributes match every pair
|
|
121
|
+
const stuck = await world.analytics.runs.list({
|
|
122
|
+
attributes: { phase: "received" }, // [!code highlight]
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Matching is latest-write-wins: once the run above writes `phase: "complete"`, it stops matching `phase: "received"`.
|
|
127
|
+
|
|
128
|
+
## Behavior
|
|
129
|
+
|
|
130
|
+
- Attributes require a World implementing spec version 4 or later.
|
|
131
|
+
- Writes from workflow and step bodies append native `attr_set` events and immediately materialize `run.attributes`.
|
|
132
|
+
- Storage errors surface rather than being silently ignored: transient errors on workflow-body writes are retried, and a write the World rejects as invalid (for example, exceeding the per-run attribute cap across multiple calls) fails the run with the validation error.
|
|
133
|
+
- Step-body storage errors throw from `setAttributes` like any other step-side network write. Catch the error inside the step if the attribute is best-effort.
|
|
134
|
+
- Reading and querying: each run's current attributes are returned on the run objects from the [Storage](/docs/api-reference/workflow-runtime/world/storage) and [Analytics](/docs/api-reference/workflow-runtime/world/analytics) APIs, and the Analytics API supports discovering attribute keys and filtering run listings by key=value pairs (see [Searching and filtering by attributes](#searching-and-filtering-by-attributes)). On Worlds without the optional `analytics` namespace, attributes are readable on run objects but not searchable.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Observability
|
|
3
|
-
description: Inspect, monitor, and debug workflows through the CLI and
|
|
4
|
-
type:
|
|
5
|
-
summary: Inspect and debug workflow runs using the CLI and
|
|
3
|
+
description: Inspect, monitor, and debug workflows through the CLI and web UI.
|
|
4
|
+
type: overview
|
|
5
|
+
summary: Inspect and debug workflow runs using the CLI and web UI.
|
|
6
6
|
prerequisites:
|
|
7
7
|
- /docs/foundations
|
|
8
8
|
related:
|
|
@@ -10,15 +10,15 @@ related:
|
|
|
10
10
|
- /docs/how-it-works/encryption
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
Workflow SDK provides
|
|
13
|
+
Workflow SDK provides a Workflow CLI and web UI to inspect, monitor, and debug workflows. You can inspect workflow runs, steps, webhooks, [events](/docs/how-it-works/event-sourcing), and stream output.
|
|
14
14
|
|
|
15
|
-
## Quick
|
|
15
|
+
## Quick start
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
npx workflow
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
The CLI comes pre-installed with the Workflow SDK and registers the `workflow` command. If the `workflow` package is not already installed, `npx workflow` will download and run the CLI temporarily, or use the
|
|
21
|
+
The CLI comes pre-installed with the Workflow SDK and registers the `workflow` command. If the `workflow` package is not already installed, `npx workflow` will download and run the CLI temporarily, or use the locally installed version if available.
|
|
22
22
|
|
|
23
23
|
Get started inspecting your local workflows:
|
|
24
24
|
|
|
@@ -33,15 +33,33 @@ npx workflow inspect runs
|
|
|
33
33
|
## Web UI
|
|
34
34
|
|
|
35
35
|
Workflow SDK ships with a local web UI for inspecting your workflows. The CLI
|
|
36
|
-
|
|
36
|
+
serves the web UI locally when you use the `--web` flag.
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
# Launch
|
|
39
|
+
# Launch the web UI for visual exploration
|
|
40
40
|
npx workflow inspect runs --web
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|

|
|
44
44
|
|
|
45
|
+
On [Nitro](/docs/getting-started/nitro), the dev server has the web UI built
|
|
46
|
+
in: open `/_workflow` while `nitro dev` is running. No separate command is required.
|
|
47
|
+
|
|
48
|
+
In the runs table, select one or more runs and choose **Cancel** to cancel the batch in a single request. Runs that fail with a retryable error stay selected so you can retry them.
|
|
49
|
+
|
|
50
|
+
To share a link to a specific run without opening a browser, use the `--url`
|
|
51
|
+
flag. It prints the dashboard deep link to stdout and exits (no browser, no
|
|
52
|
+
local server), which is useful for scripts, PR comments, or automation. Add `--json` to
|
|
53
|
+
get `{ "url": "..." }`.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Print the deep-link URL for a run (no browser, no server)
|
|
57
|
+
npx workflow inspect run <run_id> --url
|
|
58
|
+
|
|
59
|
+
# Vercel runs: add the backend (and --env preview for preview deployments)
|
|
60
|
+
npx workflow inspect run <run_id> --backend vercel --url
|
|
61
|
+
```
|
|
62
|
+
|
|
45
63
|
## Backends
|
|
46
64
|
|
|
47
65
|
The Workflow SDK CLI can inspect data from any [World](/docs/deploying). By default, it inspects data in your local development environment. For example, if you are using Next.js to develop workflows locally, the
|
|
@@ -53,9 +71,9 @@ If you're deploying workflows to a production environment, but want to inspect t
|
|
|
53
71
|
Backends might require additional configuration. If you're missing environment variables, the World package should provide instructions on how to configure it.
|
|
54
72
|
</Callout>
|
|
55
73
|
|
|
56
|
-
### Vercel
|
|
74
|
+
### Vercel backend
|
|
57
75
|
|
|
58
|
-
To inspect workflows running on Vercel, ensure you're logged in to the Vercel CLI and have linked your project. See [Vercel CLI authentication and project linking docs](https://vercel.com/docs/cli/project-linking) for more information. Then,
|
|
76
|
+
To inspect workflows running on Vercel, ensure you're logged in to the Vercel CLI and have linked your project. See [Vercel CLI authentication and project linking docs](https://vercel.com/docs/cli/project-linking) for more information. Then, specify the backend as `vercel`.
|
|
59
77
|
|
|
60
78
|
```bash
|
|
61
79
|
# Inspect workflows running on Vercel
|
|
@@ -63,3 +81,7 @@ npx workflow inspect runs --backend vercel
|
|
|
63
81
|
```
|
|
64
82
|
|
|
65
83
|
When deployed to Vercel, workflow data is [encrypted end-to-end](/docs/how-it-works/encryption). Encrypted fields display as locked placeholders until you choose to decrypt them using the **Decrypt** button in the web UI or the `--decrypt` flag in the CLI.
|
|
84
|
+
|
|
85
|
+
## More observability features
|
|
86
|
+
|
|
87
|
+
<AutoCards />
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Data retention
|
|
3
|
+
description: Control how long a run's data is kept after it finishes.
|
|
4
|
+
type: reference
|
|
5
|
+
summary: Control how long a run's data is kept after the run ends.
|
|
6
|
+
prerequisites:
|
|
7
|
+
- /docs/foundations/workflows-and-steps
|
|
8
|
+
related:
|
|
9
|
+
- /docs/observability
|
|
10
|
+
- /docs/api-reference/workflow-api/start
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
A finished run leaves data behind: the inputs and outputs of the workflow and
|
|
14
|
+
each of its steps, the payloads on its event log, and anything written to its
|
|
15
|
+
streams. How long that data is kept is decided by the World you are running
|
|
16
|
+
on, not by the SDK.
|
|
17
|
+
|
|
18
|
+
`experimental_retention` on [`start()`](/docs/api-reference/workflow-api/start)
|
|
19
|
+
lets a run ask for a specific retention period, rather than the World's default.
|
|
20
|
+
|
|
21
|
+
## Deleting a run's data as soon as it ends
|
|
22
|
+
|
|
23
|
+
{/* @skip-typecheck: abbreviated usage; processDocumentWorkflow is the reader's own workflow */}
|
|
24
|
+
```typescript lineNumbers
|
|
25
|
+
const run = await start(processDocumentWorkflow, [documentId], {
|
|
26
|
+
experimental_retention: 0, // [!code highlight]
|
|
27
|
+
})
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`0` asks the World to delete the run's **user data** the moment the run
|
|
31
|
+
completes or fails, rather than keeping it for the World's default window.
|
|
32
|
+
|
|
33
|
+
Two values are accepted today:
|
|
34
|
+
|
|
35
|
+
| Value | Meaning |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| `0` | Delete user data as soon as the run reaches a terminal state. |
|
|
38
|
+
| `'default'` | Use the World's default. Identical to omitting the option. |
|
|
39
|
+
|
|
40
|
+
<Callout type="warn">
|
|
41
|
+
The option is prefixed `experimental_` because both its name and the set of
|
|
42
|
+
values it accepts are expected to change.
|
|
43
|
+
</Callout>
|
|
44
|
+
|
|
45
|
+
## What is deleted, and what is not
|
|
46
|
+
|
|
47
|
+
**Deleted:** the run's input, output and error; every step's input, output and
|
|
48
|
+
error; the payloads on the event log; and stream contents.
|
|
49
|
+
|
|
50
|
+
**Kept:** the run, step and event records themselves — their ids, timestamps,
|
|
51
|
+
status, step names, and any [attributes](/docs/observability/attributes) you
|
|
52
|
+
set. They are kept for the World's default period so the run stays visible in
|
|
53
|
+
the CLI and web UI. A purged run is still listed and still traceable; its
|
|
54
|
+
payloads simply read back as expired.
|
|
55
|
+
|
|
56
|
+
Inspecting a purged run shows it as expired rather than failing. The Workflow
|
|
57
|
+
CLI renders the run's own input, output and error as `<data expired>`:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
workflow inspect runs wrun_...
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Step, hook and event payloads read back empty. On the Vercel World they also
|
|
64
|
+
render as `<data expired>`; on Worlds that clear the stored value outright
|
|
65
|
+
they simply show as empty. Either way the data is gone — the difference is
|
|
66
|
+
only in how the absence is labelled.
|
|
67
|
+
|
|
68
|
+
<Callout type="warn">
|
|
69
|
+
**You cannot read the return value of a run started with
|
|
70
|
+
`experimental_retention: 0`.** The deletion races your own read of the
|
|
71
|
+
result and generally wins, so `await run.returnValue` throws
|
|
72
|
+
[`RunExpiredError`](/docs/errors/run-expired) instead of resolving.
|
|
73
|
+
|
|
74
|
+
This is a known limitation. If you need the result, send it somewhere you
|
|
75
|
+
control, e.g. a step that writes it to your own store, rather than reading it back off the run.
|
|
76
|
+
</Callout>
|
|
77
|
+
|
|
78
|
+
`RunExpiredError` is not specific to `experimental_retention: 0`. Any run read
|
|
79
|
+
after its retention window has passed throws it, and the error carries
|
|
80
|
+
`runId`, `runStatus` and `expiredAt` when the World still has them — so a
|
|
81
|
+
caller can tell a successful run whose result is gone from a failed one whose
|
|
82
|
+
error is gone. If the run's metadata is gone too, the World reports the run as
|
|
83
|
+
missing and you get `WorkflowRunNotFoundError` instead.
|
|
84
|
+
|
|
85
|
+
## Retention is implemented by the World
|
|
86
|
+
|
|
87
|
+
The SDK records your preference; it does not enforce it. `start()` writes the
|
|
88
|
+
value onto the run as the reserved `$retention` attribute, and the World
|
|
89
|
+
decides what to do when the run ends.
|
|
90
|
+
A World that does not implement retention
|
|
91
|
+
**keeps the data**. If you need certainty that a specific World deletes your data,
|
|
92
|
+
confirm it against that World's own documentation rather than the presence of this
|
|
93
|
+
option.
|