workflow 5.0.0-beta.34 → 5.0.0-beta.35
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/internal/errors.d.ts +1 -1
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/errors.js +2 -2
- package/docs/api-reference/workflow-errors/index.mdx +3 -0
- package/docs/api-reference/workflow-errors/meta.json +1 -0
- package/docs/api-reference/workflow-errors/precondition-failed-error.mdx +62 -0
- package/docs/configuration/runtime-tuning.mdx +7 -0
- package/docs/configuration/worlds.mdx +3 -4
- package/docs/deploying/world/local-world.mdx +5 -1
- package/docs/deploying/world/vercel-world.mdx +3 -8
- package/docs/getting-started/index.mdx +7 -0
- package/docs/getting-started/meta.json +1 -0
- package/docs/getting-started/python.mdx +19 -17
- 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/how-it-works/framework-integrations.mdx +1 -1
- package/docs/observability/attributes.mdx +9 -0
- package/package.json +11 -11
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { EntityConflictError, HookConflictError, HookNotFoundError, RunExpiredError, RunNotSupportedError, StepNotRegisteredError, ThrottleError, TooEarlyError, WorkflowError, WorkflowNotRegisteredError, WorkflowRunCancelledError, WorkflowRunFailedError, WorkflowRunNotCompletedError, WorkflowRunNotFoundError, WorkflowRuntimeError, WorkflowWorldError, } from '@workflow/errors';
|
|
1
|
+
export { EntityConflictError, HookConflictError, HookNotFoundError, PreconditionFailedError, RunExpiredError, RunNotSupportedError, StepNotRegisteredError, ThrottleError, TooEarlyError, WorkflowError, WorkflowNotRegisteredError, WorkflowRunCancelledError, WorkflowRunFailedError, WorkflowRunNotCompletedError, WorkflowRunNotFoundError, WorkflowRuntimeError, WorkflowWorldError, } from '@workflow/errors';
|
|
2
2
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/internal/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,aAAa,EACb,aAAa,EACb,aAAa,EACb,0BAA0B,EAC1B,yBAAyB,EACzB,sBAAsB,EACtB,4BAA4B,EAC5B,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/internal/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,uBAAuB,EACvB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,aAAa,EACb,aAAa,EACb,aAAa,EACb,0BAA0B,EAC1B,yBAAyB,EACzB,sBAAsB,EACtB,4BAA4B,EAC5B,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC"}
|
package/dist/internal/errors.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { EntityConflictError, HookConflictError, HookNotFoundError, RunExpiredError, RunNotSupportedError, StepNotRegisteredError, ThrottleError, TooEarlyError, WorkflowError, WorkflowNotRegisteredError, WorkflowRunCancelledError, WorkflowRunFailedError, WorkflowRunNotCompletedError, WorkflowRunNotFoundError, WorkflowRuntimeError, WorkflowWorldError, } from '@workflow/errors';
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
1
|
+
export { EntityConflictError, HookConflictError, HookNotFoundError, PreconditionFailedError, RunExpiredError, RunNotSupportedError, StepNotRegisteredError, ThrottleError, TooEarlyError, WorkflowError, WorkflowNotRegisteredError, WorkflowRunCancelledError, WorkflowRunFailedError, WorkflowRunNotCompletedError, WorkflowRunNotFoundError, WorkflowRuntimeError, WorkflowWorldError, } from '@workflow/errors';
|
|
2
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3JzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2ludGVybmFsL2Vycm9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsbUJBQW1CLEVBQ25CLGlCQUFpQixFQUNqQixpQkFBaUIsRUFDakIsdUJBQXVCLEVBQ3ZCLGVBQWUsRUFDZixvQkFBb0IsRUFDcEIsc0JBQXNCLEVBQ3RCLGFBQWEsRUFDYixhQUFhLEVBQ2IsYUFBYSxFQUNiLDBCQUEwQixFQUMxQix5QkFBeUIsRUFDekIsc0JBQXNCLEVBQ3RCLDRCQUE0QixFQUM1Qix3QkFBd0IsRUFDeEIsb0JBQW9CLEVBQ3BCLGtCQUFrQixHQUNuQixNQUFNLGtCQUFrQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IHtcbiAgRW50aXR5Q29uZmxpY3RFcnJvcixcbiAgSG9va0NvbmZsaWN0RXJyb3IsXG4gIEhvb2tOb3RGb3VuZEVycm9yLFxuICBQcmVjb25kaXRpb25GYWlsZWRFcnJvcixcbiAgUnVuRXhwaXJlZEVycm9yLFxuICBSdW5Ob3RTdXBwb3J0ZWRFcnJvcixcbiAgU3RlcE5vdFJlZ2lzdGVyZWRFcnJvcixcbiAgVGhyb3R0bGVFcnJvcixcbiAgVG9vRWFybHlFcnJvcixcbiAgV29ya2Zsb3dFcnJvcixcbiAgV29ya2Zsb3dOb3RSZWdpc3RlcmVkRXJyb3IsXG4gIFdvcmtmbG93UnVuQ2FuY2VsbGVkRXJyb3IsXG4gIFdvcmtmbG93UnVuRmFpbGVkRXJyb3IsXG4gIFdvcmtmbG93UnVuTm90Q29tcGxldGVkRXJyb3IsXG4gIFdvcmtmbG93UnVuTm90Rm91bmRFcnJvcixcbiAgV29ya2Zsb3dSdW50aW1lRXJyb3IsXG4gIFdvcmtmbG93V29ybGRFcnJvcixcbn0gZnJvbSAnQHdvcmtmbG93L2Vycm9ycyc7XG4iXX0=
|
|
@@ -79,6 +79,9 @@ All errors extend [`WorkflowError`](/docs/api-reference/workflow-errors/workflow
|
|
|
79
79
|
<Card href="/docs/api-reference/workflow-errors/entity-conflict-error" title="EntityConflictError">
|
|
80
80
|
Thrown when a storage operation conflicts with the current entity state.
|
|
81
81
|
</Card>
|
|
82
|
+
<Card href="/docs/api-reference/workflow-errors/precondition-failed-error" title="PreconditionFailedError">
|
|
83
|
+
Thrown when an event creation is rejected because the client's event-log snapshot is stale.
|
|
84
|
+
</Card>
|
|
82
85
|
<Card href="/docs/api-reference/workflow-errors/too-early-error" title="TooEarlyError">
|
|
83
86
|
Thrown when a request is made before the system is ready to process it.
|
|
84
87
|
</Card>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: PreconditionFailedError
|
|
3
|
+
description: Thrown when an event creation is rejected because the client's event-log snapshot is stale.
|
|
4
|
+
type: reference
|
|
5
|
+
summary: Catch PreconditionFailedError when a world rejects an event creation made from a stale event-log snapshot.
|
|
6
|
+
related:
|
|
7
|
+
- /docs/api-reference/workflow-errors/workflow-world-error
|
|
8
|
+
- /docs/api-reference/workflow-errors/entity-conflict-error
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
`PreconditionFailedError` is thrown by world implementations when an event creation is rejected because the client's event-log snapshot is stale — a newer out-of-band event (such as a received hook or a completed step) was recorded after the snapshot the client replayed from. It corresponds to HTTP 412 Precondition Failed semantics.
|
|
12
|
+
|
|
13
|
+
This only occurs when the optimistic-concurrency guard is enabled via `WORKFLOW_PRECONDITION_GUARD=1` (see [Runtime Tuning](/docs/configuration/runtime-tuning)); event creations that carry no snapshot are never rejected with this error.
|
|
14
|
+
|
|
15
|
+
<Callout>
|
|
16
|
+
The Workflow runtime handles this error automatically: it reloads the event log and retries, ultimately re-enqueueing the run for a fresh replay if it cannot catch up. You will only encounter it when interacting with world storage APIs directly.
|
|
17
|
+
</Callout>
|
|
18
|
+
|
|
19
|
+
```typescript lineNumbers
|
|
20
|
+
import { PreconditionFailedError } from "workflow/errors"
|
|
21
|
+
declare const world: { events: { create(...args: any[]): Promise<any> } }; // @setup
|
|
22
|
+
declare const runId: string; // @setup
|
|
23
|
+
declare const event: any; // @setup
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
await world.events.create(runId, event);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (PreconditionFailedError.is(error)) { // [!code highlight]
|
|
29
|
+
console.log("Snapshot is stale; reload the event log and retry");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## API Signature
|
|
35
|
+
|
|
36
|
+
### Properties
|
|
37
|
+
|
|
38
|
+
<TSDoc
|
|
39
|
+
definition={`
|
|
40
|
+
interface PreconditionFailedError {
|
|
41
|
+
/** Delay in seconds before the operation should be retried. Present when the server sends a Retry-After header. */
|
|
42
|
+
retryAfter?: number;
|
|
43
|
+
/** The error message. */
|
|
44
|
+
message: string;
|
|
45
|
+
}
|
|
46
|
+
export default PreconditionFailedError;`}
|
|
47
|
+
/>
|
|
48
|
+
|
|
49
|
+
### Static Methods
|
|
50
|
+
|
|
51
|
+
#### `PreconditionFailedError.is(value)`
|
|
52
|
+
|
|
53
|
+
Type-safe check for `PreconditionFailedError` instances. Preferred over `instanceof` because it works across module boundaries and VM contexts.
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { PreconditionFailedError } from "workflow/errors"
|
|
57
|
+
declare const error: unknown; // @setup
|
|
58
|
+
|
|
59
|
+
if (PreconditionFailedError.is(error)) {
|
|
60
|
+
// error is typed as PreconditionFailedError
|
|
61
|
+
}
|
|
62
|
+
```
|
|
@@ -38,6 +38,13 @@ For example, a workflow can run a 10-minute inline step even with `WORKFLOW_REPL
|
|
|
38
38
|
- Default: `3`
|
|
39
39
|
- Recovery replays before replay divergence is recorded as corruption.
|
|
40
40
|
|
|
41
|
+
### `WORKFLOW_PRECONDITION_GUARD`
|
|
42
|
+
|
|
43
|
+
- Default: disabled
|
|
44
|
+
- Set `1` to enable an optimistic-concurrency guard for event creation: replay-context event creations send a `stateUpdatedAt` snapshot timestamp, and a backend that supports the guard rejects a creation with 412 ([`PreconditionFailedError`](/docs/api-reference/workflow-errors/precondition-failed-error)) when a newer out-of-band event (a received hook or a completed step) was recorded after that snapshot.
|
|
45
|
+
- On rejection the runtime reloads the event log and retries, falling back to a queue re-invocation with a fresh replay if it cannot catch up.
|
|
46
|
+
- Backends that do not support the guard ignore the snapshot.
|
|
47
|
+
|
|
41
48
|
## Inline execution
|
|
42
49
|
|
|
43
50
|
### `WORKFLOW_V2_TIMEOUT_MS`
|
|
@@ -82,9 +82,9 @@ The Local World is the default outside Vercel and is intended for development.
|
|
|
82
82
|
|
|
83
83
|
### `recoverActiveRuns`
|
|
84
84
|
|
|
85
|
-
- Environment variable:
|
|
85
|
+
- Environment variable: `WORKFLOW_LOCAL_RECOVER_ACTIVE_RUNS`
|
|
86
86
|
- Default: `true`
|
|
87
|
-
- Re-enqueues pending and running local runs when the World starts.
|
|
87
|
+
- Re-enqueues pending and running local runs when the World starts. Set the environment variable to `0` or `false` to skip recovery; the factory option wins when both are set.
|
|
88
88
|
|
|
89
89
|
### `tag`
|
|
90
90
|
|
|
@@ -201,8 +201,7 @@ Platform-provided values such as `VERCEL_DEPLOYMENT_ID`, `VERCEL_PROJECT_ID`, an
|
|
|
201
201
|
- Default: disabled
|
|
202
202
|
- Set `1` to serialize orchestrator (flow) invocations per run: each run's replays get their own queue topic and the flow trigger is generated with `maxConcurrency: 1`. Inline step executions get per-step topics and keep full parallelism.
|
|
203
203
|
- Read at **both build time and runtime** — set it as a project-level environment variable so the generated trigger and the runtime queue routing agree.
|
|
204
|
-
-
|
|
205
|
-
- Costs: flow-route push deliveries under `maxConcurrency` are billed at 2x units, queue observability sees one topic per run, and deliveries can queue behind the per-run slot — including hook resumes, aborts, and run-timeout enforcement, which are delayed while a replay is in flight. See [Vercel World](/docs/deploying/world/vercel-world#workflow_sequential_replays) for details.
|
|
204
|
+
- Routing each run through a dedicated `maxConcurrency: 1` topic might lead to higher queue performance overhead. See [Vercel World](/docs/deploying/world/vercel-world#workflow_sequential_replays) for details.
|
|
206
205
|
|
|
207
206
|
### `VERCEL_WORKFLOW_SERVER_URL`
|
|
208
207
|
|
|
@@ -62,6 +62,10 @@ Maximum number of concurrent queue message handlers. Default: `1000`
|
|
|
62
62
|
|
|
63
63
|
Maximum number of seconds a local queue message can stay hidden before the handler rechecks the run. Default: unlimited.
|
|
64
64
|
|
|
65
|
+
### `WORKFLOW_LOCAL_RECOVER_ACTIVE_RUNS`
|
|
66
|
+
|
|
67
|
+
Whether pending and running runs found in the data directory are re-enqueued when the world starts. Set to `0` or `false` to skip recovery and leave stale runs untouched. Default: `true`
|
|
68
|
+
|
|
65
69
|
### `WORKFLOW_STREAM_FLUSH_INTERVAL_MS`
|
|
66
70
|
|
|
67
71
|
Flush interval, in milliseconds, for buffered stream writes. Default: `10`.
|
|
@@ -78,7 +82,7 @@ export default createWorld({
|
|
|
78
82
|
port: 5173,
|
|
79
83
|
// baseUrl overrides port if set
|
|
80
84
|
baseUrl: "https://local.example.com:3000",
|
|
81
|
-
recoverActiveRuns: true,
|
|
85
|
+
recoverActiveRuns: true, // overrides WORKFLOW_LOCAL_RECOVER_ACTIVE_RUNS
|
|
82
86
|
streamFlushIntervalMs: 10, // overrides WORKFLOW_STREAM_FLUSH_INTERVAL_MS
|
|
83
87
|
});
|
|
84
88
|
```
|
|
@@ -171,21 +171,16 @@ Custom workflow-server URL for direct runtime requests, or for the proxy to forw
|
|
|
171
171
|
|
|
172
172
|
### `WORKFLOW_SEQUENTIAL_REPLAYS`
|
|
173
173
|
|
|
174
|
-
Set `WORKFLOW_SEQUENTIAL_REPLAYS=1` to guarantee that **at most one orchestrator (flow) invocation runs at a time per workflow run**. This behavior is off by default; without it, the runtime relies on idempotency and the event log to tolerate concurrent flow invocations of the same run.
|
|
174
|
+
Set `WORKFLOW_SEQUENTIAL_REPLAYS=1` to guarantee that **at most one orchestrator (flow) invocation runs at a time per workflow run**. This behavior is off by default; without it, the runtime relies on idempotency and the event log to tolerate concurrent flow invocations of the same run.
|
|
175
175
|
|
|
176
176
|
When enabled, each run's orchestrator messages are given their own queue topic and the flow trigger is configured with `maxConcurrency: 1`, so [Vercel Queues](https://vercel.com/docs/queues) processes replays for a given run strictly one at a time. Step executions (which ride the flow topic in the combined handler model) get a per-step topic, so steps keep full parallelism.
|
|
177
177
|
|
|
178
178
|
<Callout type="warn">
|
|
179
179
|
This variable is read at **both build time and runtime**, so it must be set as a project-level environment variable that applies to your build and your deployed functions. Setting it for only one will produce an inconsistent configuration. The same applies to framework integrations that write their own queue trigger configuration instead of using `getWorkflowQueueTrigger()` from `@workflow/builders`: they only get the runtime half (per-run topics) unless they also emit `maxConcurrency: 1` on their flow trigger.
|
|
180
|
-
|
|
181
|
-
Enabling sequential replays has a cost. Per [Vercel Queues pricing](https://vercel.com/docs/queues/pricing), push deliveries under `maxConcurrency` are billed at **2x units** for that operation, so every flow-route delivery costs double while this is enabled. It also creates one queue topic per run, which increases the number of distinct queues surfaced in queue observability, and each flow invocation waits for a per-run concurrency slot before delivery, which can add queueing latency. Leave it off unless you specifically need the per-run serialization guarantee.
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
While a replay holds a run's slot — including time spent executing steps inline — other wake messages for that run (hook resumes, aborts and cancellations, and run-timeout enforcement) wait for the slot. Expect aborts and timeouts to be delayed by up to the duration of the longest single invocation.
|
|
185
|
-
|
|
186
|
-
The guarantee covers messages sent by the Workflow SDK itself. External producers that compute a flow topic name directly (rather than enqueueing through the SDK) still deliver, but bypass the per-run serialization slot.
|
|
187
180
|
</Callout>
|
|
188
181
|
|
|
182
|
+
Because it routes each run's flow invocations through a dedicated `maxConcurrency: 1` queue topic, enabling this might lead to higher queue performance overhead.
|
|
183
|
+
|
|
189
184
|
### `VERCEL_QUEUE_MAX_DELAY_SECONDS`
|
|
190
185
|
|
|
191
186
|
Maximum delay, in seconds, that Workflow uses for one Vercel Queues continuation message when implementing `sleep()`. If a workflow sleeps longer than this, the runtime schedules another continuation message when the first one fires, repeating until the sleep's target time is reached. Default: `82800` (23 hours).
|
|
@@ -9,6 +9,7 @@ related:
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
import { Next, Nitro, SvelteKit, Nuxt, Hono, Bun, AstroDark, AstroLight, TanStack, Vite, Express, Nest, Fastify, Python } from "@/app/[lang]/(home)/components/frameworks";
|
|
12
|
+
import { SiReactrouter } from "@icons-pack/react-simple-icons";
|
|
12
13
|
|
|
13
14
|
<Cards>
|
|
14
15
|
<Card href="/docs/getting-started/next">
|
|
@@ -20,6 +21,12 @@ import { Next, Nitro, SvelteKit, Nuxt, Hono, Bun, AstroDark, AstroLight, TanStac
|
|
|
20
21
|
<span className="font-medium">Vite</span>
|
|
21
22
|
</div>
|
|
22
23
|
</Card>
|
|
24
|
+
<Card href="/docs/getting-started/react-router">
|
|
25
|
+
<div className="flex flex-col items-center justify-center gap-2">
|
|
26
|
+
<SiReactrouter className="size-16" />
|
|
27
|
+
<span className="font-medium">React Router</span>
|
|
28
|
+
</div>
|
|
29
|
+
</Card>
|
|
23
30
|
<Card href="/docs/getting-started/astro">
|
|
24
31
|
<div className="flex flex-col items-center justify-center gap-2">
|
|
25
32
|
<AstroLight className="size-16 dark:hidden" />
|
|
@@ -11,7 +11,7 @@ related:
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
<CopyPrompt
|
|
14
|
-
text="In this Python project,
|
|
14
|
+
text="In this Python project, add `requires-python = ">=3.12"` and `dependencies = ["vercel"]` under `[project]` in `pyproject.toml`. Add `[[tool.vercel.workflows]]` with `entrypoint = "app.workflows:wf"`. Create `app/workflow.py` with `from vercel import workflow` and `wf = workflow.Workflows()`. Create `app/workflows/ai_content_workflow.py` importing `wf`, define `@wf.workflow async def ai_content_workflow(*, topic: str)`, and call step functions such as `generate_draft` and `summarize_draft`. Export `wf` from `app/workflows/__init__.py` and import the workflow module so its definitions are registered. Mark step functions with `@wf.step`, use `await workflow.sleep("7 days")` for durable delays where needed, and use a `workflow.BaseHook` Pydantic model plus `.wait(token=...)` and `.resume(token)` for external approval events. Verify the workflow entrypoint uses the `module:object` format and points to the exported `Workflows` registry."
|
|
15
15
|
/>
|
|
16
16
|
|
|
17
17
|
<Callout type="warn">
|
|
@@ -22,26 +22,19 @@ You can build durable workflows in Python using the [`vercel` Python SDK](https:
|
|
|
22
22
|
|
|
23
23
|
## Getting Started
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Add the `vercel` package and workflow entrypoint to `pyproject.toml`:
|
|
26
26
|
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Configure `experimentalServices` in your `vercel.json`:
|
|
27
|
+
```toml filename="pyproject.toml"
|
|
28
|
+
[project]
|
|
29
|
+
requires-python = ">=3.12"
|
|
30
|
+
dependencies = ["vercel"]
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"experimentalServices": {
|
|
36
|
-
"ai_content_workflow": {
|
|
37
|
-
"type": "worker",
|
|
38
|
-
"entrypoint": "app/workflows/ai_content_workflow.py",
|
|
39
|
-
"topics": ["__wkf_*"]
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
32
|
+
[[tool.vercel.workflows]]
|
|
33
|
+
entrypoint = "app.workflows:wf"
|
|
43
34
|
```
|
|
44
35
|
|
|
36
|
+
The workflow `entrypoint` uses the `module:object` format and points to the exported `Workflows` registry.
|
|
37
|
+
|
|
45
38
|
## Workflows
|
|
46
39
|
|
|
47
40
|
A workflow is a stateful function that coordinates multi-step logic over time. Create a `Workflows` instance and use the `@wf.workflow` decorator to mark a function as durable:
|
|
@@ -66,6 +59,15 @@ async def ai_content_workflow(*, topic: str):
|
|
|
66
59
|
}
|
|
67
60
|
```
|
|
68
61
|
|
|
62
|
+
Export the registry from the workflow package and import the module containing your workflow so its definitions are registered:
|
|
63
|
+
|
|
64
|
+
```python filename="app/workflows/__init__.py"
|
|
65
|
+
from app.workflow import wf
|
|
66
|
+
from app.workflows import ai_content_workflow
|
|
67
|
+
|
|
68
|
+
__all__ = ["ai_content_workflow", "wf"]
|
|
69
|
+
```
|
|
70
|
+
|
|
69
71
|
Under the hood, the workflow compiles into a route that orchestrates execution. All inputs and outputs are recorded in an event log. If a deploy or crash happens, the system replays execution deterministically from where it stopped.
|
|
70
72
|
|
|
71
73
|
## Steps
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: React Router
|
|
3
|
+
description: Run durable workflows in a React Router framework-mode app using Nitro.
|
|
4
|
+
type: overview
|
|
5
|
+
summary: Choose your React Router version and connect React Router, Nitro, and Workflow SDK.
|
|
6
|
+
related:
|
|
7
|
+
- /docs/getting-started/nitro
|
|
8
|
+
- /docs/getting-started/vite
|
|
9
|
+
- /docs/foundations/workflows-and-steps
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
React Router framework mode builds the browser application and its server-rendering code, but it still needs a server to receive requests. [Nitro](https://v3.nitro.build) provides that server. Workflow SDK integrates with Nitro to add the durable workflow routes and build artifacts.
|
|
13
|
+
|
|
14
|
+
The three pieces share one Vite build:
|
|
15
|
+
|
|
16
|
+
1. **React Router** builds your routes, loaders, actions, and browser assets.
|
|
17
|
+
2. **Nitro** runs the React Router request handler and any routes in `server/routes`.
|
|
18
|
+
3. **Workflow SDK** finds files with `"use workflow"` and `"use step"`, then adds its runtime routes to Nitro.
|
|
19
|
+
|
|
20
|
+
Choose the guide that matches your React Router major version:
|
|
21
|
+
|
|
22
|
+
<AutoCards />
|
|
23
|
+
|
|
24
|
+
<Callout>
|
|
25
|
+
These guides require **Nitro v3**. Nitro v2 does not provide the Vite
|
|
26
|
+
environment integration used by this setup.
|
|
27
|
+
</Callout>
|
|
28
|
+
|
|
29
|
+
## What the bridge does
|
|
30
|
+
|
|
31
|
+
The setup adds a small `server/ssr.ts` file. It turns React Router's generated server build into a standard Fetch API handler that Nitro can run. The Vite config then points Nitro's server and public output at the same `build` directory React Router uses.
|
|
32
|
+
|
|
33
|
+
This is configuration in your application, not a separate React Router adapter. Your React Router routes remain React Router routes, while Nitro owns the HTTP server and Workflow SDK uses Nitro's lifecycle and routing.
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: React Router v7
|
|
3
|
+
description: Add durable workflows to a React Router v7 framework-mode app using Nitro v3.
|
|
4
|
+
type: guide
|
|
5
|
+
summary: Enable the Vite Environment API and configure React Router v7, Nitro v3, and Workflow SDK.
|
|
6
|
+
prerequisites:
|
|
7
|
+
- /docs/getting-started/react-router
|
|
8
|
+
related:
|
|
9
|
+
- /docs/getting-started/nitro
|
|
10
|
+
- /docs/foundations/workflows-and-steps
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
This guide starts with an existing React Router v7 framework-mode app. It is verified with v7.18.1; if your config does not recognize `v8_viteEnvironmentApi`, update to the latest v7 release.
|
|
14
|
+
|
|
15
|
+
<Steps>
|
|
16
|
+
|
|
17
|
+
<Step>
|
|
18
|
+
|
|
19
|
+
## Install Nitro and Workflow SDK
|
|
20
|
+
|
|
21
|
+
<Tabs items={["npm", "pnpm", "bun", "yarn"]} defaultValue="pnpm">
|
|
22
|
+
|
|
23
|
+
<Tab value="npm">
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install nitro workflow
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
</Tab>
|
|
30
|
+
|
|
31
|
+
<Tab value="pnpm">
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm add nitro workflow
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
</Tab>
|
|
38
|
+
|
|
39
|
+
<Tab value="bun">
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
bun add nitro workflow
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
</Tab>
|
|
46
|
+
|
|
47
|
+
<Tab value="yarn">
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
yarn add nitro workflow
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
</Tab>
|
|
54
|
+
|
|
55
|
+
</Tabs>
|
|
56
|
+
|
|
57
|
+
This integration requires Nitro v3.
|
|
58
|
+
|
|
59
|
+
</Step>
|
|
60
|
+
|
|
61
|
+
<Step>
|
|
62
|
+
|
|
63
|
+
## Enable the Vite Environment API
|
|
64
|
+
|
|
65
|
+
React Router v7 keeps the Vite Environment API behind a future flag. Enable the required flag and set an explicit build directory:
|
|
66
|
+
|
|
67
|
+
```typescript title="react-router.config.ts" lineNumbers
|
|
68
|
+
import type { Config } from "@react-router/dev/config";
|
|
69
|
+
|
|
70
|
+
export default {
|
|
71
|
+
ssr: true,
|
|
72
|
+
buildDirectory: "build", // [!code highlight]
|
|
73
|
+
future: {
|
|
74
|
+
v8_viteEnvironmentApi: true, // [!code highlight]
|
|
75
|
+
},
|
|
76
|
+
} satisfies Config;
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
</Step>
|
|
80
|
+
|
|
81
|
+
<Step>
|
|
82
|
+
|
|
83
|
+
## Create the React Router server handler
|
|
84
|
+
|
|
85
|
+
Create `server/ssr.ts`:
|
|
86
|
+
|
|
87
|
+
```typescript title="server/ssr.ts" lineNumbers
|
|
88
|
+
import { createRequestHandler } from "react-router";
|
|
89
|
+
|
|
90
|
+
export default {
|
|
91
|
+
fetch: createRequestHandler(
|
|
92
|
+
() => import("virtual:react-router/server-build"),
|
|
93
|
+
import.meta.env.MODE,
|
|
94
|
+
),
|
|
95
|
+
};
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This adapts React Router's generated server build to the Fetch API handler Nitro expects.
|
|
99
|
+
|
|
100
|
+
</Step>
|
|
101
|
+
|
|
102
|
+
<Step>
|
|
103
|
+
|
|
104
|
+
## Configure Vite
|
|
105
|
+
|
|
106
|
+
Update `vite.config.ts`:
|
|
107
|
+
|
|
108
|
+
```typescript title="vite.config.ts" lineNumbers
|
|
109
|
+
import { reactRouter } from "@react-router/dev/vite";
|
|
110
|
+
import { nitro } from "nitro/vite";
|
|
111
|
+
import { defineConfig } from "vite";
|
|
112
|
+
import { workflow } from "workflow/vite";
|
|
113
|
+
import reactRouterConfig from "./react-router.config";
|
|
114
|
+
|
|
115
|
+
export default defineConfig({
|
|
116
|
+
plugins: [
|
|
117
|
+
reactRouter(),
|
|
118
|
+
nitro({
|
|
119
|
+
serverDir: "./server",
|
|
120
|
+
output: {
|
|
121
|
+
dir: reactRouterConfig.buildDirectory,
|
|
122
|
+
serverDir: `${reactRouterConfig.buildDirectory}/server`,
|
|
123
|
+
publicDir: `${reactRouterConfig.buildDirectory}/client`,
|
|
124
|
+
},
|
|
125
|
+
}),
|
|
126
|
+
workflow({ dirs: ["workflows"] }),
|
|
127
|
+
],
|
|
128
|
+
environments: {
|
|
129
|
+
ssr: {
|
|
130
|
+
build: {
|
|
131
|
+
rollupOptions: {
|
|
132
|
+
input: "./server/ssr.ts",
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Keep `dirs: ["workflows"]` so subsequent builds do not scan generated files under `build`. Place `reactRouter()` before `nitro()` in the plugin array.
|
|
141
|
+
|
|
142
|
+
</Step>
|
|
143
|
+
|
|
144
|
+
<Step>
|
|
145
|
+
|
|
146
|
+
## Create a workflow
|
|
147
|
+
|
|
148
|
+
Create `workflows/greeting.ts`:
|
|
149
|
+
|
|
150
|
+
```typescript title="workflows/greeting.ts" lineNumbers
|
|
151
|
+
export async function greetingWorkflow(name: string) {
|
|
152
|
+
"use workflow";
|
|
153
|
+
|
|
154
|
+
return greet(name);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function greet(name: string) {
|
|
158
|
+
"use step";
|
|
159
|
+
|
|
160
|
+
return `Hello, ${name}!`;
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
</Step>
|
|
165
|
+
|
|
166
|
+
<Step>
|
|
167
|
+
|
|
168
|
+
## Start the workflow from a Nitro route
|
|
169
|
+
|
|
170
|
+
Create `server/routes/api/greeting.post.ts`:
|
|
171
|
+
|
|
172
|
+
```typescript title="server/routes/api/greeting.post.ts" lineNumbers
|
|
173
|
+
import { defineHandler } from "nitro";
|
|
174
|
+
import { start } from "workflow/api";
|
|
175
|
+
import { greetingWorkflow } from "../../../workflows/greeting";
|
|
176
|
+
|
|
177
|
+
export default defineHandler(async (event) => {
|
|
178
|
+
const { name } = (await event.req.json()) as { name: string };
|
|
179
|
+
const run = await start(greetingWorkflow, [name]);
|
|
180
|
+
|
|
181
|
+
return { runId: run.runId };
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
React Router continues to handle your application routes. Nitro handles this server route at `POST /api/greeting`, as well as Workflow SDK's internal routes.
|
|
186
|
+
|
|
187
|
+
</Step>
|
|
188
|
+
|
|
189
|
+
<Step>
|
|
190
|
+
|
|
191
|
+
## Run the app
|
|
192
|
+
|
|
193
|
+
Start the development server:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
pnpm vite dev
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Then start a workflow:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
curl -X POST \
|
|
203
|
+
-H "content-type: application/json" \
|
|
204
|
+
-d '{"name":"Workflow"}' \
|
|
205
|
+
http://localhost:3000/api/greeting
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Build and start the production server:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
pnpm vite build
|
|
212
|
+
node ./build/server/index.mjs
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
You can inspect local runs with `pnpm workflow web`.
|
|
216
|
+
|
|
217
|
+
</Step>
|
|
218
|
+
|
|
219
|
+
</Steps>
|
|
220
|
+
|
|
221
|
+
## Troubleshooting
|
|
222
|
+
|
|
223
|
+
### Vite reports an invalid SSR input or `path.replace is not a function`
|
|
224
|
+
|
|
225
|
+
Set `future.v8_viteEnvironmentApi` to `true` in `react-router.config.ts`.
|
|
226
|
+
|
|
227
|
+
### React Router pages return 404
|
|
228
|
+
|
|
229
|
+
Check that the `ssr` environment input points to `./server/ssr.ts`.
|
|
230
|
+
|
|
231
|
+
### A second build tries to compile files under `build/server`
|
|
232
|
+
|
|
233
|
+
Use `workflow({ dirs: ["workflows"] })`, remove the existing `build` directory once, and rebuild.
|
|
234
|
+
|
|
235
|
+
### `vite build` finishes output but does not exit
|
|
236
|
+
|
|
237
|
+
Use `workflow@5.0.0-beta.33` or later with Nitro v3.
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: React Router v8
|
|
3
|
+
description: Add durable workflows to a React Router v8 framework-mode app using Nitro v3.
|
|
4
|
+
type: guide
|
|
5
|
+
summary: Configure React Router v8, Nitro v3, and Workflow SDK in one Vite build.
|
|
6
|
+
prerequisites:
|
|
7
|
+
- /docs/getting-started/react-router
|
|
8
|
+
related:
|
|
9
|
+
- /docs/getting-started/nitro
|
|
10
|
+
- /docs/foundations/workflows-and-steps
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
This guide starts with an existing React Router v8 framework-mode app.
|
|
14
|
+
|
|
15
|
+
<Steps>
|
|
16
|
+
|
|
17
|
+
<Step>
|
|
18
|
+
|
|
19
|
+
## Install Nitro and Workflow SDK
|
|
20
|
+
|
|
21
|
+
<Tabs items={["npm", "pnpm", "bun", "yarn"]} defaultValue="pnpm">
|
|
22
|
+
|
|
23
|
+
<Tab value="npm">
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install nitro workflow
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
</Tab>
|
|
30
|
+
|
|
31
|
+
<Tab value="pnpm">
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm add nitro workflow
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
</Tab>
|
|
38
|
+
|
|
39
|
+
<Tab value="bun">
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
bun add nitro workflow
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
</Tab>
|
|
46
|
+
|
|
47
|
+
<Tab value="yarn">
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
yarn add nitro workflow
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
</Tab>
|
|
54
|
+
|
|
55
|
+
</Tabs>
|
|
56
|
+
|
|
57
|
+
This integration requires Nitro v3.
|
|
58
|
+
|
|
59
|
+
</Step>
|
|
60
|
+
|
|
61
|
+
<Step>
|
|
62
|
+
|
|
63
|
+
## Use a shared build directory
|
|
64
|
+
|
|
65
|
+
Set an explicit build directory in your React Router config:
|
|
66
|
+
|
|
67
|
+
```typescript title="react-router.config.ts" lineNumbers
|
|
68
|
+
import type { Config } from "@react-router/dev/config";
|
|
69
|
+
|
|
70
|
+
export default {
|
|
71
|
+
ssr: true,
|
|
72
|
+
buildDirectory: "build", // [!code highlight]
|
|
73
|
+
} satisfies Config;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
React Router will place browser assets in `build/client`. Nitro will place the runnable server in `build/server`.
|
|
77
|
+
|
|
78
|
+
</Step>
|
|
79
|
+
|
|
80
|
+
<Step>
|
|
81
|
+
|
|
82
|
+
## Create the React Router server handler
|
|
83
|
+
|
|
84
|
+
Create `server/ssr.ts`:
|
|
85
|
+
|
|
86
|
+
```typescript title="server/ssr.ts" lineNumbers
|
|
87
|
+
import { createRequestHandler } from "react-router";
|
|
88
|
+
|
|
89
|
+
export default {
|
|
90
|
+
fetch: createRequestHandler(
|
|
91
|
+
() => import("virtual:react-router/server-build"),
|
|
92
|
+
import.meta.env.MODE,
|
|
93
|
+
),
|
|
94
|
+
};
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This adapts React Router's generated server build to the Fetch API handler Nitro expects.
|
|
98
|
+
|
|
99
|
+
</Step>
|
|
100
|
+
|
|
101
|
+
<Step>
|
|
102
|
+
|
|
103
|
+
## Configure Vite
|
|
104
|
+
|
|
105
|
+
Update `vite.config.ts`:
|
|
106
|
+
|
|
107
|
+
```typescript title="vite.config.ts" lineNumbers
|
|
108
|
+
import { reactRouter } from "@react-router/dev/vite";
|
|
109
|
+
import { nitro } from "nitro/vite";
|
|
110
|
+
import { defineConfig } from "vite";
|
|
111
|
+
import { workflow } from "workflow/vite";
|
|
112
|
+
import reactRouterConfig from "./react-router.config";
|
|
113
|
+
|
|
114
|
+
export default defineConfig({
|
|
115
|
+
plugins: [
|
|
116
|
+
reactRouter(),
|
|
117
|
+
nitro({
|
|
118
|
+
serverDir: "./server",
|
|
119
|
+
output: {
|
|
120
|
+
dir: reactRouterConfig.buildDirectory,
|
|
121
|
+
serverDir: `${reactRouterConfig.buildDirectory}/server`,
|
|
122
|
+
publicDir: `${reactRouterConfig.buildDirectory}/client`,
|
|
123
|
+
},
|
|
124
|
+
}),
|
|
125
|
+
workflow({ dirs: ["workflows"] }),
|
|
126
|
+
],
|
|
127
|
+
environments: {
|
|
128
|
+
ssr: {
|
|
129
|
+
build: {
|
|
130
|
+
rollupOptions: {
|
|
131
|
+
input: "./server/ssr.ts",
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Keep `dirs: ["workflows"]` so subsequent builds do not scan generated files under `build`. Place `reactRouter()` before `nitro()` in the plugin array.
|
|
140
|
+
|
|
141
|
+
</Step>
|
|
142
|
+
|
|
143
|
+
<Step>
|
|
144
|
+
|
|
145
|
+
## Create a workflow
|
|
146
|
+
|
|
147
|
+
Create `workflows/greeting.ts`:
|
|
148
|
+
|
|
149
|
+
```typescript title="workflows/greeting.ts" lineNumbers
|
|
150
|
+
export async function greetingWorkflow(name: string) {
|
|
151
|
+
"use workflow";
|
|
152
|
+
|
|
153
|
+
return greet(name);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function greet(name: string) {
|
|
157
|
+
"use step";
|
|
158
|
+
|
|
159
|
+
return `Hello, ${name}!`;
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
</Step>
|
|
164
|
+
|
|
165
|
+
<Step>
|
|
166
|
+
|
|
167
|
+
## Start the workflow from a Nitro route
|
|
168
|
+
|
|
169
|
+
Create `server/routes/api/greeting.post.ts`:
|
|
170
|
+
|
|
171
|
+
```typescript title="server/routes/api/greeting.post.ts" lineNumbers
|
|
172
|
+
import { defineHandler } from "nitro";
|
|
173
|
+
import { start } from "workflow/api";
|
|
174
|
+
import { greetingWorkflow } from "../../../workflows/greeting";
|
|
175
|
+
|
|
176
|
+
export default defineHandler(async (event) => {
|
|
177
|
+
const { name } = (await event.req.json()) as { name: string };
|
|
178
|
+
const run = await start(greetingWorkflow, [name]);
|
|
179
|
+
|
|
180
|
+
return { runId: run.runId };
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
React Router continues to handle your application routes. Nitro handles this server route at `POST /api/greeting`, as well as Workflow SDK's internal routes.
|
|
185
|
+
|
|
186
|
+
</Step>
|
|
187
|
+
|
|
188
|
+
<Step>
|
|
189
|
+
|
|
190
|
+
## Run the app
|
|
191
|
+
|
|
192
|
+
Start the development server:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
pnpm vite dev
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Then start a workflow:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
curl -X POST \
|
|
202
|
+
-H "content-type: application/json" \
|
|
203
|
+
-d '{"name":"Workflow"}' \
|
|
204
|
+
http://localhost:3000/api/greeting
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Build and start the production server:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
pnpm vite build
|
|
211
|
+
node ./build/server/index.mjs
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
You can inspect local runs with `pnpm workflow web`.
|
|
215
|
+
|
|
216
|
+
</Step>
|
|
217
|
+
|
|
218
|
+
</Steps>
|
|
219
|
+
|
|
220
|
+
## Troubleshooting
|
|
221
|
+
|
|
222
|
+
### React Router pages return 404
|
|
223
|
+
|
|
224
|
+
Check that the `ssr` environment input points to `./server/ssr.ts`.
|
|
225
|
+
|
|
226
|
+
### A second build tries to compile files under `build/server`
|
|
227
|
+
|
|
228
|
+
Use `workflow({ dirs: ["workflows"] })`, remove the existing `build` directory once, and rebuild.
|
|
229
|
+
|
|
230
|
+
### `vite build` finishes output but does not exit
|
|
231
|
+
|
|
232
|
+
Use `workflow@5.0.0-beta.33` or later with Nitro v3.
|
|
@@ -414,7 +414,7 @@ const flowTriggers = [getWorkflowQueueTrigger()];
|
|
|
414
414
|
const stepTriggers = [STEP_QUEUE_TRIGGER];
|
|
415
415
|
```
|
|
416
416
|
|
|
417
|
-
If your integration constructs the flow trigger object itself instead of calling `getWorkflowQueueTrigger()`, it must add `maxConcurrency: 1` to that trigger when sequential replays are enabled at build time (`WORKFLOW_SEQUENTIAL_REPLAYS=1
|
|
417
|
+
If your integration constructs the flow trigger object itself instead of calling `getWorkflowQueueTrigger()`, it must add `maxConcurrency: 1` to that trigger when sequential replays are enabled at build time (`WORKFLOW_SEQUENTIAL_REPLAYS=1` — the exported `isSequentialReplaysEnabled()` helper implements this check). The runtime half of the feature (per-run queue topics) activates from the environment variable alone — without the trigger half, those per-run topics are not serialized and the setting only adds queue-topic cardinality.
|
|
418
418
|
|
|
419
419
|
|
|
420
420
|
### Custom implementations
|
|
@@ -57,6 +57,15 @@ export async function cleanupAttributes() {
|
|
|
57
57
|
|
|
58
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
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
|
+
|
|
60
69
|
## Viewing attributes
|
|
61
70
|
|
|
62
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:
|
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.35",
|
|
4
4
|
"description": "Workflow SDK - Build durable, resilient, and observable workflows",
|
|
5
5
|
"main": "dist/typescript-plugin.cjs",
|
|
6
6
|
"type": "module",
|
|
@@ -57,18 +57,18 @@
|
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"ms": "2.1.3",
|
|
60
|
-
"@workflow/astro": "5.0.0-beta.
|
|
61
|
-
"@workflow/cli": "5.0.0-beta.
|
|
62
|
-
"@workflow/core": "5.0.0-beta.
|
|
63
|
-
"@workflow/errors": "5.0.0-beta.
|
|
60
|
+
"@workflow/astro": "5.0.0-beta.35",
|
|
61
|
+
"@workflow/cli": "5.0.0-beta.35",
|
|
62
|
+
"@workflow/core": "5.0.0-beta.35",
|
|
63
|
+
"@workflow/errors": "5.0.0-beta.11",
|
|
64
64
|
"@workflow/typescript-plugin": "5.0.0-beta.5",
|
|
65
65
|
"@workflow/utils": "5.0.0-beta.6",
|
|
66
|
-
"@workflow/next": "5.0.0-beta.
|
|
67
|
-
"@workflow/nest": "5.0.0-beta.
|
|
68
|
-
"@workflow/nitro": "5.0.0-beta.
|
|
69
|
-
"@workflow/nuxt": "5.0.0-beta.
|
|
70
|
-
"@workflow/sveltekit": "5.0.0-beta.
|
|
71
|
-
"@workflow/rollup": "5.0.0-beta.
|
|
66
|
+
"@workflow/next": "5.0.0-beta.35",
|
|
67
|
+
"@workflow/nest": "5.0.0-beta.35",
|
|
68
|
+
"@workflow/nitro": "5.0.0-beta.35",
|
|
69
|
+
"@workflow/nuxt": "5.0.0-beta.35",
|
|
70
|
+
"@workflow/sveltekit": "5.0.0-beta.35",
|
|
71
|
+
"@workflow/rollup": "5.0.0-beta.35"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@types/ms": "2.1.0",
|