workflow 5.0.0-beta.2 → 5.0.0-beta.20
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/dist/api-workflow.d.ts +1 -1
- package/dist/api-workflow.d.ts.map +1 -1
- package/dist/api-workflow.js +2 -2
- package/dist/api.d.ts +5 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +14 -2
- package/dist/internal/builtins.d.ts +17 -0
- package/dist/internal/builtins.d.ts.map +1 -1
- package/dist/internal/builtins.js +65 -1
- package/dist/observability.d.ts +1 -1
- package/dist/observability.js +2 -2
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +2 -2
- package/docs/ai/index.mdx +27 -23
- package/docs/api-reference/index.mdx +24 -0
- package/docs/api-reference/meta.json +8 -0
- package/docs/api-reference/vitest/index.mdx +28 -7
- package/docs/api-reference/workflow/create-hook.mdx +38 -0
- package/docs/api-reference/workflow/create-webhook.mdx +1 -0
- package/docs/api-reference/workflow/experimental-set-attributes.mdx +65 -0
- package/docs/api-reference/workflow/fetch.mdx +5 -0
- package/docs/api-reference/workflow/index.mdx +3 -0
- package/docs/api-reference/workflow-ai/durable-agent.mdx +7 -45
- package/docs/api-reference/workflow-api/get-hook-by-token.mdx +7 -0
- package/docs/api-reference/workflow-api/get-run.mdx +6 -0
- package/docs/api-reference/workflow-api/index.mdx +6 -8
- package/docs/api-reference/workflow-api/resume-hook.mdx +57 -0
- package/docs/api-reference/workflow-api/start.mdx +13 -5
- 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/hook-conflict-error.mdx +60 -0
- package/docs/api-reference/workflow-errors/index.mdx +85 -0
- package/docs/api-reference/workflow-errors/meta.json +5 -0
- package/docs/api-reference/workflow-errors/run-not-supported-error.mdx +58 -0
- package/docs/api-reference/workflow-errors/workflow-error.mdx +52 -0
- package/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx +16 -6
- package/docs/api-reference/workflow-errors/workflow-run-not-completed-error.mdx +58 -0
- package/docs/api-reference/workflow-errors/workflow-runtime-error.mdx +58 -0
- 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 +34 -2
- package/docs/api-reference/workflow-nitro/index.mdx +59 -0
- package/docs/api-reference/workflow-nuxt/index.mdx +47 -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 +64 -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 +7 -10
- package/docs/api-reference/workflow-runtime/health-check.mdx +50 -0
- package/docs/api-reference/workflow-runtime/index.mdx +43 -0
- package/docs/api-reference/workflow-runtime/meta.json +12 -0
- package/docs/api-reference/workflow-runtime/set-world.mdx +49 -0
- package/docs/api-reference/workflow-runtime/workflow-entrypoint.mdx +42 -0
- package/docs/api-reference/{workflow-api → workflow-runtime}/world/index.mdx +5 -8
- package/docs/api-reference/workflow-runtime/world/meta.json +4 -0
- package/docs/api-reference/{workflow-api → workflow-runtime}/world/queue.mdx +2 -2
- package/docs/api-reference/{workflow-api → workflow-runtime}/world/storage.mdx +11 -4
- package/docs/api-reference/{workflow-api → workflow-runtime}/world/streams.mdx +2 -2
- package/docs/api-reference/workflow-serde/index.mdx +0 -1
- package/docs/api-reference/workflow-serde/workflow-deserialize.mdx +1 -2
- package/docs/api-reference/workflow-serde/workflow-serialize.mdx +1 -2
- 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/eager-processing.mdx +269 -0
- package/docs/changelog/index.mdx +2 -1
- package/docs/changelog/lazy-event-creation.md +127 -0
- package/docs/changelog/meta.json +6 -1
- package/docs/changelog/resilient-start.mdx +31 -283
- package/docs/cookbook/advanced/child-workflows.mdx +315 -0
- package/docs/cookbook/advanced/meta.json +2 -3
- package/docs/cookbook/advanced/publishing-libraries.mdx +87 -29
- package/docs/cookbook/advanced/serializable-steps.mdx +17 -5
- package/docs/cookbook/advanced/upgrading-workflows.mdx +195 -0
- package/docs/cookbook/agent-patterns/agent-cancellation.mdx +156 -0
- package/docs/cookbook/agent-patterns/durable-agent.mdx +11 -184
- package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +150 -173
- package/docs/cookbook/agent-patterns/meta.json +1 -7
- package/docs/cookbook/common-patterns/batching.mdx +44 -118
- package/docs/cookbook/common-patterns/idempotency.mdx +36 -52
- package/docs/cookbook/common-patterns/meta.json +4 -4
- package/docs/cookbook/common-patterns/rate-limiting.mdx +1 -1
- package/docs/cookbook/common-patterns/saga.mdx +128 -33
- package/docs/cookbook/common-patterns/scheduling.mdx +77 -193
- package/docs/cookbook/common-patterns/sequential-and-parallel.mdx +155 -0
- package/docs/cookbook/common-patterns/timeouts.mdx +100 -0
- package/docs/cookbook/common-patterns/workflow-composition.mdx +117 -0
- package/docs/cookbook/index.mdx +14 -17
- package/docs/cookbook/integrations/ai-sdk.mdx +330 -142
- package/docs/cookbook/integrations/chat-sdk.mdx +264 -151
- package/docs/cookbook/integrations/sandbox.mdx +482 -81
- package/docs/cookbook/meta.json +1 -1
- package/docs/deploying/building-a-world.mdx +1 -1
- package/docs/deploying/world/postgres-world.mdx +5 -3
- package/docs/deploying/world/vercel-world.mdx +2 -0
- package/docs/errors/abort-signal-timeout-in-workflow.mdx +80 -0
- package/docs/errors/corrupted-event-log.mdx +5 -5
- package/docs/errors/hook-conflict.mdx +56 -4
- package/docs/errors/index.mdx +9 -0
- package/docs/errors/replay-divergence.mdx +27 -0
- package/docs/errors/runtime-decryption-failed.mdx +77 -0
- package/docs/errors/step-executed-multiple-times.mdx +23 -0
- package/docs/errors/step-not-registered.mdx +1 -1
- package/docs/foundations/cancellation.mdx +459 -0
- package/docs/foundations/errors-and-retries.mdx +7 -3
- package/docs/foundations/hooks.mdx +29 -0
- package/docs/foundations/idempotency.mdx +236 -11
- package/docs/foundations/index.mdx +3 -3
- package/docs/foundations/meta.json +3 -2
- package/docs/foundations/serialization.mdx +78 -42
- package/docs/foundations/starting-workflows.mdx +6 -2
- package/docs/foundations/streaming.mdx +14 -23
- package/docs/foundations/versioning.mdx +263 -0
- package/docs/getting-started/astro.mdx +6 -0
- package/docs/getting-started/index.mdx +6 -7
- package/docs/getting-started/meta.json +1 -0
- package/docs/getting-started/nestjs.mdx +9 -0
- package/docs/getting-started/next.mdx +5 -3
- package/docs/getting-started/nitro.mdx +22 -0
- package/docs/getting-started/sveltekit.mdx +6 -0
- package/docs/getting-started/tanstack-start.mdx +241 -0
- package/docs/how-it-works/cancellation.mdx +287 -0
- package/docs/how-it-works/code-transform.mdx +2 -2
- package/docs/how-it-works/encryption.mdx +2 -2
- package/docs/how-it-works/event-sourcing.mdx +2 -2
- package/docs/how-it-works/meta.json +2 -1
- package/docs/internal/index.mdx +21 -0
- package/docs/internal/meta.json +10 -0
- 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 +148 -0
- package/docs/migration-guides/migrating-from-aws-step-functions.mdx +63 -16
- package/docs/migration-guides/migrating-from-inngest.mdx +44 -22
- package/docs/migration-guides/migrating-from-temporal.mdx +43 -14
- package/docs/migration-guides/migrating-from-trigger-dev.mdx +59 -27
- package/docs/observability/attributes.mdx +87 -0
- package/docs/observability/index.mdx +25 -1
- package/docs/observability/meta.json +1 -1
- package/docs/observability/tracing.mdx +106 -0
- package/docs/testing/index.mdx +2 -2
- package/package.json +14 -13
- package/docs/api-reference/workflow-api/world/meta.json +0 -4
- package/docs/api-reference/workflow-api/world/observability.mdx +0 -164
- package/docs/cookbook/advanced/custom-serialization.mdx +0 -168
- package/docs/cookbook/advanced/durable-objects.mdx +0 -148
- package/docs/cookbook/advanced/isomorphic-packages.mdx +0 -145
- package/docs/cookbook/agent-patterns/stop-workflow.mdx +0 -216
- package/docs/cookbook/agent-patterns/tool-orchestration.mdx +0 -255
- package/docs/cookbook/agent-patterns/tool-streaming.mdx +0 -181
- package/docs/cookbook/common-patterns/child-workflows.mdx +0 -372
- package/docs/cookbook/common-patterns/content-router.mdx +0 -207
- package/docs/cookbook/common-patterns/fan-out.mdx +0 -208
- package/docs/foundations/common-patterns.mdx +0 -265
|
@@ -32,7 +32,7 @@ npx skills add https://github.com/vercel/workflow --skill migrating-to-workflow-
|
|
|
32
32
|
|
|
33
33
|
## What changes when you leave trigger.dev?
|
|
34
34
|
|
|
35
|
-
trigger.dev v3 defines durable work with `task()` or `schemaTask()` from `@trigger.dev/sdk
|
|
35
|
+
trigger.dev v3 defines durable work with `task()` or `schemaTask()` from `@trigger.dev/sdk` (trigger.dev v3), deploys tasks to the trigger.dev cloud or a self-hosted instance, and triggers runs via `tasks.trigger()`. A separate worker fleet picks up runs, applies retry policies, and routes `wait.for`, `wait.forToken`, and `metadata.stream` calls through the platform.
|
|
36
36
|
|
|
37
37
|
The Workflow SDK replaces that with `"use workflow"` functions that orchestrate `"use step"` functions in plain TypeScript. There is no task registry, separate deploy target, or SDK client. Durable replay, retries, and event history ship with the runtime.
|
|
38
38
|
|
|
@@ -46,21 +46,25 @@ Migration collapses the task abstraction into plain async functions. Business lo
|
|
|
46
46
|
| `schemaTask({ schema, run })` | Typed function + `"use workflow"` | Validate inputs at the call site. |
|
|
47
47
|
| Inline `run` body | `"use step"` function | Side effects move into named steps. |
|
|
48
48
|
| `logger` / `metadata.set` | `console` + `getWritable({ namespace: 'status' })` | Logs flow through the run timeline. Status writes go on a named stream. |
|
|
49
|
-
| `wait.for({ seconds })` / `wait.until({ date })` | `sleep()` | Import from `workflow`. |
|
|
49
|
+
| `wait.for({ seconds \| minutes \| hours \| days })` / `wait.until({ date })` | `sleep()` | Import from `workflow`. |
|
|
50
50
|
| `wait.forToken({ timeout })` | `createHook()` + `Promise.race` with `sleep()` | Hooks carry a typed token. |
|
|
51
|
-
| `tasks.trigger()` / `triggerAndWait()` | `start()` and `getRun(runId).returnValue` |
|
|
51
|
+
| `tasks.trigger()` / `triggerAndWait()` | `start()` and `getRun(runId).returnValue` | Call `start()` directly; wrap `getRun()` collection in a `"use step"` function. |
|
|
52
52
|
| `batch.triggerAndWait()` | `Promise.all(runIds.map(collectResult))` | Fan out via standard concurrency. |
|
|
53
53
|
| `AbortTaskRunError` | `FatalError` | Stops retries immediately. |
|
|
54
|
-
| `retry.onThrow` / `retry.fetch` | `RetryableError`, `FatalError`, `maxRetries` | Retry lives on the step.
|
|
55
|
-
| `metadata.stream()` / Realtime | `getWritable()` / `getWritable({ namespace })` |
|
|
54
|
+
| `retry.onThrow` / `retry.fetch` | `RetryableError`, `FatalError`, `maxRetries` | Retry count lives on the step via `myStep.maxRetries = N` (default 3). Control delay between attempts by throwing `new RetryableError(msg, { retryAfter: '5s' })` — there is no built-in exponential helper; compute the delay yourself based on `getStepMetadata().attempt` if you need one. |
|
|
55
|
+
| `metadata.stream()` / Realtime | `getWritable()` / `getWritable({ namespace })` | For granular business status (progress updates, current-stage messages), prefer writing to `getWritable({ namespace: 'status' })` from a step and reading from the end of the named stream on the client. Use `getRun(runId).status` for terminal/lifecycle state only. |
|
|
56
56
|
| Self-hosted worker + dashboard | Managed execution + built-in UI | No worker fleet to operate. |
|
|
57
57
|
|
|
58
58
|
## Translate your first workflow
|
|
59
59
|
|
|
60
|
+
<Callout type="warn">
|
|
61
|
+
trigger.dev's `task.run` body has full Node.js access. The SDK's `'use workflow'` body runs in a sandboxed VM — side effects (I/O, `Date.now()`, `Math.random()`, DB, fetch) must live inside `'use step'` functions. Orchestration stays in the workflow body.
|
|
62
|
+
</Callout>
|
|
63
|
+
|
|
60
64
|
Start with the shell. trigger.dev wraps the handler in `task()`; the Workflow SDK marks the function with a directive.
|
|
61
65
|
|
|
62
66
|
```typescript title="trigger/order.ts (trigger.dev)"
|
|
63
|
-
import { task } from '@trigger.dev/sdk
|
|
67
|
+
import { task } from '@trigger.dev/sdk';
|
|
64
68
|
|
|
65
69
|
export const processOrder = task({
|
|
66
70
|
id: 'process-order',
|
|
@@ -116,7 +120,7 @@ No id lookup, no API key, no separate worker. `start()` returns a handle immedia
|
|
|
116
120
|
|
|
117
121
|
{/* @skip-typecheck: trigger.dev SDK types not available */}
|
|
118
122
|
```typescript title="workflow/workflows/refund.ts (trigger.dev, abbreviated)"
|
|
119
|
-
// import { wait } from '@trigger.dev/sdk
|
|
123
|
+
// import { wait } from '@trigger.dev/sdk';
|
|
120
124
|
const token = await wait.createToken({ timeout: '7d' });
|
|
121
125
|
const approval = await wait.forToken<{ approved: boolean }>(token.id).unwrap();
|
|
122
126
|
// External system resumes with: await wait.completeToken(token.id, { approved: true });
|
|
@@ -130,10 +134,17 @@ using approval = createHook<{ approved: boolean }>({ // [!code highlight]
|
|
|
130
134
|
const payload = await approval;
|
|
131
135
|
```
|
|
132
136
|
|
|
133
|
-
**What changed:** the platform-issued opaque token becomes an app-owned string. The caller that resumes the run supplies that same string, so there is no token lookup.
|
|
137
|
+
**What changed:** the platform-issued opaque token becomes an app-owned string. The caller that resumes the run supplies that same string, so there is no token lookup. (trigger.dev also exposes `token.url` for external callers; the SDK analog is `createWebhook().url`).
|
|
134
138
|
|
|
135
139
|
### Resume from an API route
|
|
136
140
|
|
|
141
|
+
There are two shapes of resume, and `wait.forToken` can map to either:
|
|
142
|
+
|
|
143
|
+
- **Server-side resume (known token):** `createHook<T>({ token: 'business-token' })` + `resumeHook(token, payload)` from an API route. Use this when your app knows the token shape and controls the resume call.
|
|
144
|
+
- **Third-party callback URL (generated token):** `createWebhook({ respondWith: 'default' })` + pass `webhook.url` to the external system. The external system hits the URL to resume.
|
|
145
|
+
|
|
146
|
+
See [`/docs/foundations/hooks`](/docs/foundations/hooks) for both surfaces.
|
|
147
|
+
|
|
137
148
|
trigger.dev completes a token with `wait.completeToken(tokenId, { approved })`. The SDK equivalent is `resumeHook`:
|
|
138
149
|
|
|
139
150
|
```typescript title="app/api/refunds/[refundId]/approve/route.ts"
|
|
@@ -164,26 +175,19 @@ return { refundId, status: 'approved' };
|
|
|
164
175
|
```
|
|
165
176
|
|
|
166
177
|
<Callout type="info">
|
|
167
|
-
A hook is an inbound write channel. The caller that knows the token resumes the run with a typed payload.
|
|
178
|
+
A hook is an inbound write channel. The caller that knows the token resumes the run with a typed payload. For granular business status (progress updates, current-stage messages), prefer writing to `getWritable({ namespace: 'status' })` from a step and reading from the end of the named stream on the client. Use `getRun(runId).status` for terminal/lifecycle state only.
|
|
168
179
|
</Callout>
|
|
169
180
|
|
|
170
181
|
## Spawn a child workflow
|
|
171
182
|
|
|
172
|
-
`triggerAndWait()` splits into
|
|
173
|
-
|
|
174
|
-
```typescript title="workflow/workflows/parent.ts"
|
|
175
|
-
import { start } from 'workflow/api';
|
|
183
|
+
`triggerAndWait()` splits into spawn and collect. In v5, call `start()` directly from the workflow to spawn the child. Wrap `getRun()` and `returnValue` access in a `"use step"` function when you need to collect the result.
|
|
176
184
|
|
|
177
|
-
|
|
178
|
-
'use step';
|
|
179
|
-
return start(childWorkflow, [item]); // [!code highlight]
|
|
180
|
-
}
|
|
181
|
-
```
|
|
185
|
+
You can return either the full `Run` object (enables deep-linking) or just `run.runId` (simpler). The runtime serializes `Run` to its `runId` in the event log either way.
|
|
182
186
|
|
|
183
|
-
Await the result in a
|
|
187
|
+
Await the result in a step, then orchestrate both from the parent:
|
|
184
188
|
|
|
185
189
|
```typescript title="workflow/workflows/parent.ts"
|
|
186
|
-
import { getRun } from 'workflow/api';
|
|
190
|
+
import { getRun, start } from 'workflow/api';
|
|
187
191
|
|
|
188
192
|
async function collectResult(runId: string) {
|
|
189
193
|
'use step';
|
|
@@ -193,19 +197,21 @@ async function collectResult(runId: string) {
|
|
|
193
197
|
|
|
194
198
|
export async function parentWorkflow(item: string) {
|
|
195
199
|
'use workflow';
|
|
196
|
-
const child = await
|
|
200
|
+
const child = await start(childWorkflow, [item]); // [!code highlight]
|
|
197
201
|
return await collectResult(child.runId);
|
|
198
202
|
}
|
|
199
203
|
```
|
|
200
204
|
|
|
201
|
-
To fan out, call `
|
|
205
|
+
To fan out, call `start()` inside a loop, then `Promise.all` the `collectResult` calls. That replaces `batch.triggerAndWait()`.
|
|
206
|
+
|
|
207
|
+
`Promise.all` rejects on first failure; use `Promise.allSettled` if you need batch-mode error tolerance similar to trigger.dev's `{ ok, output, error }` per-run result.
|
|
202
208
|
|
|
203
209
|
## What you stop operating
|
|
204
210
|
|
|
205
211
|
Dropping the trigger.dev SDK removes several moving parts:
|
|
206
212
|
|
|
207
213
|
- **No task registry or `id` strings.** Workflow files carry directive annotations and export plain functions.
|
|
208
|
-
- **No `@trigger.dev/sdk
|
|
214
|
+
- **No `@trigger.dev/sdk` client or API key.** `start()` launches runs directly from API routes or server actions.
|
|
209
215
|
- **No worker fleet or self-hosted instance.** The runtime schedules execution inside the app's deploy target.
|
|
210
216
|
- **No separate Realtime channel.** `getWritable()` streams updates from steps over the run's durable stream.
|
|
211
217
|
- **No dashboard account.** The built-in observability UI (`npx workflow web`) reads the same event log the runtime writes.
|
|
@@ -218,7 +224,7 @@ Pick one trigger.dev task and migrate it end-to-end before touching the rest. Th
|
|
|
218
224
|
|
|
219
225
|
### Step 1: Install the Workflow SDK
|
|
220
226
|
|
|
221
|
-
Add the runtime.
|
|
227
|
+
Add the runtime. Framework integrations (Next.js, Nitro, Nuxt, SvelteKit, Astro, Nest) are subpath exports of the same `workflow` package, e.g. `workflow/next` — no additional install needed.
|
|
222
228
|
|
|
223
229
|
```bash
|
|
224
230
|
pnpm add workflow
|
|
@@ -255,10 +261,10 @@ async function loadOrder(id: string) {
|
|
|
255
261
|
|
|
256
262
|
### Step 4: Replace `wait.*` with hooks and `sleep`
|
|
257
263
|
|
|
258
|
-
- `wait.for({ seconds })` / `wait.until({ date })` → `sleep('5m')` or `sleep(date)` from `workflow`.
|
|
264
|
+
- `wait.for({ seconds | minutes | hours | days })` / `wait.until({ date })` → `sleep('5m')` or `sleep(date)` from `workflow`.
|
|
259
265
|
- `wait.forToken(token)` → `createHook({ token })` + `await`. Complete it with `resumeHook(token, payload)` from an API route.
|
|
260
266
|
- `wait.forToken({ timeout })` → `Promise.race([hook, sleep(timeout)])`.
|
|
261
|
-
- `triggerAndWait(payload)` →
|
|
267
|
+
- `triggerAndWait(payload)` → call `start(child, [payload])` from the workflow and return the `Run` object, then read the result with a step that calls `getRun(runId).returnValue`.
|
|
262
268
|
|
|
263
269
|
### Step 5: Start runs from the app
|
|
264
270
|
|
|
@@ -279,6 +285,29 @@ export async function POST(req: Request) {
|
|
|
279
285
|
|
|
280
286
|
Remove the `@trigger.dev/sdk` dependency, the `trigger.config.ts` file, the `trigger/` directory, and any self-hosted worker deployment. Delete dashboard API keys from the environment. Verify the run in `npx workflow web` before shipping.
|
|
281
287
|
|
|
288
|
+
## Retries on steps
|
|
289
|
+
|
|
290
|
+
Retry count lives on the step function itself. Set it as a property on the step:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
async function chargePayment(orderId: string) {
|
|
294
|
+
"use step";
|
|
295
|
+
// ...
|
|
296
|
+
}
|
|
297
|
+
chargePayment.maxRetries = 5;
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Throw `new RetryableError(msg, { retryAfter: '5s' })` to control delay between attempts, or `FatalError` to stop retries immediately. See [`/docs/foundations/errors-and-retries`](/docs/foundations/errors-and-retries).
|
|
301
|
+
|
|
302
|
+
## Features without a 1:1 equivalent
|
|
303
|
+
|
|
304
|
+
- **`schedules.task()` / cron triggers.** The SDK has no built-in scheduler. Trigger runs from Vercel Cron or a system cron calling `start()`.
|
|
305
|
+
- **Concurrency keys / queue concurrency limits.** No direct analog. Enforce limits inside steps (semaphores, external coordinator) or debounce at the publisher.
|
|
306
|
+
- **`machine` presets / custom images.** Machine specs are per-task in trigger.dev; in the SDK, function resources are per-deployment (configure via your hosting platform).
|
|
307
|
+
- **Realtime / `subscribeToRun`.** Use `getRun(runId).getReadable()` plus named `getWritable()` streams for live progress.
|
|
308
|
+
- **`onFailure` lifecycle hook.** No equivalent. Handle cleanup in the workflow body with a try/catch + compensation-stack pattern.
|
|
309
|
+
- **Trigger.dev dashboard.** Workflow SDK ships `npx workflow web` for local inspection and the Vercel Observability tab for deployed runs.
|
|
310
|
+
|
|
282
311
|
## Quick-start checklist
|
|
283
312
|
|
|
284
313
|
- Replace `task({ id, run })` with a `"use workflow"` function; launch it with `start()`.
|
|
@@ -286,7 +315,7 @@ Remove the `@trigger.dev/sdk` dependency, the `trigger.config.ts` file, the `tri
|
|
|
286
315
|
- Swap `wait.for` / `wait.until` for `sleep()` from `workflow`.
|
|
287
316
|
- Swap `wait.forToken` for `createHook()` (internal) or `createWebhook()` (HTTP).
|
|
288
317
|
- Model `wait.forToken` timeouts as `Promise.race()` between the hook and `sleep()`.
|
|
289
|
-
- Replace `triggerAndWait()` with `"use step"`
|
|
318
|
+
- Replace `triggerAndWait()` with direct `start()` calls and a `"use step"` wrapper around `getRun()` when collecting results.
|
|
290
319
|
- Replace `batch.triggerAndWait()` with `Promise.all` over the collected child `Run` handles.
|
|
291
320
|
- Move `schemaTask` validation to the call site; pass typed arguments into the workflow.
|
|
292
321
|
- Replace `AbortTaskRunError` with `FatalError`; model retries per step with `RetryableError` and `maxRetries`.
|
|
@@ -294,3 +323,6 @@ Remove the `@trigger.dev/sdk` dependency, the `trigger.config.ts` file, the `tri
|
|
|
294
323
|
- Replace `metadata.stream()` and Realtime with `getWritable()`.
|
|
295
324
|
- Remove the `@trigger.dev/sdk` dependency, `trigger.config.ts`, and any self-hosted worker.
|
|
296
325
|
- Deploy and verify runs end-to-end with the built-in observability UI.
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
*Verified against `workflow@5.0.0-beta.1` and `@trigger.dev/sdk` v3 on 2026-04-16.*
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Attributes
|
|
3
|
+
description: Attach experimental 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/experimental-set-attributes
|
|
11
|
+
- /docs/api-reference/workflow-errors/workflow-world-error
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<Callout type="warn">
|
|
15
|
+
This feature is experimental and may change before the stable attributes API is released.
|
|
16
|
+
</Callout>
|
|
17
|
+
|
|
18
|
+
[`experimental_setAttributes`](/docs/api-reference/workflow/experimental-set-attributes) attaches plaintext string metadata to the current workflow run. These attributes are displayed in observability CLI/UI.
|
|
19
|
+
In the future, you'll be able to search and filter runs by attributes.
|
|
20
|
+
|
|
21
|
+
You can also seed any attributes directly when starting a run:
|
|
22
|
+
|
|
23
|
+
{/* @skip-typecheck: abbreviated usage; orderWorkflow is defined below */}
|
|
24
|
+
```typescript lineNumbers
|
|
25
|
+
const run = await start(orderWorkflow, ["ord_123"], {
|
|
26
|
+
attributes: { source: "checkout" }, // [!code highlight]
|
|
27
|
+
})
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```typescript lineNumbers
|
|
31
|
+
import { experimental_setAttributes } from "workflow"
|
|
32
|
+
|
|
33
|
+
export async function orderWorkflow(orderId: string) {
|
|
34
|
+
"use workflow"
|
|
35
|
+
|
|
36
|
+
await experimental_setAttributes({ // [!code highlight]
|
|
37
|
+
phase: "received", // [!code highlight]
|
|
38
|
+
orderId, // [!code highlight]
|
|
39
|
+
}) // [!code highlight]
|
|
40
|
+
|
|
41
|
+
// ...work...
|
|
42
|
+
|
|
43
|
+
await experimental_setAttributes({ phase: "complete" }) // [!code highlight]
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
Call [`experimental_setAttributes`](/docs/api-reference/workflow/experimental-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.
|
|
50
|
+
|
|
51
|
+
Values must be strings. Pass `undefined` to remove a key:
|
|
52
|
+
|
|
53
|
+
```typescript lineNumbers
|
|
54
|
+
import { experimental_setAttributes } from "workflow"
|
|
55
|
+
|
|
56
|
+
export async function cleanupAttributes() {
|
|
57
|
+
"use workflow"
|
|
58
|
+
|
|
59
|
+
await experimental_setAttributes({ staleKey: undefined }) // [!code highlight]
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
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.
|
|
64
|
+
|
|
65
|
+
## Viewing attributes
|
|
66
|
+
|
|
67
|
+
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:
|
|
68
|
+
|
|
69
|
+

|
|
70
|
+
|
|
71
|
+
Each `experimental_setAttributes` call appears on the trace timeline as a diamond marker at the moment the attributes were written:
|
|
72
|
+
|
|
73
|
+

|
|
74
|
+
|
|
75
|
+
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):
|
|
76
|
+
|
|
77
|
+

|
|
78
|
+
|
|
79
|
+
## Experimental Behavior
|
|
80
|
+
|
|
81
|
+
While attributes are experimental:
|
|
82
|
+
|
|
83
|
+
- Attributes require a World implementing spec version 4 or later.
|
|
84
|
+
- Writes from workflow and step bodies append native `attr_set` events and immediately materialize `run.attributes`.
|
|
85
|
+
- 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.
|
|
86
|
+
- Step-body storage errors throw from `experimental_setAttributes` like any other step-side network write. Catch the error inside the step if the attribute is best-effort.
|
|
87
|
+
- Reading and querying attributes is not available yet. A query API is planned.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Observability
|
|
3
3
|
description: Inspect, monitor, and debug workflows through the CLI and Web UI with powerful observability tools.
|
|
4
|
-
type:
|
|
4
|
+
type: overview
|
|
5
5
|
summary: Inspect and debug workflow runs using the CLI and Web UI.
|
|
6
6
|
prerequisites:
|
|
7
7
|
- /docs/foundations
|
|
@@ -42,6 +42,19 @@ npx workflow inspect runs --web
|
|
|
42
42
|
|
|
43
43
|

|
|
44
44
|
|
|
45
|
+
To share a link to a specific run without opening a browser, use the `--url`
|
|
46
|
+
flag. It prints the dashboard deep link to stdout and exits (no browser, no
|
|
47
|
+
local server) — useful for scripts, PR comments, or automation. Add `--json` to
|
|
48
|
+
get `{ "url": "..." }`.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Print the deep-link URL for a run (no browser, no server)
|
|
52
|
+
npx workflow inspect run <run_id> --url
|
|
53
|
+
|
|
54
|
+
# Vercel runs: add the backend (and --env preview for preview deployments)
|
|
55
|
+
npx workflow inspect run <run_id> --backend vercel --url
|
|
56
|
+
```
|
|
57
|
+
|
|
45
58
|
## Backends
|
|
46
59
|
|
|
47
60
|
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
|
|
@@ -63,3 +76,14 @@ npx workflow inspect runs --backend vercel
|
|
|
63
76
|
```
|
|
64
77
|
|
|
65
78
|
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.
|
|
79
|
+
|
|
80
|
+
## More Observability Features
|
|
81
|
+
|
|
82
|
+
<Cards>
|
|
83
|
+
<Card href="/docs/observability/tracing" title="Tracing">
|
|
84
|
+
Distributed tracing with OpenTelemetry for workflow runs, steps, and queue deliveries.
|
|
85
|
+
</Card>
|
|
86
|
+
<Card href="/docs/observability/attributes" title="Attributes">
|
|
87
|
+
Attach experimental metadata to workflow runs for observability.
|
|
88
|
+
</Card>
|
|
89
|
+
</Cards>
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Tracing
|
|
3
|
+
description: Distributed tracing with OpenTelemetry for workflow runs, steps, and queue deliveries.
|
|
4
|
+
type: guide
|
|
5
|
+
summary: Trace workflow execution end to end with OpenTelemetry.
|
|
6
|
+
prerequisites:
|
|
7
|
+
- /docs/foundations/workflows-and-steps
|
|
8
|
+
related:
|
|
9
|
+
- /docs/observability
|
|
10
|
+
- /docs/observability/attributes
|
|
11
|
+
- /docs/how-it-works/event-sourcing
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
The Workflow SDK is instrumented with [OpenTelemetry](https://opentelemetry.io) out of the box. It emits spans for workflow starts, every workflow and step invocation, and the HTTP calls it makes to the workflow backend — and it propagates trace context across queue deliveries so a run remains traceable end to end.
|
|
15
|
+
|
|
16
|
+
The SDK only depends on the OpenTelemetry **API**, never on an SDK or exporter. If your application does not register an OpenTelemetry SDK, all tracing code is a silent no-op with no overhead and no behavior change.
|
|
17
|
+
|
|
18
|
+
## Enabling tracing
|
|
19
|
+
|
|
20
|
+
Register any OpenTelemetry Node SDK in your application. On Vercel with Next.js, the simplest setup is [`@vercel/otel`](https://vercel.com/docs/observability/otel-overview) in `instrumentation.ts`:
|
|
21
|
+
|
|
22
|
+
```typescript title="instrumentation.ts" lineNumbers
|
|
23
|
+
import { registerOTel } from "@vercel/otel"
|
|
24
|
+
|
|
25
|
+
export function register() {
|
|
26
|
+
registerOTel({ serviceName: "my-app" })
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
No workflow-specific configuration is required. As soon as a tracer provider and propagator are registered, the SDK's spans, context propagation, and span links activate automatically.
|
|
31
|
+
|
|
32
|
+
## Spans
|
|
33
|
+
|
|
34
|
+
| Span name | Kind | Emitted when |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| `workflow.start <name>` | internal | `start()` is called in your application code |
|
|
37
|
+
| `workflow.execute <name>` | consumer (root) | a queue delivery invokes the workflow — replay, orchestration, and inline steps run under it |
|
|
38
|
+
| `step.execute <name>` | internal (inline) / consumer + root (queue-delivered) | a step function executes |
|
|
39
|
+
| `http <method>` | client | the SDK calls the workflow backend (event reads/writes) |
|
|
40
|
+
|
|
41
|
+
`<name>` is the short function name (for example `processOrder`); the full machine name, including the source module, is available in the `workflow.name` / `step.name` attributes.
|
|
42
|
+
|
|
43
|
+
## Key attributes
|
|
44
|
+
|
|
45
|
+
| Attribute | Description |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `workflow.run.id` | The run ID (`wrun_...`). Present on every workflow and step span — the primary key for finding all spans of a run. |
|
|
48
|
+
| `workflow.name` | The workflow function name. |
|
|
49
|
+
| `workflow.trace.mode` | The active trace mode (`linked` or `continuous`). |
|
|
50
|
+
| `workflow.trace.propagated` | Whether the invocation received trace context from the queue message. |
|
|
51
|
+
| `workflow.queue.overhead_ms` | Time between the message being enqueued and the handler starting — queue dwell plus any cold start. |
|
|
52
|
+
|
|
53
|
+
## Trace shape: one trace per invocation
|
|
54
|
+
|
|
55
|
+
A single workflow run can span hours or days across many separate function invocations: every step completion, `sleep()` wake-up, and retry is a new queue delivery. Stitching all of that into one trace produces giant, slow-loading traces that most tracing backends truncate.
|
|
56
|
+
|
|
57
|
+
Instead, the SDK creates **one bounded trace per invocation**. Each `workflow.execute` (or background `step.execute`) span starts a new trace root and attaches two **span links**:
|
|
58
|
+
|
|
59
|
+
- a link to the **enqueue site** — the span that queued the message which triggered this invocation, and
|
|
60
|
+
- a link to the **run origin** — the trace in which `start()` was originally called.
|
|
61
|
+
|
|
62
|
+
A span link is OpenTelemetry's relationship for "causally related, but in a different trace." It is the standard pattern for asynchronous messaging, where producing and consuming a message can be separated by arbitrary time.
|
|
63
|
+
|
|
64
|
+
```mermaid
|
|
65
|
+
flowchart LR
|
|
66
|
+
O["start() request trace"]
|
|
67
|
+
A["invocation 1"]
|
|
68
|
+
B["invocation 2"]
|
|
69
|
+
C["invocation 3 ..."]
|
|
70
|
+
A -. "link" .-> O
|
|
71
|
+
B -. "link" .-> O
|
|
72
|
+
C -. "link" .-> O
|
|
73
|
+
B -. "link" .-> A
|
|
74
|
+
C -. "link" .-> B
|
|
75
|
+
|
|
76
|
+
style O fill:#a78bfa,stroke:#8b5cf6,color:#000
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Each invocation links back to the trace that enqueued it and to the run origin.
|
|
80
|
+
|
|
81
|
+
To see a whole run, query by attribute rather than by trace ID — for example `workflow.run.id = wrun_...` in your tracing backend — or follow the span links between invocation traces.
|
|
82
|
+
|
|
83
|
+
## Trace modes
|
|
84
|
+
|
|
85
|
+
The `WORKFLOW_TRACE_MODE` environment variable controls the shape:
|
|
86
|
+
|
|
87
|
+
| Mode | Behavior |
|
|
88
|
+
| --- | --- |
|
|
89
|
+
| `linked` (default) | Each invocation is its own trace root with span links to the enqueue site and the run origin. Traces stay small; sampling is decided per invocation. |
|
|
90
|
+
| `continuous` | The run-origin context becomes the **parent** of every invocation, so the entire run shares one trace ID. |
|
|
91
|
+
|
|
92
|
+
<Callout type="warn">
|
|
93
|
+
This is a behavior change from v4, which always used `continuous`-style tracing. If you have dashboards or queries that assume one trace ID per run, either update them to use `workflow.run.id` and span links, or set `WORKFLOW_TRACE_MODE=continuous` to restore the previous shape. Note that in `linked` mode each invocation root makes its own sampling decision, and the number of root spans increases to one per invocation.
|
|
94
|
+
</Callout>
|
|
95
|
+
|
|
96
|
+
## Context propagation
|
|
97
|
+
|
|
98
|
+
When tracing is enabled, the SDK propagates [W3C Trace Context](https://www.w3.org/TR/trace-context/) on its outbound calls:
|
|
99
|
+
|
|
100
|
+
- **Backend requests** carry `traceparent`, `tracestate`, and `baggage` headers, so backend spans can join your trace.
|
|
101
|
+
- **Queue messages** carry the run-origin trace context in the message payload, and the queue re-delivers the producer's context to the workflow handler, where it becomes the enqueue-site span link.
|
|
102
|
+
- **Baggage** carries `workflow.run_id` and `workflow.name` entries during workflow execution, allowing downstream services you call from steps to tag their own telemetry with the run ID.
|
|
103
|
+
|
|
104
|
+
<Callout>
|
|
105
|
+
Baggage entries set by your application are propagated as a `baggage` HTTP header on the SDK's backend requests, like any other OpenTelemetry-instrumented HTTP call. Avoid placing sensitive values in baggage.
|
|
106
|
+
</Callout>
|
package/docs/testing/index.mdx
CHANGED
|
@@ -109,7 +109,7 @@ That's it. The plugin automatically:
|
|
|
109
109
|
|
|
110
110
|
1. Transforms `"use workflow"` and `"use step"` directives via SWC
|
|
111
111
|
2. Builds workflow and step bundles before tests run
|
|
112
|
-
3. Sets up an in-process workflow runtime using a fresh [Local World](/
|
|
112
|
+
3. Sets up an in-process workflow runtime using a fresh [Local World](/worlds/local) instance in each test worker — all workflow data is cleared automatically between test files for full isolation
|
|
113
113
|
|
|
114
114
|
<Callout type="info">
|
|
115
115
|
Use a separate Vitest configuration and a distinct file naming convention (e.g. `*.integration.test.ts`) to keep unit tests and integration tests separate. Unit tests run with a standard Vitest config without the workflow plugin, while integration tests use the config above.
|
|
@@ -117,7 +117,7 @@ Use a separate Vitest configuration and a distinct file naming convention (e.g.
|
|
|
117
117
|
|
|
118
118
|
### Writing Integration Tests
|
|
119
119
|
|
|
120
|
-
Use [`start()`](/docs/api-reference/workflow-api/start) to trigger a workflow and [`run.returnValue`](/docs/api-reference/workflow-api/start#
|
|
120
|
+
Use [`start()`](/docs/api-reference/workflow-api/start) to trigger a workflow and [`run.returnValue`](/docs/api-reference/workflow-api/start#returns) to get the result. `returnValue` is a promise that blocks until the workflow completes (or throws if it fails):
|
|
121
121
|
|
|
122
122
|
```typescript title="workflows/calculate.integration.test.ts" lineNumbers
|
|
123
123
|
import { describe, it, expect } from "vitest";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "workflow",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.20",
|
|
4
4
|
"description": "Workflow SDK - Build durable, resilient, and observable workflows",
|
|
5
5
|
"main": "dist/typescript-plugin.cjs",
|
|
6
6
|
"type": "module",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
".": {
|
|
30
30
|
"types": "./dist/index.d.ts",
|
|
31
31
|
"workflow": "./dist/workflow.js",
|
|
32
|
+
"node": "./dist/index.js",
|
|
32
33
|
"require": "./dist/typescript-plugin.cjs",
|
|
33
34
|
"default": "./dist/index.js"
|
|
34
35
|
},
|
|
@@ -56,18 +57,18 @@
|
|
|
56
57
|
},
|
|
57
58
|
"dependencies": {
|
|
58
59
|
"ms": "2.1.3",
|
|
59
|
-
"@workflow/astro": "5.0.0-beta.
|
|
60
|
-
"@workflow/
|
|
61
|
-
"@workflow/
|
|
62
|
-
"@workflow/
|
|
63
|
-
"@workflow/typescript-plugin": "5.0.0-beta.
|
|
64
|
-
"@workflow/
|
|
65
|
-
"@workflow/
|
|
66
|
-
"@workflow/
|
|
67
|
-
"@workflow/
|
|
68
|
-
"@workflow/
|
|
69
|
-
"@workflow/sveltekit": "5.0.0-beta.
|
|
70
|
-
"@workflow/rollup": "5.0.0-beta.
|
|
60
|
+
"@workflow/astro": "5.0.0-beta.20",
|
|
61
|
+
"@workflow/cli": "5.0.0-beta.20",
|
|
62
|
+
"@workflow/core": "5.0.0-beta.20",
|
|
63
|
+
"@workflow/errors": "5.0.0-beta.8",
|
|
64
|
+
"@workflow/typescript-plugin": "5.0.0-beta.4",
|
|
65
|
+
"@workflow/utils": "5.0.0-beta.4",
|
|
66
|
+
"@workflow/next": "5.0.0-beta.20",
|
|
67
|
+
"@workflow/nest": "5.0.0-beta.20",
|
|
68
|
+
"@workflow/nitro": "5.0.0-beta.20",
|
|
69
|
+
"@workflow/nuxt": "5.0.0-beta.20",
|
|
70
|
+
"@workflow/sveltekit": "5.0.0-beta.20",
|
|
71
|
+
"@workflow/rollup": "5.0.0-beta.20"
|
|
71
72
|
},
|
|
72
73
|
"devDependencies": {
|
|
73
74
|
"@types/ms": "2.1.0",
|