workflow 5.0.0-beta.43 → 5.0.0-beta.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/internal/builtins.d.ts +4 -4
- package/dist/internal/builtins.js +6 -6
- package/docs/ai/chat-session-modeling.mdx +23 -24
- package/docs/ai/defining-tools.mdx +5 -6
- package/docs/ai/human-in-the-loop.mdx +11 -11
- package/docs/ai/index.mdx +20 -20
- package/docs/ai/message-queueing.mdx +6 -6
- package/docs/ai/meta.json +1 -0
- package/docs/ai/resumable-streams.mdx +28 -28
- package/docs/ai/sleep-and-delays.mdx +9 -9
- package/docs/ai/streaming-updates-from-tools.mdx +4 -4
- package/docs/api-reference/vitest/index.mdx +8 -8
- package/docs/api-reference/workflow/create-hook.mdx +15 -15
- package/docs/api-reference/workflow/create-webhook.mdx +15 -15
- package/docs/api-reference/workflow/define-hook.mdx +10 -10
- package/docs/api-reference/workflow/fatal-error.mdx +2 -2
- package/docs/api-reference/workflow/fetch.mdx +7 -7
- 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 +1 -1
- package/docs/api-reference/workflow/retryable-error.mdx +1 -1
- package/docs/api-reference/workflow/set-attributes.mdx +2 -2
- package/docs/api-reference/workflow/sleep.mdx +3 -3
- package/docs/api-reference/workflow-ai/durable-agent.mdx +42 -42
- package/docs/api-reference/workflow-ai/index.mdx +3 -3
- package/docs/api-reference/workflow-ai/workflow-chat-transport.mdx +28 -28
- package/docs/api-reference/workflow-api/get-hook-by-token.mdx +11 -11
- package/docs/api-reference/workflow-api/get-run.mdx +10 -10
- package/docs/api-reference/workflow-api/index.mdx +2 -4
- package/docs/api-reference/workflow-api/resume-hook.mdx +14 -14
- package/docs/api-reference/workflow-api/resume-webhook.mdx +3 -3
- package/docs/api-reference/workflow-api/start.mdx +16 -15
- package/docs/api-reference/workflow-astro/workflow.mdx +3 -3
- package/docs/api-reference/workflow-errors/entity-conflict-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/hook-conflict-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/hook-not-found-error.mdx +8 -8
- package/docs/api-reference/workflow-errors/index.mdx +6 -6
- package/docs/api-reference/workflow-errors/precondition-failed-error.mdx +9 -9
- package/docs/api-reference/workflow-errors/run-expired-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/run-not-supported-error.mdx +4 -4
- 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 +4 -4
- 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 +4 -4
- package/docs/api-reference/workflow-errors/workflow-run-not-found-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/workflow-runtime-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/workflow-world-error.mdx +8 -8
- package/docs/api-reference/workflow-globals.mdx +12 -11
- package/docs/api-reference/workflow-nest/configure-workflow-controller.mdx +2 -2
- package/docs/api-reference/workflow-nest/nest-local-builder.mdx +4 -4
- package/docs/api-reference/workflow-nest/workflow-controller.mdx +1 -1
- package/docs/api-reference/workflow-nest/workflow-module.mdx +4 -4
- package/docs/api-reference/workflow-next/with-workflow.mdx +16 -16
- package/docs/api-reference/workflow-nitro/index.mdx +6 -6
- package/docs/api-reference/workflow-nuxt/index.mdx +4 -4
- package/docs/api-reference/workflow-observability/hydrate-data.mdx +5 -5
- package/docs/api-reference/workflow-observability/hydrate-resource-io.mdx +4 -4
- package/docs/api-reference/workflow-observability/index.mdx +6 -8
- package/docs/api-reference/workflow-observability/observability-revivers.mdx +2 -2
- package/docs/api-reference/workflow-observability/parse-class-name.mdx +3 -3
- package/docs/api-reference/workflow-observability/parse-step-name.mdx +4 -4
- package/docs/api-reference/workflow-observability/parse-workflow-name.mdx +4 -4
- package/docs/api-reference/workflow-runtime/create-world.mdx +8 -8
- package/docs/api-reference/workflow-runtime/get-world-handlers.mdx +6 -6
- package/docs/api-reference/workflow-runtime/get-world.mdx +4 -4
- package/docs/api-reference/workflow-runtime/health-check.mdx +1 -1
- package/docs/api-reference/workflow-runtime/index.mdx +2 -4
- package/docs/api-reference/workflow-runtime/set-world.mdx +15 -13
- package/docs/api-reference/workflow-runtime/workflow-entrypoint.mdx +6 -5
- package/docs/api-reference/workflow-runtime/world/analytics.mdx +9 -9
- package/docs/api-reference/workflow-runtime/world/index.mdx +5 -3
- package/docs/api-reference/workflow-runtime/world/queue.mdx +11 -11
- package/docs/api-reference/workflow-runtime/world/storage.mdx +62 -28
- package/docs/api-reference/workflow-runtime/world/streams.mdx +7 -7
- package/docs/api-reference/workflow-serde/index.mdx +1 -1
- package/docs/api-reference/workflow-serde/workflow-deserialize.mdx +2 -2
- package/docs/api-reference/workflow-serde/workflow-serialize.mdx +7 -7
- package/docs/api-reference/workflow-sveltekit/workflow-plugin.mdx +3 -3
- package/docs/api-reference/workflow-vite/workflow.mdx +5 -5
- package/docs/changelog/attributes-mvp.mdx +39 -39
- package/docs/changelog/batched-event-writes.mdx +12 -12
- package/docs/changelog/eager-processing.mdx +63 -63
- package/docs/changelog/index.mdx +3 -3
- package/docs/changelog/lazy-event-creation.md +27 -27
- package/docs/changelog/resilient-resume.mdx +5 -5
- package/docs/changelog/resilient-start.mdx +14 -14
- package/docs/changelog/step-message-ownership.mdx +47 -47
- package/docs/changelog/turbo-mode.md +20 -20
- package/docs/comparisons/index.mdx +13 -13
- package/docs/comparisons/workflow-sdk-vs-aws-agentcore.mdx +15 -15
- package/docs/comparisons/workflow-sdk-vs-aws-step-functions.mdx +12 -12
- package/docs/comparisons/workflow-sdk-vs-cloudflare-workflows.mdx +11 -11
- package/docs/comparisons/workflow-sdk-vs-inngest.mdx +19 -19
- package/docs/comparisons/workflow-sdk-vs-temporal.mdx +23 -23
- package/docs/comparisons/workflow-sdk-vs-trigger-dev.mdx +18 -17
- package/docs/configuration/build-and-diagnostics.mdx +5 -5
- package/docs/configuration/cli-and-web-ui.mdx +4 -4
- package/docs/configuration/runtime-tuning.mdx +86 -23
- package/docs/configuration/worlds.mdx +28 -14
- package/docs/cookbook/advanced/child-workflows.mdx +25 -25
- package/docs/cookbook/advanced/publishing-libraries.mdx +40 -40
- package/docs/cookbook/advanced/serializable-steps.mdx +21 -21
- package/docs/cookbook/advanced/upgrading-workflows.mdx +31 -31
- package/docs/cookbook/agent-patterns/agent-cancellation.mdx +20 -20
- package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +22 -22
- package/docs/cookbook/common-patterns/batching.mdx +14 -14
- package/docs/cookbook/common-patterns/idempotency.mdx +9 -9
- package/docs/cookbook/common-patterns/rate-limiting.mdx +3 -3
- package/docs/cookbook/common-patterns/saga.mdx +19 -19
- package/docs/cookbook/common-patterns/scheduling.mdx +23 -23
- package/docs/cookbook/common-patterns/sequential-and-parallel.mdx +26 -26
- package/docs/cookbook/common-patterns/timeouts.mdx +23 -23
- package/docs/cookbook/common-patterns/webhooks.mdx +6 -6
- package/docs/cookbook/common-patterns/workflow-composition.mdx +19 -19
- package/docs/cookbook/index.mdx +22 -22
- package/docs/cookbook/integrations/ai-sdk.mdx +43 -41
- package/docs/cookbook/integrations/chat-sdk.mdx +34 -34
- package/docs/cookbook/integrations/sandbox.mdx +46 -46
- package/docs/deploying.mdx +15 -15
- package/docs/errors/abort-signal-timeout-in-workflow.mdx +12 -12
- package/docs/errors/corrupted-event-log.mdx +11 -11
- package/docs/errors/deployment-mismatch.mdx +14 -14
- package/docs/errors/fetch-in-workflow.mdx +8 -8
- package/docs/errors/hook-conflict.mdx +11 -11
- package/docs/errors/index.mdx +1 -1
- package/docs/errors/node-js-module-in-workflow.mdx +5 -5
- package/docs/errors/replay-divergence.mdx +2 -2
- package/docs/errors/runtime-decryption-failed.mdx +12 -12
- package/docs/errors/serialization-failed.mdx +40 -12
- package/docs/errors/start-invalid-workflow-function.mdx +5 -5
- package/docs/errors/step-executed-multiple-times.mdx +2 -2
- package/docs/errors/step-not-registered.mdx +5 -5
- package/docs/errors/timeout-in-workflow.mdx +8 -8
- package/docs/errors/webhook-invalid-respond-with-value.mdx +18 -18
- package/docs/errors/webhook-response-not-sent.mdx +16 -16
- package/docs/errors/workflow-not-registered.mdx +5 -5
- package/docs/foundations/cancellation.mdx +31 -31
- package/docs/foundations/errors-and-retries.mdx +42 -11
- package/docs/foundations/hooks.mdx +35 -35
- package/docs/foundations/idempotency.mdx +9 -9
- package/docs/foundations/serialization.mdx +21 -22
- package/docs/foundations/starting-workflows.mdx +36 -37
- package/docs/foundations/streaming.mdx +46 -41
- package/docs/foundations/versioning.mdx +3 -3
- package/docs/foundations/workflows-and-steps.mdx +9 -9
- package/docs/getting-started/astro.mdx +16 -16
- package/docs/getting-started/express.mdx +8 -8
- package/docs/getting-started/fastify.mdx +8 -8
- package/docs/getting-started/hono.mdx +8 -8
- package/docs/getting-started/nestjs.mdx +18 -17
- package/docs/getting-started/next.mdx +11 -11
- package/docs/getting-started/nitro.mdx +16 -16
- package/docs/getting-started/nuxt.mdx +8 -8
- package/docs/getting-started/python.mdx +4 -4
- package/docs/getting-started/react-router/v7.mdx +1 -1
- package/docs/getting-started/react-router/v8.mdx +1 -1
- package/docs/getting-started/sveltekit.mdx +14 -14
- package/docs/getting-started/tanstack-start.mdx +12 -12
- package/docs/getting-started/vite.mdx +8 -8
- package/docs/how-it-works/cancellation.mdx +62 -62
- package/docs/how-it-works/code-transform.mdx +66 -54
- package/docs/how-it-works/encryption.mdx +25 -21
- package/docs/how-it-works/event-sourcing.mdx +53 -35
- package/docs/how-it-works/framework-integrations.mdx +12 -12
- package/docs/how-it-works/understanding-directives.mdx +21 -21
- package/docs/internal/index.mdx +6 -6
- package/docs/internal/nitro-native-build.mdx +2 -2
- package/docs/internal/nitro-web-ui.mdx +4 -4
- package/docs/internal/serializable-abort-controller.mdx +7 -7
- package/docs/observability/attributes.mdx +3 -3
- package/docs/observability/index.mdx +14 -10
- package/docs/observability/tracing.mdx +10 -10
- package/docs/testing/index.mdx +33 -33
- package/docs/testing/server-based.mdx +10 -10
- package/docs/whats-new.mdx +185 -0
- package/package.json +12 -12
|
@@ -6,19 +6,19 @@ type: overview
|
|
|
6
6
|
|
|
7
7
|
# Local web UI in Nitro dev
|
|
8
8
|
|
|
9
|
-
{/* TODO: unreleased
|
|
9
|
+
{/* TODO: unreleased. Changeset .changeset/nitro-dashboard-route.md is pending; ships in @workflow/nitro 5.0.0. Update this date on publish. */}
|
|
10
10
|
<span className="text-sm text-fd-muted-foreground">June 2, 2026</span>
|
|
11
11
|
|
|
12
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
13
|
|
|
14
14
|
## What's new
|
|
15
15
|
|
|
16
|
-
- **Built-in `/_workflow` route in development.** The route starts the local web UI and redirects to it
|
|
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
17
|
- **Inspect runs in place.** Inspect, monitor, and debug your workflow runs directly from the dev server you're already running.
|
|
18
18
|
|
|
19
19
|

