workflow 5.0.0-beta.5 → 5.0.0-beta.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -23
- package/dist/api-workflow.d.ts +1 -1
- package/dist/api-workflow.d.ts.map +1 -1
- package/dist/api-workflow.js +1 -1
- package/dist/api.d.ts +3 -3
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +5 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/internal/builtins.d.ts +20 -3
- package/dist/internal/builtins.d.ts.map +1 -1
- package/dist/internal/builtins.js +68 -4
- package/dist/internal/errors.d.ts +1 -1
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/errors.js +2 -2
- package/dist/nest-builder.d.ts +2 -0
- package/dist/nest-builder.d.ts.map +1 -0
- package/dist/nest-builder.js +2 -0
- package/dist/nest-vercel-builder.d.ts +2 -0
- package/dist/nest-vercel-builder.d.ts.map +1 -0
- package/dist/nest-vercel-builder.js +2 -0
- package/dist/observability.d.ts +1 -1
- package/dist/observability.js +2 -2
- package/dist/runtime.d.ts +2 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +4 -1
- package/docs/ai/chat-session-modeling.mdx +29 -26
- package/docs/ai/defining-tools.mdx +6 -7
- package/docs/ai/human-in-the-loop.mdx +11 -11
- package/docs/ai/index.mdx +50 -45
- package/docs/ai/message-queueing.mdx +16 -16
- package/docs/ai/meta.json +1 -0
- package/docs/ai/resumable-streams.mdx +40 -28
- package/docs/ai/sleep-and-delays.mdx +10 -10
- package/docs/ai/streaming-updates-from-tools.mdx +6 -6
- package/docs/api-reference/index.mdx +24 -0
- package/docs/api-reference/meta.json +8 -0
- package/docs/api-reference/vitest/index.mdx +9 -15
- package/docs/api-reference/workflow/create-hook.mdx +89 -10
- package/docs/api-reference/workflow/create-webhook.mdx +16 -15
- package/docs/api-reference/workflow/define-hook.mdx +35 -33
- package/docs/api-reference/workflow/fatal-error.mdx +30 -8
- package/docs/api-reference/workflow/fetch.mdx +14 -10
- package/docs/api-reference/workflow/get-step-metadata.mdx +2 -2
- package/docs/api-reference/workflow/get-workflow-metadata.mdx +3 -3
- package/docs/api-reference/workflow/get-writable.mdx +7 -7
- package/docs/api-reference/workflow/index.mdx +4 -1
- package/docs/api-reference/workflow/retryable-error.mdx +1 -1
- package/docs/api-reference/workflow/set-attributes.mdx +61 -0
- package/docs/api-reference/workflow/sleep.mdx +4 -4
- package/docs/api-reference/workflow-ai/durable-agent.mdx +48 -86
- package/docs/api-reference/workflow-ai/index.mdx +3 -3
- package/docs/api-reference/workflow-ai/workflow-chat-transport.mdx +67 -24
- package/docs/api-reference/workflow-api/get-hook-by-token.mdx +26 -12
- package/docs/api-reference/workflow-api/get-run.mdx +43 -8
- package/docs/api-reference/workflow-api/index.mdx +6 -10
- package/docs/api-reference/workflow-api/resume-hook.mdx +73 -12
- package/docs/api-reference/workflow-api/resume-webhook.mdx +11 -9
- package/docs/api-reference/workflow-api/start.mdx +60 -13
- package/docs/api-reference/workflow-astro/index.mdx +18 -0
- package/docs/api-reference/workflow-astro/meta.json +4 -0
- package/docs/api-reference/workflow-astro/workflow.mdx +45 -0
- package/docs/api-reference/workflow-errors/entity-conflict-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/hook-conflict-error.mdx +60 -0
- package/docs/api-reference/workflow-errors/hook-not-found-error.mdx +8 -8
- package/docs/api-reference/workflow-errors/index.mdx +88 -0
- package/docs/api-reference/workflow-errors/meta.json +6 -0
- package/docs/api-reference/workflow-errors/precondition-failed-error.mdx +68 -0
- package/docs/api-reference/workflow-errors/run-expired-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/run-not-supported-error.mdx +58 -0
- package/docs/api-reference/workflow-errors/step-not-registered-error.mdx +5 -5
- package/docs/api-reference/workflow-errors/throttle-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/too-early-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/workflow-error.mdx +52 -0
- package/docs/api-reference/workflow-errors/workflow-not-registered-error.mdx +5 -6
- package/docs/api-reference/workflow-errors/workflow-run-cancelled-error.mdx +6 -6
- package/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx +5 -5
- package/docs/api-reference/workflow-errors/workflow-run-not-completed-error.mdx +58 -0
- package/docs/api-reference/workflow-errors/workflow-run-not-found-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/workflow-runtime-error.mdx +58 -0
- package/docs/api-reference/workflow-errors/workflow-world-error.mdx +8 -8
- package/docs/api-reference/workflow-globals.mdx +14 -10
- package/docs/api-reference/workflow-nest/configure-workflow-controller.mdx +33 -0
- package/docs/api-reference/workflow-nest/index.mdx +31 -0
- package/docs/api-reference/workflow-nest/meta.json +9 -0
- package/docs/api-reference/workflow-nest/nest-local-builder.mdx +64 -0
- package/docs/api-reference/workflow-nest/workflow-controller.mdx +40 -0
- package/docs/api-reference/workflow-nest/workflow-module.mdx +74 -0
- package/docs/api-reference/workflow-next/with-workflow.mdx +39 -17
- package/docs/api-reference/workflow-nitro/index.mdx +60 -0
- package/docs/api-reference/workflow-nuxt/index.mdx +48 -0
- package/docs/api-reference/workflow-observability/hydrate-data.mdx +35 -0
- package/docs/api-reference/workflow-observability/hydrate-resource-io.mdx +62 -0
- package/docs/api-reference/workflow-observability/index.mdx +62 -0
- package/docs/api-reference/workflow-observability/meta.json +11 -0
- package/docs/api-reference/workflow-observability/observability-revivers.mdx +50 -0
- package/docs/api-reference/workflow-observability/parse-class-name.mdx +41 -0
- package/docs/api-reference/workflow-observability/parse-step-name.mdx +40 -0
- package/docs/api-reference/workflow-observability/parse-workflow-name.mdx +55 -0
- package/docs/api-reference/workflow-runtime/create-world.mdx +39 -0
- package/docs/api-reference/workflow-runtime/get-world-handlers.mdx +44 -0
- package/docs/api-reference/{workflow-api → workflow-runtime}/get-world.mdx +11 -14
- package/docs/api-reference/workflow-runtime/health-check.mdx +50 -0
- package/docs/api-reference/workflow-runtime/index.mdx +41 -0
- package/docs/api-reference/workflow-runtime/meta.json +12 -0
- package/docs/api-reference/workflow-runtime/set-world.mdx +51 -0
- package/docs/api-reference/workflow-runtime/workflow-entrypoint.mdx +43 -0
- package/docs/api-reference/workflow-runtime/world/analytics.mdx +315 -0
- package/docs/api-reference/workflow-runtime/world/index.mdx +60 -0
- package/docs/api-reference/workflow-runtime/world/meta.json +4 -0
- package/docs/api-reference/workflow-runtime/world/queue.mdx +88 -0
- package/docs/api-reference/{workflow-api → workflow-runtime}/world/storage.mdx +98 -34
- package/docs/api-reference/{workflow-api → workflow-runtime}/world/streams.mdx +8 -8
- package/docs/api-reference/workflow-serde/index.mdx +1 -2
- package/docs/api-reference/workflow-serde/workflow-deserialize.mdx +3 -4
- package/docs/api-reference/workflow-serde/workflow-serialize.mdx +8 -8
- package/docs/api-reference/workflow-sveltekit/index.mdx +18 -0
- package/docs/api-reference/workflow-sveltekit/meta.json +4 -0
- package/docs/api-reference/workflow-sveltekit/workflow-plugin.mdx +42 -0
- package/docs/api-reference/workflow-vite/index.mdx +18 -0
- package/docs/api-reference/workflow-vite/meta.json +4 -0
- package/docs/api-reference/workflow-vite/workflow.mdx +48 -0
- package/docs/changelog/attributes-mvp.mdx +380 -0
- package/docs/changelog/batched-event-writes.mdx +79 -0
- package/docs/changelog/eager-processing.mdx +110 -436
- package/docs/changelog/index.mdx +4 -2
- package/docs/changelog/lazy-event-creation.md +127 -0
- package/docs/changelog/lazy-hook-resume.mdx +78 -0
- package/docs/changelog/meta.json +11 -1
- package/docs/changelog/resilient-resume.mdx +32 -0
- package/docs/changelog/resilient-start.mdx +33 -285
- package/docs/changelog/step-message-ownership.mdx +360 -0
- package/docs/changelog/turbo-mode.md +87 -0
- package/docs/comparisons/index.mdx +66 -0
- package/docs/comparisons/meta.json +11 -0
- package/docs/comparisons/workflow-sdk-vs-aws-agentcore.mdx +55 -0
- package/docs/comparisons/workflow-sdk-vs-aws-step-functions.mdx +111 -0
- package/docs/comparisons/workflow-sdk-vs-cloudflare-workflows.mdx +71 -0
- package/docs/comparisons/workflow-sdk-vs-inngest.mdx +102 -0
- package/docs/comparisons/workflow-sdk-vs-temporal.mdx +123 -0
- package/docs/comparisons/workflow-sdk-vs-trigger-dev.mdx +104 -0
- package/docs/configuration/build-and-diagnostics.mdx +70 -0
- package/docs/configuration/cli-and-web-ui.mdx +241 -0
- package/docs/configuration/framework-options.mdx +165 -0
- package/docs/configuration/index.mdx +32 -0
- package/docs/configuration/meta.json +12 -0
- package/docs/configuration/runtime-tuning.mdx +376 -0
- package/docs/configuration/worlds.mdx +313 -0
- package/docs/cookbook/advanced/child-workflows.mdx +211 -264
- package/docs/cookbook/advanced/meta.json +6 -1
- package/docs/cookbook/advanced/publishing-libraries.mdx +65 -56
- package/docs/cookbook/advanced/serializable-steps.mdx +28 -20
- package/docs/cookbook/advanced/upgrading-workflows.mdx +199 -0
- package/docs/cookbook/agent-patterns/agent-cancellation.mdx +27 -19
- package/docs/cookbook/agent-patterns/durable-agent.mdx +14 -142
- package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +30 -22
- package/docs/cookbook/common-patterns/batching.mdx +18 -14
- package/docs/cookbook/common-patterns/idempotency.mdx +41 -53
- package/docs/cookbook/common-patterns/rate-limiting.mdx +8 -4
- package/docs/cookbook/common-patterns/saga.mdx +23 -19
- package/docs/cookbook/common-patterns/scheduling.mdx +34 -22
- package/docs/cookbook/common-patterns/sequential-and-parallel.mdx +29 -25
- package/docs/cookbook/common-patterns/timeouts.mdx +26 -21
- package/docs/cookbook/common-patterns/webhooks.mdx +10 -6
- package/docs/cookbook/common-patterns/workflow-composition.mdx +30 -27
- package/docs/cookbook/index.mdx +22 -21
- package/docs/cookbook/integrations/ai-sdk.mdx +85 -47
- package/docs/cookbook/integrations/chat-sdk.mdx +50 -33
- package/docs/cookbook/integrations/sandbox.mdx +62 -45
- package/docs/deploying.mdx +95 -0
- package/docs/errors/abort-signal-timeout-in-workflow.mdx +16 -12
- package/docs/errors/corrupted-event-log.mdx +29 -18
- package/docs/errors/deployment-mismatch.mdx +71 -0
- package/docs/errors/fetch-in-workflow.mdx +11 -7
- package/docs/errors/hook-conflict.mdx +69 -13
- package/docs/errors/index.mdx +2 -36
- package/docs/errors/node-js-module-in-workflow.mdx +9 -5
- package/docs/errors/replay-divergence.mdx +27 -0
- package/docs/errors/run-expired.mdx +85 -0
- package/docs/errors/runtime-decryption-failed.mdx +77 -0
- package/docs/errors/serialization-failed.mdx +44 -12
- package/docs/errors/start-invalid-workflow-function.mdx +9 -5
- package/docs/errors/step-executed-multiple-times.mdx +23 -0
- package/docs/errors/step-not-registered.mdx +6 -6
- package/docs/errors/timeout-in-workflow.mdx +12 -8
- package/docs/errors/webhook-invalid-respond-with-value.mdx +18 -18
- package/docs/errors/webhook-response-not-sent.mdx +20 -16
- package/docs/errors/workflow-not-registered.mdx +5 -5
- package/docs/foundations/cancellation.mdx +31 -32
- package/docs/foundations/errors-and-retries.mdx +42 -11
- package/docs/foundations/hooks.mdx +64 -35
- package/docs/foundations/idempotency.mdx +244 -12
- package/docs/foundations/index.mdx +1 -23
- package/docs/foundations/meta.json +2 -1
- package/docs/foundations/serialization.mdx +21 -22
- package/docs/foundations/starting-workflows.mdx +106 -30
- package/docs/foundations/streaming.mdx +107 -59
- package/docs/foundations/versioning.mdx +263 -0
- package/docs/foundations/workflows-and-steps.mdx +9 -9
- package/docs/getting-started/astro.mdx +22 -18
- package/docs/getting-started/express.mdx +15 -11
- package/docs/getting-started/fastify.mdx +15 -11
- package/docs/getting-started/hono.mdx +15 -11
- package/docs/getting-started/index.mdx +10 -3
- package/docs/getting-started/meta.json +3 -1
- package/docs/getting-started/nestjs.mdx +87 -20
- package/docs/getting-started/next.mdx +22 -16
- package/docs/getting-started/nitro.mdx +22 -18
- package/docs/getting-started/nuxt.mdx +15 -11
- package/docs/getting-started/python.mdx +135 -40
- package/docs/getting-started/react-router/index.mdx +33 -0
- package/docs/getting-started/react-router/meta.json +5 -0
- package/docs/getting-started/react-router/v7.mdx +237 -0
- package/docs/getting-started/react-router/v8.mdx +232 -0
- package/docs/getting-started/sveltekit.mdx +20 -16
- package/docs/getting-started/tanstack-start.mdx +17 -13
- package/docs/getting-started/vite.mdx +15 -11
- package/docs/how-it-works/cancellation.mdx +63 -63
- package/docs/how-it-works/code-transform.mdx +83 -67
- package/docs/how-it-works/encryption.mdx +30 -26
- package/docs/how-it-works/event-sourcing.mdx +98 -34
- package/docs/how-it-works/framework-integrations.mdx +96 -337
- package/docs/how-it-works/understanding-directives.mdx +22 -22
- package/docs/internal/index.mdx +6 -4
- package/docs/internal/meta.json +6 -1
- package/docs/internal/nitro-native-build.mdx +38 -0
- package/docs/internal/nitro-web-ui.mdx +24 -0
- package/docs/internal/serializable-abort-controller.mdx +7 -7
- package/docs/meta.json +3 -2
- package/docs/observability/attributes.mdx +134 -0
- package/docs/observability/index.mdx +32 -10
- package/docs/observability/meta.json +1 -1
- package/docs/observability/retention.mdx +93 -0
- package/docs/observability/tracing.mdx +124 -0
- package/docs/testing/index.mdx +36 -36
- package/docs/testing/server-based.mdx +10 -10
- package/docs/whats-new.mdx +186 -0
- package/package.json +17 -14
- package/docs/api-reference/workflow-api/world/index.mdx +0 -58
- package/docs/api-reference/workflow-api/world/meta.json +0 -4
- package/docs/api-reference/workflow-api/world/observability.mdx +0 -164
- package/docs/api-reference/workflow-api/world/queue.mdx +0 -86
- package/docs/deploying/building-a-world.mdx +0 -251
- package/docs/deploying/index.mdx +0 -95
- package/docs/deploying/meta.json +0 -4
- package/docs/deploying/world/local-world.mdx +0 -84
- package/docs/deploying/world/meta.json +0 -4
- package/docs/deploying/world/postgres-world.mdx +0 -224
- package/docs/deploying/world/vercel-world.mdx +0 -179
- package/docs/migration-guides/index.mdx +0 -34
- package/docs/migration-guides/meta.json +0 -9
- package/docs/migration-guides/migrating-from-aws-step-functions.mdx +0 -363
- package/docs/migration-guides/migrating-from-inngest.mdx +0 -314
- package/docs/migration-guides/migrating-from-temporal.mdx +0 -318
- package/docs/migration-guides/migrating-from-trigger-dev.mdx +0 -337
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "workflow/api"
|
|
3
|
-
description: Runtime functions to inspect runs, start workflows, and
|
|
3
|
+
description: Runtime functions to inspect runs, start workflows, and manage hooks.
|
|
4
4
|
type: overview
|
|
5
5
|
summary: Explore runtime functions for starting workflows, inspecting runs, and managing hooks.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
The `workflow/api` package provides runtime functions to inspect runs, start new runs, and manage hooks.
|
|
9
9
|
|
|
10
10
|
## Functions
|
|
11
11
|
|
|
12
|
-
The API package is for access and introspection of workflow data to inspect runs, start new runs, or access anything else directly accessible by the world.
|
|
13
|
-
|
|
14
12
|
<Cards>
|
|
15
13
|
<Card href="/docs/api-reference/workflow-api/start" title="start()">
|
|
16
14
|
Start/enqueue a new workflow run.
|
|
@@ -27,10 +25,8 @@ The API package is for access and introspection of workflow data to inspect runs
|
|
|
27
25
|
<Card href="/docs/api-reference/workflow-api/get-run" title="getRun()">
|
|
28
26
|
Get workflow run status and metadata without waiting for completion.
|
|
29
27
|
</Card>
|
|
30
|
-
<Card href="/docs/api-reference/workflow-api/get-world" title="getWorld()">
|
|
31
|
-
Async: resolve the World instance for storage, queuing, and streaming backends.
|
|
32
|
-
</Card>
|
|
33
|
-
<Card href="/docs/api-reference/workflow-api/world" title="World SDK">
|
|
34
|
-
Low-level API for inspecting runs, steps, events, hooks, streams, and queues.
|
|
35
|
-
</Card>
|
|
36
28
|
</Cards>
|
|
29
|
+
|
|
30
|
+
<Callout type="info">
|
|
31
|
+
Looking for `getWorld()` and the World SDK? They are exported from `workflow/runtime`. See the [`workflow/runtime` reference](/docs/api-reference/workflow-runtime).
|
|
32
|
+
</Callout>
|
|
@@ -7,11 +7,16 @@ prerequisites:
|
|
|
7
7
|
- /docs/foundations/hooks
|
|
8
8
|
related:
|
|
9
9
|
- /docs/api-reference/workflow-api/resume-webhook
|
|
10
|
+
- /docs/foundations/idempotency
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
Resumes a workflow run by sending a payload to a hook identified by its token.
|
|
13
14
|
|
|
14
|
-
It
|
|
15
|
+
It durably writes the `hook_received` event and only then publishes a workflow wake. The call resolves only after both operations succeed, in that order.
|
|
16
|
+
|
|
17
|
+
`resumeHook()` throws `HookNotFoundError` when no hook holds the token or when its `hook_received` write is refused because the hook was disposed or the run ended. See [durable hook resume](/docs/changelog/lazy-hook-resume).
|
|
18
|
+
|
|
19
|
+
If `resumeHook()` throws any other error, the outcome is ambiguous only in dispatch, never in durability: the event may already be durable even though the workflow wake failed, and any later wake of the run delivers it. Calling `resumeHook()` again creates a new `resumeId` and can append a second `hook_received`. Callers that need at-most-once behavior across separate invocations must retain and deduplicate their own request key.
|
|
15
20
|
|
|
16
21
|
<Callout type="warn">
|
|
17
22
|
`resumeHook` is a runtime function that must be called from outside a workflow function.
|
|
@@ -34,7 +39,7 @@ export async function POST(request: Request) {
|
|
|
34
39
|
}
|
|
35
40
|
```
|
|
36
41
|
|
|
37
|
-
## API
|
|
42
|
+
## API signature
|
|
38
43
|
|
|
39
44
|
### Parameters
|
|
40
45
|
|
|
@@ -47,18 +52,18 @@ showSections={["parameters"]}
|
|
|
47
52
|
|
|
48
53
|
### Returns
|
|
49
54
|
|
|
50
|
-
Returns a `Promise<Hook
|
|
55
|
+
Returns a `Promise<ResumedHook>`, a `Hook` (from `workflow/api`) extended with an optional `resilientResume` flag. Resolving means the payload is durably recorded as `hook_received` and the workflow wake was accepted. `resilientResume` is retained for source compatibility and is no longer set by any path. Resuming never reads the hook's metadata, so the resolved hook's `metadata` is a Promise that hydrates on first access, exactly as with [`getHookByToken()`](/docs/api-reference/workflow-api/get-hook-by-token): `await hook.metadata` to read it. The resolved hook:
|
|
51
56
|
|
|
52
57
|
<TSDoc
|
|
53
58
|
definition={`
|
|
54
|
-
import type { Hook } from "
|
|
59
|
+
import type { Hook } from "workflow/api";
|
|
55
60
|
export default Hook;`}
|
|
56
61
|
showSections={["returns"]}
|
|
57
62
|
/>
|
|
58
63
|
|
|
59
64
|
## Examples
|
|
60
65
|
|
|
61
|
-
### Basic API
|
|
66
|
+
### Basic API route
|
|
62
67
|
|
|
63
68
|
Using `resumeHook` in a basic API route to resume a hook:
|
|
64
69
|
|
|
@@ -81,7 +86,7 @@ export async function POST(request: Request) {
|
|
|
81
86
|
}
|
|
82
87
|
```
|
|
83
88
|
|
|
84
|
-
### With
|
|
89
|
+
### With type safety
|
|
85
90
|
|
|
86
91
|
Defining a payload type and using `resumeHook` to resume a hook with type safety:
|
|
87
92
|
|
|
@@ -109,7 +114,7 @@ export async function POST(request: Request) {
|
|
|
109
114
|
}
|
|
110
115
|
```
|
|
111
116
|
|
|
112
|
-
### Server
|
|
117
|
+
### Server action (Next.js)
|
|
113
118
|
|
|
114
119
|
Using `resumeHook` in Next.js server actions to resume a hook:
|
|
115
120
|
|
|
@@ -128,7 +133,7 @@ export async function approveRequest(token: string, approved: boolean) {
|
|
|
128
133
|
}
|
|
129
134
|
```
|
|
130
135
|
|
|
131
|
-
### Webhook
|
|
136
|
+
### Webhook handler
|
|
132
137
|
|
|
133
138
|
Using `resumeHook` in a generic webhook handler to resume a hook:
|
|
134
139
|
|
|
@@ -155,8 +160,64 @@ export async function POST(request: Request) {
|
|
|
155
160
|
}
|
|
156
161
|
```
|
|
157
162
|
|
|
158
|
-
|
|
163
|
+
### Resume or start
|
|
164
|
+
|
|
165
|
+
A common endpoint shape is "resume or start": one route that resumes the active workflow run for a business key if one exists, or starts a new run otherwise. This comes up when the workflow uses a deterministic hook token as its idempotency key, for example, one active run per order or conversation.
|
|
166
|
+
|
|
167
|
+
`resumeHook()` is the resume half of that flow. Try it first; if it throws `HookNotFoundError`, no active run owns the token yet, so start the workflow. One subtlety: `start()` returns before the new run executes and registers its hook, so you cannot resume immediately after starting. Retry the resume until the hook is registered: if you drop the payload and only start the workflow, the data from this request is lost.
|
|
168
|
+
|
|
169
|
+
```typescript lineNumbers
|
|
170
|
+
import { resumeHook, start } from "workflow/api";
|
|
171
|
+
import { HookNotFoundError } from "workflow/errors";
|
|
172
|
+
import { processOrder } from "./workflows/process-order";
|
|
173
|
+
|
|
174
|
+
type OrderRequest = { confirmed: boolean };
|
|
175
|
+
|
|
176
|
+
async function resumeWithRetry(token: string, payload: OrderRequest) {
|
|
177
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
178
|
+
try {
|
|
179
|
+
return await resumeHook(token, payload); // [!code highlight]
|
|
180
|
+
} catch (error) {
|
|
181
|
+
if (!HookNotFoundError.is(error)) throw error;
|
|
182
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
throw new Error("Workflow did not register its hook in time");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export async function POST(request: Request) {
|
|
190
|
+
const { orderId, confirmed } = await request.json();
|
|
191
|
+
const token = `order:${orderId}`;
|
|
192
|
+
const payload = { confirmed };
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
// An active run already owns this token: resume it.
|
|
196
|
+
const hook = await resumeHook(token, payload); // [!code highlight]
|
|
197
|
+
return Response.json({ runId: hook.runId, reused: true });
|
|
198
|
+
} catch (error) {
|
|
199
|
+
if (!HookNotFoundError.is(error)) throw error;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// No hook yet: start a new run, then retry the resume so this
|
|
203
|
+
// request's payload still reaches the workflow.
|
|
204
|
+
const run = await start(processOrder, [orderId]); // [!code highlight]
|
|
205
|
+
const resumed = await resumeWithRetry(token, payload);
|
|
206
|
+
|
|
207
|
+
// A concurrent request can win the race between `start()` and hook
|
|
208
|
+
// registration; the resume always reaches the actual active owner.
|
|
209
|
+
return Response.json({
|
|
210
|
+
runId: resumed.runId,
|
|
211
|
+
reused: resumed.runId !== run.runId,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
See [Run idempotency](/docs/foundations/idempotency#run-idempotency) for the full pattern, including how the workflow claims the token with `hook.getConflict()` and how concurrent starts converge on one active owner.
|
|
217
|
+
|
|
218
|
+
## Related functions
|
|
159
219
|
|
|
160
|
-
- [`getHookByToken()`](/docs/api-reference/workflow-api/get-hook-by-token)
|
|
161
|
-
- [`createHook()`](/docs/api-reference/workflow/create-hook)
|
|
162
|
-
- [`defineHook()`](/docs/api-reference/workflow/define-hook)
|
|
220
|
+
- [`getHookByToken()`](/docs/api-reference/workflow-api/get-hook-by-token): Get hook details before resuming.
|
|
221
|
+
- [`createHook()`](/docs/api-reference/workflow/create-hook): Create a hook in a workflow.
|
|
222
|
+
- [`defineHook()`](/docs/api-reference/workflow/define-hook): Type-safe hook helper.
|
|
223
|
+
- [Idempotency](/docs/foundations/idempotency): Deduplicate step side effects and workflow starts.
|
|
@@ -11,7 +11,7 @@ related:
|
|
|
11
11
|
|
|
12
12
|
Resumes a workflow run by sending an HTTP `Request` to a webhook identified by its token.
|
|
13
13
|
|
|
14
|
-
This function
|
|
14
|
+
This function publishes a workflow invocation carrying the request; the runtime creates the `hook_received` event from it and continues execution. It's designed to be called from API routes or server actions that receive external HTTP requests.
|
|
15
15
|
|
|
16
16
|
<Callout type="warn">
|
|
17
17
|
`resumeWebhook` is a runtime function that must be called from outside a workflow function.
|
|
@@ -37,7 +37,7 @@ export async function POST(request: Request) {
|
|
|
37
37
|
}
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
## API
|
|
40
|
+
## API signature
|
|
41
41
|
|
|
42
42
|
### Parameters
|
|
43
43
|
|
|
@@ -50,16 +50,18 @@ showSections={['parameters']}
|
|
|
50
50
|
|
|
51
51
|
### Returns
|
|
52
52
|
|
|
53
|
-
Returns a `Promise<Response>` that resolves to:
|
|
53
|
+
Returns a `Promise<Response>` that resolves to one of:
|
|
54
54
|
|
|
55
|
-
- `
|
|
55
|
+
- A `202 Accepted` response when the webhook was created in the default mode (no `respondWith` option).
|
|
56
|
+
- The exact `Response` object configured via `createWebhook({ respondWith: new Response(...) })`.
|
|
57
|
+
- The workflow's manual `Response` when the webhook was created with `createWebhook({ respondWith: "manual" })` and a step calls `request.respondWith(response)`.
|
|
56
58
|
|
|
57
|
-
Throws
|
|
59
|
+
Throws [`HookNotFoundError`](/docs/api-reference/workflow-errors/hook-not-found-error) if the token does not match an active webhook.
|
|
58
60
|
|
|
59
|
-
## Usage
|
|
61
|
+
## Usage note
|
|
60
62
|
|
|
61
63
|
<Callout type="warn">
|
|
62
|
-
In most cases, you should not need to call `resumeWebhook()` directly. When you use `createWebhook()`, the framework automatically generates a
|
|
64
|
+
In most cases, you should not need to call `resumeWebhook()` directly. When you use `createWebhook()`, the framework automatically generates a webhook token and provides a public URL at `/.well-known/workflow/v1/webhook/:token`. External systems can send HTTP requests directly to that URL.
|
|
63
65
|
|
|
64
66
|
For server-side hook resumption with deterministic tokens, use [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook) with [`createHook()`](/docs/api-reference/workflow/create-hook) instead.
|
|
65
67
|
</Callout>
|
|
@@ -81,14 +83,14 @@ export async function POST(request: Request) {
|
|
|
81
83
|
|
|
82
84
|
try {
|
|
83
85
|
const response = await resumeWebhook(token, request); // [!code highlight]
|
|
84
|
-
return response; //
|
|
86
|
+
return response; // 202 Accepted, a configured static Response, or a manual workflow response
|
|
85
87
|
} catch (error) {
|
|
86
88
|
return new Response("Webhook not found", { status: 404 });
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
```
|
|
90
92
|
|
|
91
|
-
## Related
|
|
93
|
+
## Related functions
|
|
92
94
|
|
|
93
95
|
- [`createWebhook()`](/docs/api-reference/workflow/create-webhook) - Create a webhook in a workflow
|
|
94
96
|
- [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook) - Resume a hook with arbitrary payload
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
title: start
|
|
3
3
|
description: Start and enqueue a new workflow run.
|
|
4
4
|
type: reference
|
|
5
|
-
summary: Use start to programmatically enqueue a new workflow run
|
|
5
|
+
summary: Use start to programmatically enqueue a new workflow run.
|
|
6
6
|
prerequisites:
|
|
7
7
|
- /docs/foundations/starting-workflows
|
|
8
|
+
related:
|
|
9
|
+
- /docs/foundations/idempotency
|
|
8
10
|
---
|
|
9
11
|
|
|
10
12
|
Start/enqueue a new workflow run.
|
|
@@ -16,7 +18,7 @@ import { myWorkflow } from "./workflows/my-workflow";
|
|
|
16
18
|
const run = await start(myWorkflow); // [!code highlight]
|
|
17
19
|
```
|
|
18
20
|
|
|
19
|
-
## API
|
|
21
|
+
## API signature
|
|
20
22
|
|
|
21
23
|
### Parameters
|
|
22
24
|
|
|
@@ -48,21 +50,26 @@ showSections={["returns"]}
|
|
|
48
50
|
|
|
49
51
|
Learn more about [`WorkflowReadableStreamOptions`](/docs/api-reference/workflow-api/get-run#workflowreadablestreamoptions).
|
|
50
52
|
|
|
51
|
-
## Good to
|
|
53
|
+
## Good to know
|
|
52
54
|
|
|
53
|
-
*
|
|
55
|
+
* Use the `start()` function in runtime contexts to programmatically trigger workflow executions.
|
|
56
|
+
* In v5, you can also call `start()` directly from a workflow function to spawn a child run or continue work in a new run. See [Workflow Composition](/cookbook/common-patterns/workflow-composition) and [Versioning](/docs/foundations/versioning).
|
|
54
57
|
* This is different from calling workflow functions directly, which is the typical pattern in Next.js applications.
|
|
55
|
-
* The function returns immediately after enqueuing the workflow
|
|
58
|
+
* The function returns immediately after enqueuing the workflow. It doesn't wait for the workflow to complete.
|
|
59
|
+
* Each call to `start()` creates a new workflow run. If retried requests must route to one active workflow, have the workflow create a deterministic hook token and use [`getHookByToken()`](/docs/api-reference/workflow-api/get-hook-by-token) to reuse an already-registered active hook. The lookup is not atomic with `start()`, so concurrent callers can still create extra runs before the hook is registered. Handle that race inside the workflow by checking `await hook.getConflict()` before duplicate-sensitive work. On a conflict, it resolves with the run that owns the token, so the duplicate can return the active owner to the caller. If duplicates must be rejected before a workflow body runs, keep a durable request record until native atomic start-and-hook registration exists. See [Idempotency](/docs/foundations/idempotency#run-idempotency).
|
|
56
60
|
* All arguments must be [serializable](/docs/foundations/serialization).
|
|
57
|
-
* When `deploymentId
|
|
61
|
+
* When you provide `deploymentId`, the argument types and return type become `unknown` because the workflow function's types may differ across deployments.
|
|
62
|
+
* `attributes` seeds plaintext run metadata as part of creation and requires a World implementing spec version 4 or later. Keys that start with `$` are reserved for framework and library code; framework-level callers can pass `allowReservedAttributes: true` to seed reserved keys, with the same semantics as the [`setAttributes`](/docs/api-reference/workflow/set-attributes) option of the same name.
|
|
63
|
+
* `region` pins the new run to a specific region on Worlds with a regional dimension. The [Vercel World](/worlds/vercel#explicit-region-selection) then serves the run's storage, queue dispatch, and streams from that region. When you omit `region`, the run is pinned to the region where it was created. Worlds without regions ignore the option.
|
|
64
|
+
* `experimental_retention` asks the World to delete the run's user data as soon as the run completes or fails, instead of keeping it for the World's default window. `0` requests immediate deletion; `'default'` is identical to omitting the option. These are the only two values accepted — the value is a duration and zero is the only one implemented, and its unit is not yet decided. Recorded as the reserved `$retention` attribute, so it needs a World implementing spec version 4 or later. Retention is enforced by the World, not the SDK: the first-party Worlds implement it and a World that does not keeps the data. Note that `await run.returnValue` on a run started with `experimental_retention: 0` usually throws [`RunExpiredError`](/docs/errors/run-expired) rather than resolving, because the deletion races the read. See [Data retention](/docs/observability/retention).
|
|
58
65
|
|
|
59
66
|
<Callout type="info">
|
|
60
|
-
If `start()` throws `'start' received an invalid workflow function. Ensure the Workflow
|
|
67
|
+
If `start()` throws `'start' received an invalid workflow function. Ensure the Workflow SDK is configured correctly and the function includes a 'use workflow' directive.`, the compiler did not transform the passed function as a workflow. The two most common causes are a missing `"use workflow"` directive or missing framework integration. See [start-invalid-workflow-function](/docs/errors/start-invalid-workflow-function).
|
|
61
68
|
</Callout>
|
|
62
69
|
|
|
63
70
|
## Examples
|
|
64
71
|
|
|
65
|
-
### With
|
|
72
|
+
### With arguments
|
|
66
73
|
|
|
67
74
|
```typescript
|
|
68
75
|
import { start } from "workflow/api";
|
|
@@ -78,13 +85,14 @@ import { start } from "workflow/api";
|
|
|
78
85
|
import { myWorkflow } from "./workflows/my-workflow";
|
|
79
86
|
|
|
80
87
|
const run = await start(myWorkflow, ["arg1", "arg2"], { // [!code highlight]
|
|
81
|
-
deploymentId: "custom-deployment-id" // [!code highlight]
|
|
88
|
+
deploymentId: "custom-deployment-id", // [!code highlight]
|
|
89
|
+
attributes: { source: "checkout" } // [!code highlight]
|
|
82
90
|
}); // [!code highlight]
|
|
83
91
|
```
|
|
84
92
|
|
|
85
93
|
### Using `deploymentId: "latest"`
|
|
86
94
|
|
|
87
|
-
Set `deploymentId` to `"latest"` to automatically resolve the most recent deployment for the current environment. This is useful when you want to ensure a workflow run targets the latest deployed version of your application rather than the deployment that initiated the call.
|
|
95
|
+
Set `deploymentId` to `"latest"` to automatically resolve the most recent deployment for the current environment. This is useful when you want to ensure a workflow run targets the latest deployed version of your application rather than the deployment that initiated the call. For when to use this and how it fits with default run pinning, see [Versioning](/docs/foundations/versioning).
|
|
88
96
|
|
|
89
97
|
```typescript
|
|
90
98
|
import { start } from "workflow/api";
|
|
@@ -96,12 +104,51 @@ const run = await start(myWorkflow, ["arg1", "arg2"], { // [!code highlight]
|
|
|
96
104
|
```
|
|
97
105
|
|
|
98
106
|
<Callout type="info">
|
|
99
|
-
The `deploymentId` option is currently a Vercel-specific feature.
|
|
107
|
+
The `deploymentId` option is currently a Vercel-specific feature. Other Worlds may implement this option differently to match their own deployment runtimes, and the World spec may rename it from `deploymentId` to `version` in a future SDK version. On Vercel, `"latest"` resolves to the most recent deployment matching your current environment: the same production target for production deployments, or the same git branch for preview deployments.
|
|
108
|
+
|
|
109
|
+
In Worlds without atomic, immutable deployments (such as local development or self-hosted Postgres), there is no notion of multiple deployments to resolve between, so `deploymentId: "latest"` has no effect: the SDK logs a warning and the run targets the current deployment. This means a workflow that opts into `"latest"` on Vercel still runs unchanged in local development.
|
|
110
|
+
</Callout>
|
|
111
|
+
|
|
112
|
+
<Callout type="info">
|
|
113
|
+
Resolving `"latest"` is the one `start()` path that calls the Vercel API, so it
|
|
114
|
+
needs an identity that can see the calling deployment. Inside a Vercel
|
|
115
|
+
deployment the SDK authenticates with the deployment's own OIDC token, which
|
|
116
|
+
carries the owning team, and this takes precedence over a `VERCEL_TOKEN` set in
|
|
117
|
+
the function's environment. A `VERCEL_TOKEN` belongs to a *user* and carries no
|
|
118
|
+
team, so authenticating with it scopes the lookup to that user's default team
|
|
119
|
+
and fails with a 404 whenever that is not the team that owns the deployment.
|
|
120
|
+
Outside a deployment (CLI, CI, the dashboard) `VERCEL_TOKEN` is still used;
|
|
121
|
+
configure the World's `teamId` so the request is scoped explicitly.
|
|
100
122
|
</Callout>
|
|
101
123
|
|
|
102
124
|
<Callout type="warn">
|
|
103
125
|
When using `deploymentId: "latest"`, the workflow run will execute on a potentially different deployment than the one calling `start()`. Be mindful of forward and backward compatibility:
|
|
104
126
|
|
|
105
|
-
- **Workflow identity**: The
|
|
106
|
-
- **Input and output compatibility**: The arguments passed to `start()
|
|
127
|
+
- **Workflow identity**: The function name and file path determine the workflow ID. If the latest deployment has renamed the workflow function or moved it to a different directory, the workflow ID will no longer match and the run will fail to start.
|
|
128
|
+
- **Input and output compatibility**: The calling deployment serializes the arguments passed to `start()`, and the target deployment deserializes them. Similarly, the target deployment serializes the workflow's return value, and the caller deserializes it. If the workflow's expected arguments or return type have changed (for example, added required fields, removed fields, or changed types), the run may fail or behave unexpectedly. Ensure that input and output schemas remain backward-compatible across deployments.
|
|
129
|
+
</Callout>
|
|
130
|
+
|
|
131
|
+
### Inside a workflow function
|
|
132
|
+
|
|
133
|
+
Call `start()` directly from a workflow function to spawn a child run. It is step-backed, so the spawn records a deterministic step boundary in the parent's event log.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { start } from "workflow/api";
|
|
137
|
+
import { childWorkflow } from "./workflows/child";
|
|
138
|
+
|
|
139
|
+
export async function parentWorkflow(value: number) {
|
|
140
|
+
"use workflow";
|
|
141
|
+
|
|
142
|
+
const childRun = await start(childWorkflow, [value]); // [!code highlight]
|
|
143
|
+
const result = await childRun.returnValue; // [!code highlight]
|
|
144
|
+
return { childRunId: childRun.runId, result };
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
<Callout type="info">
|
|
149
|
+
The returned `Run` object is fully functional inside a workflow. Each property access or method call (`.status`, `.returnValue`, `.cancel()`) executes as a separate step. See [Workflow Composition](/cookbook/common-patterns/workflow-composition) for choosing between spawning a child run and awaiting a workflow function directly.
|
|
150
|
+
</Callout>
|
|
151
|
+
|
|
152
|
+
<Callout type="warn">
|
|
153
|
+
`returnValue` polls the child run every second and holds the polling step's worker slot open for as long as the child takes to finish. For long-running children, spawn without awaiting `returnValue` and have the child resume a [hook](/docs/foundations/hooks) when it completes. See the [`startAndWait()` pattern](/cookbook/advanced/child-workflows).
|
|
107
154
|
</Callout>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "workflow/astro"
|
|
3
|
+
description: Astro integration for automatic workflow bundling and route registration.
|
|
4
|
+
type: overview
|
|
5
|
+
summary: Explore the Astro integration for automatic workflow bundling and runtime support.
|
|
6
|
+
related:
|
|
7
|
+
- /docs/getting-started/astro
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
Astro integration for Workflow SDK that transforms workflow code and builds the workflow bundles.
|
|
11
|
+
|
|
12
|
+
## Functions
|
|
13
|
+
|
|
14
|
+
<Cards>
|
|
15
|
+
<Card title="workflow()" href="/docs/api-reference/workflow-astro/workflow">
|
|
16
|
+
Astro integration that transforms workflow code (`"use step"`/`"use workflow"` directives)
|
|
17
|
+
</Card>
|
|
18
|
+
</Cards>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: workflow
|
|
3
|
+
description: Configure Astro to transform workflow directives.
|
|
4
|
+
type: reference
|
|
5
|
+
summary: Add the workflow integration to your Astro config to enable workflow directive transformation.
|
|
6
|
+
prerequisites:
|
|
7
|
+
- /docs/getting-started/astro
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
Returns an Astro integration that transforms workflow code (`"use step"`/`"use workflow"` directives) and builds the workflow bundles.
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
To enable `"use step"` and `"use workflow"` directives while developing locally or deploying to production, add `workflow()` to the `integrations` array of your Astro config.
|
|
15
|
+
|
|
16
|
+
```typescript title="astro.config.mjs" lineNumbers
|
|
17
|
+
// @ts-check
|
|
18
|
+
import { defineConfig } from "astro/config";
|
|
19
|
+
import { workflow } from "workflow/astro"; // [!code highlight]
|
|
20
|
+
|
|
21
|
+
// https://astro.build/config
|
|
22
|
+
export default defineConfig({
|
|
23
|
+
integrations: [workflow()], // [!code highlight]
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The integration registers the workflow Vite transform plugins during `astro:config:setup` and builds the workflow bundles: locally during config setup, or via the Vercel builder after `astro:build:done` when deploying to Vercel.
|
|
28
|
+
|
|
29
|
+
## API signature
|
|
30
|
+
|
|
31
|
+
### Parameters
|
|
32
|
+
|
|
33
|
+
| Parameter | Type | Description |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| `options` | `WorkflowPluginOptions` | Optional. Configures the workflow build. |
|
|
36
|
+
|
|
37
|
+
#### WorkflowPluginOptions
|
|
38
|
+
|
|
39
|
+
| Option | Type | Default | Description |
|
|
40
|
+
| --- | --- | --- | --- |
|
|
41
|
+
| `sourcemap` | `boolean \| 'inline' \| 'linked' \| 'external' \| 'both'` | `'inline'` (dev) / `false` (prod) | Controls source maps on generated workflow bundles. Accepts the same values as esbuild's `sourcemap` option. Defaults to `'inline'` in development and `false` in production (smaller function bundles, which helps stay under the Vercel 250 MB function size limit). Set it explicitly, or use the `WORKFLOW_SOURCEMAP` environment variable, to override in either environment. |
|
|
42
|
+
|
|
43
|
+
### Returns
|
|
44
|
+
|
|
45
|
+
Returns an `AstroIntegration` object to include in the `integrations` array of your Astro config.
|
|
@@ -26,12 +26,12 @@ try {
|
|
|
26
26
|
await world.events.create(runId, event);
|
|
27
27
|
} catch (error) {
|
|
28
28
|
if (EntityConflictError.is(error)) { // [!code highlight]
|
|
29
|
-
// Event already exists
|
|
29
|
+
// Event already exists, safe to ignore during replay
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
## API
|
|
34
|
+
## API signature
|
|
35
35
|
|
|
36
36
|
### Properties
|
|
37
37
|
|
|
@@ -44,11 +44,11 @@ interface EntityConflictError {
|
|
|
44
44
|
export default EntityConflictError;`}
|
|
45
45
|
/>
|
|
46
46
|
|
|
47
|
-
### Static
|
|
47
|
+
### Static methods
|
|
48
48
|
|
|
49
49
|
#### `EntityConflictError.is(value)`
|
|
50
50
|
|
|
51
|
-
Type-safe check for `EntityConflictError` instances. Preferred over `instanceof` because it works across module boundaries and
|
|
51
|
+
Type-safe check for `EntityConflictError` instances. Preferred over `instanceof` because it works across module boundaries and virtual machine contexts.
|
|
52
52
|
|
|
53
53
|
```typescript
|
|
54
54
|
import { EntityConflictError } from "workflow/errors"
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: HookConflictError
|
|
3
|
+
description: Thrown when creating a hook with a token that is already in use by another workflow run.
|
|
4
|
+
type: reference
|
|
5
|
+
summary: Catch HookConflictError when a hook token is already claimed by another active workflow run.
|
|
6
|
+
related:
|
|
7
|
+
- /docs/api-reference/workflow/create-hook
|
|
8
|
+
- /docs/foundations/hooks
|
|
9
|
+
- /docs/errors/hook-conflict
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
`HookConflictError` is thrown when creating a hook with a token that is already in use by another active workflow run. Hook tokens must be unique across all running workflows. See the [hook-conflict](/docs/errors/hook-conflict) error guide for resolution strategies.
|
|
13
|
+
|
|
14
|
+
```typescript lineNumbers
|
|
15
|
+
import { HookConflictError } from "workflow/errors"
|
|
16
|
+
declare function startApprovalWorkflow(token: string): Promise<void>; // @setup
|
|
17
|
+
declare const token: string; // @setup
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
await startApprovalWorkflow(token);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
if (HookConflictError.is(error)) { // [!code highlight]
|
|
23
|
+
console.error(
|
|
24
|
+
`Token "${error.token}" already in use by run ${error.conflictingRunId}`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## API signature
|
|
31
|
+
|
|
32
|
+
### Properties
|
|
33
|
+
|
|
34
|
+
<TSDoc
|
|
35
|
+
definition={`
|
|
36
|
+
interface HookConflictError {
|
|
37
|
+
/** The hook token that conflicted. */
|
|
38
|
+
token: string;
|
|
39
|
+
/** The run ID of the workflow currently holding the token, when known. */
|
|
40
|
+
conflictingRunId?: string;
|
|
41
|
+
/** The error message. */
|
|
42
|
+
message: string;
|
|
43
|
+
}
|
|
44
|
+
export default HookConflictError;`}
|
|
45
|
+
/>
|
|
46
|
+
|
|
47
|
+
### Static methods
|
|
48
|
+
|
|
49
|
+
#### `HookConflictError.is(value)`
|
|
50
|
+
|
|
51
|
+
Type-safe check for `HookConflictError` instances. Preferred over `instanceof` because it works across module boundaries and virtual machine contexts.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { HookConflictError } from "workflow/errors"
|
|
55
|
+
declare const error: unknown; // @setup
|
|
56
|
+
|
|
57
|
+
if (HookConflictError.is(error)) {
|
|
58
|
+
// error is typed as HookConflictError
|
|
59
|
+
}
|
|
60
|
+
```
|
|
@@ -10,9 +10,9 @@ related:
|
|
|
10
10
|
|
|
11
11
|
`HookNotFoundError` is thrown when calling `resumeHook()` or `resumeWebhook()` with a token that does not match any active hook. This typically happens when:
|
|
12
12
|
|
|
13
|
-
- The hook
|
|
14
|
-
- The hook was already consumed and disposed
|
|
15
|
-
- The workflow has not started yet, so the hook does not exist
|
|
13
|
+
- The hook's time to live (TTL) has expired.
|
|
14
|
+
- The hook was already consumed and disposed.
|
|
15
|
+
- The workflow has not started yet, so the hook does not exist.
|
|
16
16
|
|
|
17
17
|
```typescript lineNumbers
|
|
18
18
|
import { HookNotFoundError } from "workflow/errors"
|
|
@@ -29,7 +29,7 @@ try {
|
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
## API
|
|
32
|
+
## API signature
|
|
33
33
|
|
|
34
34
|
### Properties
|
|
35
35
|
|
|
@@ -44,11 +44,11 @@ interface HookNotFoundError {
|
|
|
44
44
|
export default HookNotFoundError;`}
|
|
45
45
|
/>
|
|
46
46
|
|
|
47
|
-
### Static
|
|
47
|
+
### Static methods
|
|
48
48
|
|
|
49
49
|
#### `HookNotFoundError.is(value)`
|
|
50
50
|
|
|
51
|
-
Type-safe check for `HookNotFoundError` instances. Preferred over `instanceof` because it works across module boundaries and
|
|
51
|
+
Type-safe check for `HookNotFoundError` instances. Preferred over `instanceof` because it works across module boundaries and virtual machine contexts.
|
|
52
52
|
|
|
53
53
|
```typescript
|
|
54
54
|
import { HookNotFoundError } from "workflow/errors"
|
|
@@ -66,7 +66,7 @@ if (HookNotFoundError.is(error)) {
|
|
|
66
66
|
A common pattern for idempotent workflows is to try resuming a hook, and if it doesn't exist yet, start a new workflow run with the input data.
|
|
67
67
|
|
|
68
68
|
<Callout>
|
|
69
|
-
This "resume or start" pattern is not atomic
|
|
69
|
+
This "resume or start" pattern is not atomic: there is a small window where a race condition is possible. A better native approach is being worked on, but this pattern works well for many use cases.
|
|
70
70
|
</Callout>
|
|
71
71
|
|
|
72
72
|
```typescript lineNumbers
|
|
@@ -80,7 +80,7 @@ async function handleIncomingEvent(token: string, data: unknown) {
|
|
|
80
80
|
await resumeHook(token, data);
|
|
81
81
|
} catch (error) {
|
|
82
82
|
if (HookNotFoundError.is(error)) { // [!code highlight]
|
|
83
|
-
// Hook doesn't exist yet
|
|
83
|
+
// Hook doesn't exist yet, so start a new workflow run
|
|
84
84
|
await startWorkflow("processEvent", data); // [!code highlight]
|
|
85
85
|
} else {
|
|
86
86
|
throw error;
|