|
|
20
20
|
|
|
21
21
|
## Learn more
|
|
22
22
|
|
|
23
|
-
- [Observability](/docs/observability)
|
|
24
|
-
- [Nitro](/docs/getting-started/nitro)
|
|
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
|
|
@@ -11,7 +11,7 @@ related:
|
|
|
11
11
|
- /docs/api-reference/workflow-errors/workflow-world-error
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
[`setAttributes`](/docs/api-reference/workflow/set-attributes) attaches plaintext string metadata to the current workflow run. These attributes
|
|
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 use them to search and filter runs through the [Analytics API](/docs/api-reference/workflow-runtime/world/analytics).
|
|
15
15
|
|
|
16
16
|
You can also seed any attributes directly when starting a run:
|
|
17
17
|
|
|
@@ -76,13 +76,13 @@ Each `setAttributes` call appears on the trace timeline as a diamond marker at t
|
|
|
76
76
|
|
|
77
77
|

|
|
78
78
|
|
|
79
|
-
Expanding an `attr_set` event
|
|
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
80
|
|
|
81
81
|

|
|
82
82
|
|
|
83
83
|
## Searching and filtering by attributes
|
|
84
84
|
|
|
85
|
-
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
|
|
85
|
+
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:
|
|
86
86
|
|
|
87
87
|
```typescript lineNumbers
|
|
88
88
|
import { getWorld } from "workflow/runtime";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Observability
|
|
3
|
-
description: Inspect, monitor, and debug workflows through the CLI and
|
|
3
|
+
description: Inspect, monitor, and debug workflows through the CLI and web UI.
|
|
4
4
|
type: overview
|
|
5
|
-
summary: Inspect and debug workflow runs using the CLI and
|
|
5
|
+
summary: Inspect and debug workflow runs using the CLI and web UI.
|
|
6
6
|
prerequisites:
|
|
7
7
|
- /docs/foundations
|
|
8
8
|
related:
|
|
@@ -10,9 +10,9 @@ 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
|
|
@@ -33,20 +33,24 @@ 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
|
+
needed.
|
|
48
|
+
|
|
45
49
|
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.
|
|
46
50
|
|
|
47
51
|
To share a link to a specific run without opening a browser, use the `--url`
|
|
48
52
|
flag. It prints the dashboard deep link to stdout and exits (no browser, no
|
|
49
|
-
local server)
|
|
53
|
+
local server), which is useful for scripts, PR comments, or automation. Add `--json` to
|
|
50
54
|
get `{ "url": "..." }`.
|
|
51
55
|
|
|
52
56
|
```bash
|
|
@@ -68,9 +72,9 @@ If you're deploying workflows to a production environment, but want to inspect t
|
|
|
68
72
|
Backends might require additional configuration. If you're missing environment variables, the World package should provide instructions on how to configure it.
|
|
69
73
|
</Callout>
|
|
70
74
|
|
|
71
|
-
### Vercel
|
|
75
|
+
### Vercel backend
|
|
72
76
|
|
|
73
|
-
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,
|
|
77
|
+
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`.
|
|
74
78
|
|
|
75
79
|
```bash
|
|
76
80
|
# Inspect workflows running on Vercel
|
|
@@ -79,6 +83,6 @@ npx workflow inspect runs --backend vercel
|
|
|
79
83
|
|
|
80
84
|
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.
|
|
81
85
|
|
|
82
|
-
## More
|
|
86
|
+
## More observability features
|
|
83
87
|
|
|
84
88
|
<AutoCards />
|
|
@@ -11,7 +11,7 @@ related:
|
|
|
11
11
|
- /docs/how-it-works/event-sourcing
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
The Workflow SDK
|
|
14
|
+
The Workflow SDK includes [OpenTelemetry](https://opentelemetry.io) instrumentation. 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
15
|
|
|
16
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
17
|
|
|
@@ -30,7 +30,7 @@ export function register() {
|
|
|
30
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
31
|
|
|
32
32
|
<Callout>
|
|
33
|
-
`@opentelemetry/api` is an **optional peer dependency**. An OpenTelemetry SDK such as `@vercel/otel` normally pulls it in transitively, but installing it directly (`npm i @opentelemetry/api`) guarantees it is present in your build
|
|
33
|
+
`@opentelemetry/api` is an **optional peer dependency**. An OpenTelemetry SDK such as `@vercel/otel` normally pulls it in transitively, but installing it directly (`npm i @opentelemetry/api`) guarantees it is present in your build, particularly for bundled or serverless targets where the SDK's tracing is inlined at build time. If it can't be resolved, tracing is a silent no-op.
|
|
34
34
|
</Callout>
|
|
35
35
|
|
|
36
36
|
## Spans
|
|
@@ -38,7 +38,7 @@ No workflow-specific configuration is required. As soon as a tracer provider and
|
|
|
38
38
|
| Span name | Kind | Emitted when |
|
|
39
39
|
| --- | --- | --- |
|
|
40
40
|
| `workflow.start <name>` | internal | `start()` is called in your application code |
|
|
41
|
-
| `workflow.execute <name>` | consumer (root) | a queue delivery invokes the workflow
|
|
41
|
+
| `workflow.execute <name>` | consumer (root) | a queue delivery invokes the workflow; replay, orchestration, and inline steps run under it |
|
|
42
42
|
| `step.execute <name>` | internal (inline) / consumer + root (queue-delivered) | a step function executes |
|
|
43
43
|
| `http <method>` | client | the SDK calls the workflow backend (event reads/writes) |
|
|
44
44
|
| `workflow.stream.write` | client | a stream chunk (or the stream close) is flushed to the backend |
|
|
@@ -56,11 +56,11 @@ Stream spans are emitted by the SDK's world backend on the client that writes or
|
|
|
56
56
|
|
|
57
57
|
| Attribute | Description |
|
|
58
58
|
| --- | --- |
|
|
59
|
-
| `workflow.run.id` | The run ID (`wrun_...`). Present on every workflow and step span
|
|
59
|
+
| `workflow.run.id` | The run ID (`wrun_...`). Present on every workflow and step span; the primary key for finding all spans of a run. |
|
|
60
60
|
| `workflow.name` | The workflow function name. |
|
|
61
61
|
| `workflow.trace.mode` | The active trace mode (`linked` or `continuous`). |
|
|
62
62
|
| `workflow.trace.propagated` | Whether the invocation received trace context from the queue message. |
|
|
63
|
-
| `workflow.queue.overhead_ms` | Time between the message being enqueued and the handler starting
|
|
63
|
+
| `workflow.queue.overhead_ms` | Time between the message being enqueued and the handler starting: queue dwell plus any cold start. |
|
|
64
64
|
| `workflow.stream.name` | The stream name, on stream write/read spans. |
|
|
65
65
|
| `workflow.stream.operation` | The stream operation: `write`, `write_multi`, `close`, `read`, or `flush`. |
|
|
66
66
|
| `workflow.stream.write.chunk_rtt` | Time between emissions of a chunk to the wire, and receiving the `ack` message for that chunk. Also stamped on `workflow.stream.flush` (the batch's write RPC duration, network included). |
|
|
@@ -70,12 +70,12 @@ Stream spans are emitted by the SDK's world backend on the client that writes or
|
|
|
70
70
|
|
|
71
71
|
## Trace shape: one trace per invocation
|
|
72
72
|
|
|
73
|
-
A single workflow run can span hours or days across many separate function invocations: every
|
|
73
|
+
A single workflow run can span hours or days across many separate function invocations: every `sleep()` wake-up, hook resume, retry, and queued step continuation is a new queue delivery. Stitching all of that into one trace produces giant, slow-loading traces that most tracing backends truncate.
|
|
74
74
|
|
|
75
75
|
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**:
|
|
76
76
|
|
|
77
|
-
- a link to the **enqueue site
|
|
78
|
-
- a link to the **run origin
|
|
77
|
+
- a link to the **enqueue site**, the span that queued the message which triggered this invocation, and
|
|
78
|
+
- a link to the **run origin**, the trace in which `start()` was originally called.
|
|
79
79
|
|
|
80
80
|
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.
|
|
81
81
|
|
|
@@ -96,7 +96,7 @@ flowchart LR
|
|
|
96
96
|
|
|
97
97
|
Each invocation links back to the trace that enqueued it and to the run origin.
|
|
98
98
|
|
|
99
|
-
To see a whole run, query by attribute rather than by trace ID
|
|
99
|
+
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.
|
|
100
100
|
|
|
101
101
|
## Trace modes
|
|
102
102
|
|
|
@@ -108,7 +108,7 @@ The `WORKFLOW_TRACE_MODE` environment variable controls the shape:
|
|
|
108
108
|
| `continuous` | The run-origin context becomes the **parent** of every invocation, so the entire run shares one trace ID. |
|
|
109
109
|
|
|
110
110
|
<Callout type="warn">
|
|
111
|
-
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.
|
|
111
|
+
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. In `linked` mode, each invocation root makes its own sampling decision, and the number of root spans increases to one per invocation.
|
|
112
112
|
</Callout>
|
|
113
113
|
|
|
114
114
|
## Context propagation
|
package/docs/testing/index.mdx
CHANGED
|
@@ -3,18 +3,18 @@ title: Testing
|
|
|
3
3
|
description: Unit test individual steps and integration test entire workflows using Vitest.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Test steps like any other JavaScript function, or use the Workflow SDK Vitest plugin to run complete workflows in-process without a server.
|
|
7
7
|
|
|
8
8
|
This guide covers two approaches:
|
|
9
9
|
|
|
10
|
-
1. **Unit testing
|
|
11
|
-
2. **Integration testing
|
|
10
|
+
1. **Unit testing**: Test individual steps as plain functions without the workflow runtime.
|
|
11
|
+
2. **Integration testing**: Test entire workflows in-process using the `workflow()` Vitest plugin. Use integration tests for workflow-specific code paths that use [hooks](/docs/foundations/hooks), webhooks, [`sleep()`](/docs/api-reference/workflow/sleep), or retries.
|
|
12
12
|
|
|
13
|
-
## Unit
|
|
13
|
+
## Unit testing steps
|
|
14
14
|
|
|
15
|
-
Without the workflow compiler, the `"use step"` directive is a no-op. Your step functions run as regular JavaScript functions,
|
|
15
|
+
Without the workflow compiler, the `"use step"` directive is a no-op. Your step functions run as regular JavaScript functions, so you can unit test them without special configuration.
|
|
16
16
|
|
|
17
|
-
### Example
|
|
17
|
+
### Example steps
|
|
18
18
|
|
|
19
19
|
Given a workflow file with step functions like this:
|
|
20
20
|
|
|
@@ -49,7 +49,7 @@ export async function sendOnboardingEmail(user: { id: string; email: string }) {
|
|
|
49
49
|
}
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
### Writing
|
|
52
|
+
### Writing unit tests for steps
|
|
53
53
|
|
|
54
54
|
You can import and test step functions directly with Vitest. No special configuration or workflow plugin is needed:
|
|
55
55
|
|
|
@@ -77,18 +77,18 @@ describe("sendWelcomeEmail step", () => {
|
|
|
77
77
|
This approach is ideal for verifying the business logic inside individual steps in isolation.
|
|
78
78
|
|
|
79
79
|
<Callout type="info">
|
|
80
|
-
Unit testing works well for individual steps. A
|
|
80
|
+
Unit testing works well for individual steps. A workflow that only calls steps can also be unit tested this way, since `"use workflow"` is similarly a no-op without the compiler. However, any workflow that uses runtime features like [`sleep()`](/docs/api-reference/workflow/sleep), [hooks](/docs/foundations/hooks), or [webhooks](/docs/foundations/hooks#understanding-webhooks) cannot be unit tested directly because those APIs require the workflow runtime. Use [integration testing](#integration-testing-with-the-vitest-plugin) for testing entire workflows, especially those that depend on workflow-only features.
|
|
81
81
|
</Callout>
|
|
82
82
|
|
|
83
|
-
## Integration
|
|
83
|
+
## Integration testing with the Vitest plugin
|
|
84
84
|
|
|
85
|
-
For workflows that rely on runtime features like [hooks](/docs/foundations/hooks), [webhooks](/docs/foundations/hooks#understanding-webhooks), [`sleep()`](/docs/api-reference/workflow/sleep), or error retries, you need to test against a real workflow setup. The `@workflow/vitest` plugin handles everything automatically
|
|
85
|
+
For workflows that rely on runtime features like [hooks](/docs/foundations/hooks), [webhooks](/docs/foundations/hooks#understanding-webhooks), [`sleep()`](/docs/api-reference/workflow/sleep), or error retries, you need to test against a real workflow setup. The `@workflow/vitest` plugin handles everything automatically: it compiles your workflow directives, builds the runtime bundles, and executes workflows entirely in-process. No server required.
|
|
86
86
|
|
|
87
87
|
<Callout type="warn">
|
|
88
88
|
`vi.mock()` and related calls do _not_ work inside workflow functions, only step functions. Your workflow functions cannot import third party code that needs to be mocked. Mocking works for npm packages imported in step functions. If something needs to be mocked, it likely belongs inside a step function either way.
|
|
89
89
|
</Callout>
|
|
90
90
|
|
|
91
|
-
### Vitest
|
|
91
|
+
### Vitest configuration
|
|
92
92
|
|
|
93
93
|
Create a separate Vitest config for integration tests that includes the `workflow()` plugin:
|
|
94
94
|
|
|
@@ -109,13 +109,13 @@ 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](/worlds/local) instance in each test worker
|
|
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.
|
|
116
116
|
</Callout>
|
|
117
117
|
|
|
118
|
-
### Writing
|
|
118
|
+
### Writing integration tests
|
|
119
119
|
|
|
120
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
|
|
|
@@ -144,9 +144,9 @@ describe("calculateWorkflow", () => {
|
|
|
144
144
|
});
|
|
145
145
|
```
|
|
146
146
|
|
|
147
|
-
### Testing
|
|
147
|
+
### Testing hooks and waits
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
Integration testing is most useful for workflow-only features. You can resume hooks and waits programmatically using the [`workflow/api`](/docs/api-reference/workflow-api) functions to simulate external events in your tests.
|
|
150
150
|
|
|
151
151
|
Given a workflow that waits for approval via a hook, then sleeps before publishing:
|
|
152
152
|
|
|
@@ -228,7 +228,7 @@ describe("approvalWorkflow", () => {
|
|
|
228
228
|
reviewer: "bob",
|
|
229
229
|
});
|
|
230
230
|
|
|
231
|
-
// No wakeUp() needed here
|
|
231
|
+
// No wakeUp() needed here; the rejected path has no sleep
|
|
232
232
|
const result = await run.returnValue;
|
|
233
233
|
expect(result).toEqual({
|
|
234
234
|
status: "rejected",
|
|
@@ -243,12 +243,12 @@ describe("approvalWorkflow", () => {
|
|
|
243
243
|
</Callout>
|
|
244
244
|
|
|
245
245
|
<Callout type="info">
|
|
246
|
-
`waitForSleep()` returns the first **pending** sleep
|
|
246
|
+
`waitForSleep()` returns the first **pending** sleep, one that has a `wait_created` event but no corresponding `wait_completed` event. If your workflow has multiple parallel sleeps, `waitForSleep()` returns whichever is found first. After waking one, call `waitForSleep()` again to get the next pending one. For sequential sleeps, `waitForSleep()` naturally returns each one as the workflow reaches it.
|
|
247
247
|
</Callout>
|
|
248
248
|
|
|
249
|
-
### Testing
|
|
249
|
+
### Testing webhooks
|
|
250
250
|
|
|
251
|
-
Webhooks are hooks that receive HTTP `Request` objects. In tests, resume them using [`resumeWebhook()`](/docs/api-reference/workflow-api/resume-webhook) with a `Request` payload
|
|
251
|
+
Webhooks are hooks that receive HTTP `Request` objects. In tests, resume them using [`resumeWebhook()`](/docs/api-reference/workflow-api/resume-webhook) with a `Request` payload, with no HTTP server needed:
|
|
252
252
|
|
|
253
253
|
```typescript title="workflows/ingest.ts" lineNumbers
|
|
254
254
|
import { createWebhook } from "workflow";
|
|
@@ -303,7 +303,7 @@ describe("ingestWorkflow", () => {
|
|
|
303
303
|
});
|
|
304
304
|
```
|
|
305
305
|
|
|
306
|
-
### Manual
|
|
306
|
+
### Manual setup
|
|
307
307
|
|
|
308
308
|
If you need more control over the test lifecycle, the plugin also exports the individual setup functions:
|
|
309
309
|
|
|
@@ -354,11 +354,11 @@ afterAll(async () => {
|
|
|
354
354
|
For advanced setups that require a running server (e.g. testing against your actual framework's HTTP layer), see [Server-based integration testing](/docs/testing/server-based).
|
|
355
355
|
</Callout>
|
|
356
356
|
|
|
357
|
-
## Debugging
|
|
357
|
+
## Debugging test runs
|
|
358
358
|
|
|
359
|
-
When integration tests fail, the [Workflow SDK CLI and
|
|
359
|
+
When integration tests fail, the [Workflow SDK CLI and web UI](/docs/observability) can help you inspect what happened. Because integration tests persist workflow state locally, you can use the same observability tools you use in development.
|
|
360
360
|
|
|
361
|
-
Launch the
|
|
361
|
+
Launch the web UI to explore your test workflow runs:
|
|
362
362
|
|
|
363
363
|
```bash
|
|
364
364
|
npx workflow web
|
|
@@ -374,7 +374,7 @@ npx workflow inspect runs
|
|
|
374
374
|
npx workflow inspect run <run-id>
|
|
375
375
|
```
|
|
376
376
|
|
|
377
|
-
The
|
|
377
|
+
The web UI shows each step, its inputs and outputs, retry attempts, hook state, and timing. Use it to diagnose hooks that were not resumed, steps that failed unexpectedly, or workflows that timed out.
|
|
378
378
|
|
|
379
379
|

|
|
380
380
|
|
|
@@ -382,28 +382,28 @@ The Web UI shows each step, its inputs and outputs, retry attempts, hook state,
|
|
|
382
382
|
See the [Observability](/docs/observability) docs for the full set of CLI commands and Web UI features.
|
|
383
383
|
</Callout>
|
|
384
384
|
|
|
385
|
-
## Best
|
|
385
|
+
## Best practices
|
|
386
386
|
|
|
387
|
-
### Separate
|
|
387
|
+
### Separate unit and integration tests
|
|
388
388
|
|
|
389
389
|
Keep two test configurations:
|
|
390
390
|
|
|
391
|
-
- **Unit tests
|
|
392
|
-
- **Integration tests
|
|
391
|
+
- **Unit tests**: Standard Vitest config with no workflow plugin. These tests require no infrastructure.
|
|
392
|
+
- **Integration tests**: Vitest config with the `workflow()` plugin. These tests cover the full workflow lifecycle, including hooks, sleeps, and retries.
|
|
393
393
|
|
|
394
|
-
### Use
|
|
394
|
+
### Use custom hook tokens for deterministic testing
|
|
395
395
|
|
|
396
|
-
When testing workflows with hooks, use [custom tokens](/docs/foundations/hooks#custom-tokens-for-deterministic-hooks) based on predictable values (like document IDs or test identifiers). This
|
|
396
|
+
When testing workflows with hooks, use [custom tokens](/docs/foundations/hooks#custom-tokens-for-deterministic-hooks) based on predictable values (like document IDs or test identifiers). This lets you resume the correct hook in your test code.
|
|
397
397
|
|
|
398
|
-
### Set
|
|
398
|
+
### Set appropriate timeouts
|
|
399
399
|
|
|
400
400
|
Workflows may take longer to execute than typical unit tests, especially when they involve multiple steps or retries. Set a generous `testTimeout` in your integration test config.
|
|
401
401
|
|
|
402
|
-
### Test
|
|
402
|
+
### Test error and retry scenarios
|
|
403
403
|
|
|
404
404
|
Integration tests are the right place to verify that your workflows handle errors correctly, including retryable errors, fatal errors, and timeout scenarios.
|
|
405
405
|
|
|
406
|
-
## Further
|
|
406
|
+
## Further reading
|
|
407
407
|
|
|
408
408
|
- [Hooks & Webhooks](/docs/foundations/hooks) - Pausing and resuming workflows with external data
|
|
409
409
|
- [`start()` API Reference](/docs/api-reference/workflow-api/start) - Start workflows programmatically
|
|
@@ -9,9 +9,9 @@ The [Vitest plugin](/docs/testing#integration-testing-with-the-vitest-plugin) ru
|
|
|
9
9
|
- Reproducing behavior that only occurs in a specific framework's runtime (e.g. Next.js, Nitro)
|
|
10
10
|
- Testing webhook endpoints that receive real HTTP requests
|
|
11
11
|
|
|
12
|
-
This guide shows how to set up integration tests that spawn a dev server as a sidecar process. The example below uses [Nitro](https://v3.nitro.build), but the same pattern works with any supported server framework. It is meant as a starting point
|
|
12
|
+
This guide shows how to set up integration tests that spawn a dev server as a sidecar process. The example below uses [Nitro](https://v3.nitro.build), but the same pattern works with any supported server framework. It is meant as a starting point; customize the server setup to match your own deployment environment.
|
|
13
13
|
|
|
14
|
-
## Vitest
|
|
14
|
+
## Vitest configuration
|
|
15
15
|
|
|
16
16
|
Create a Vitest config with the `workflow()` Vite plugin for code transforms and a `globalSetup` script that manages the server lifecycle:
|
|
17
17
|
|
|
@@ -36,7 +36,7 @@ export default defineConfig({
|
|
|
36
36
|
Note the import path: `workflow/vite` (not `@workflow/vitest`). The Vite plugin handles code transforms but does not set up in-process execution. The server handles workflow execution instead.
|
|
37
37
|
</Callout>
|
|
38
38
|
|
|
39
|
-
## Global
|
|
39
|
+
## Global setup script
|
|
40
40
|
|
|
41
41
|
The `globalSetup` script starts a dev server before tests run and tears it down afterwards. This example uses [Nitro](https://v3.nitro.build), but you can use any server framework that supports the workflow runtime.
|
|
42
42
|
|
|
@@ -148,15 +148,15 @@ export async function teardown() { // [!code highlight]
|
|
|
148
148
|
|
|
149
149
|
These JSON log lines are intentional. They give CI jobs, local tooling, and agents stable events to watch for (`server_starting`, `server_stdout`, `server_stderr`, `server_ready`, `server_exit`), and the thrown timeout error includes the command, expected `WORKFLOW_LOCAL_BASE_URL`, and buffered stdout/stderr so a failed setup is actionable without interactive debugging.
|
|
150
150
|
|
|
151
|
-
The setup script sets `WORKFLOW_LOCAL_BASE_URL` so the workflow runtime sends step execution
|
|
151
|
+
The setup script sets `WORKFLOW_LOCAL_BASE_URL` so the workflow runtime sends flow-route requests (workflow orchestration and step execution) to the running server.
|
|
152
152
|
|
|
153
153
|
<Callout type="info">
|
|
154
154
|
You can use any server framework that supports the workflow runtime. The example above uses [Nitro](https://v3.nitro.build), but you could also use [Next.js](https://nextjs.org), [Hono](https://hono.dev), or any other supported server.
|
|
155
155
|
</Callout>
|
|
156
156
|
|
|
157
|
-
## Writing
|
|
157
|
+
## Writing tests
|
|
158
158
|
|
|
159
|
-
Tests are written the same way as [in-process integration tests](/docs/testing#writing-integration-tests). You can use the same programmatic APIs
|
|
159
|
+
Tests are written the same way as [in-process integration tests](/docs/testing#writing-integration-tests). You can use the same programmatic APIs ([`start()`](/docs/api-reference/workflow-api/start), [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook), [`resumeWebhook()`](/docs/api-reference/workflow-api/resume-webhook), and [`getRun().wakeUp()`](/docs/api-reference/workflow-api/get-run)) to control workflow execution:
|
|
160
160
|
|
|
161
161
|
```typescript title="workflows/calculate.server.test.ts" lineNumbers
|
|
162
162
|
import { describe, it, expect } from "vitest";
|
|
@@ -199,10 +199,10 @@ describe("approvalWorkflow", () => {
|
|
|
199
199
|
```
|
|
200
200
|
|
|
201
201
|
<Callout type="info">
|
|
202
|
-
In server-based tests, the `waitForSleep()` and `waitForHook()` helpers from `@workflow/vitest` are not available since there is no in-process world. Instead, use the programmatic APIs directly
|
|
202
|
+
In server-based tests, the `waitForSleep()` and `waitForHook()` helpers from `@workflow/vitest` are not available since there is no in-process world. Instead, use the programmatic APIs directly. You may need to add short delays or polling to ensure the workflow has reached the desired state before resuming.
|
|
203
203
|
</Callout>
|
|
204
204
|
|
|
205
|
-
## Running
|
|
205
|
+
## Running tests
|
|
206
206
|
|
|
207
207
|
Add a script to your `package.json`:
|
|
208
208
|
|
|
@@ -215,12 +215,12 @@ Add a script to your `package.json`:
|
|
|
215
215
|
}
|
|
216
216
|
```
|
|
217
217
|
|
|
218
|
-
## When to
|
|
218
|
+
## When to use this approach
|
|
219
219
|
|
|
220
220
|
| Scenario | Recommended approach |
|
|
221
221
|
| --- | --- |
|
|
222
222
|
| Testing workflow logic, steps, hooks, retries | [In-process plugin](/docs/testing) |
|
|
223
223
|
| Testing HTTP middleware or authentication | Server-based |
|
|
224
224
|
| Testing webhook endpoints with real HTTP | Server-based |
|
|
225
|
-
| CI/CD pipeline testing | [In-process plugin](/docs/testing) |
|
|
225
|
+
| Continuous integration and continuous delivery (CI/CD) pipeline testing | [In-process plugin](/docs/testing) |
|
|
226
226
|
| Reproducing framework-specific behavior | Server-based |
|