@workflow/core 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/dist/attribute-changes.d.ts +5 -0
- package/dist/attribute-changes.d.ts.map +1 -0
- package/dist/attribute-changes.js +25 -0
- package/dist/capabilities.d.ts +35 -2
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +74 -5
- package/dist/capture-stack.d.ts +1 -1
- package/dist/capture-stack.js +2 -2
- package/dist/class-serialization.d.ts +32 -0
- package/dist/class-serialization.d.ts.map +1 -1
- package/dist/class-serialization.js +37 -1
- package/dist/classify-error.d.ts +19 -3
- package/dist/classify-error.d.ts.map +1 -1
- package/dist/classify-error.js +98 -6
- package/dist/context-violation-error.d.ts +5 -5
- package/dist/context-violation-error.d.ts.map +1 -1
- package/dist/context-violation-error.js +8 -6
- package/dist/create-hook.d.ts +72 -7
- package/dist/create-hook.d.ts.map +1 -1
- package/dist/create-hook.js +1 -1
- package/dist/define-hook.js +2 -2
- package/dist/describe-error.d.ts +7 -7
- package/dist/describe-error.d.ts.map +1 -1
- package/dist/describe-error.js +97 -12
- package/dist/encryption.d.ts +38 -4
- package/dist/encryption.d.ts.map +1 -1
- package/dist/encryption.js +96 -14
- package/dist/events-consumer.d.ts +271 -0
- package/dist/events-consumer.d.ts.map +1 -1
- package/dist/events-consumer.js +646 -42
- package/dist/flushable-stream.d.ts +56 -1
- package/dist/flushable-stream.d.ts.map +1 -1
- package/dist/flushable-stream.js +123 -11
- package/dist/global.d.ts +36 -2
- package/dist/global.d.ts.map +1 -1
- package/dist/global.js +21 -6
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/log-format.d.ts +2 -2
- package/dist/log-format.js +7 -7
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +10 -6
- package/dist/private.d.ts +226 -9
- package/dist/private.d.ts.map +1 -1
- package/dist/private.js +626 -7
- package/dist/replay-payload-cache.d.ts +64 -0
- package/dist/replay-payload-cache.d.ts.map +1 -0
- package/dist/replay-payload-cache.js +158 -0
- package/dist/runtime/compute-instance.d.ts +13 -0
- package/dist/runtime/compute-instance.d.ts.map +1 -0
- package/dist/runtime/compute-instance.js +14 -0
- package/dist/runtime/constants.d.ts +315 -0
- package/dist/runtime/constants.d.ts.map +1 -1
- package/dist/runtime/constants.js +502 -16
- package/dist/runtime/count-step-started-events.d.ts +52 -0
- package/dist/runtime/count-step-started-events.d.ts.map +1 -0
- package/dist/runtime/count-step-started-events.js +72 -0
- package/dist/runtime/deployment-guard.d.ts +99 -0
- package/dist/runtime/deployment-guard.d.ts.map +1 -0
- package/dist/runtime/deployment-guard.js +154 -0
- package/dist/runtime/get-port-lazy.d.ts +15 -0
- package/dist/runtime/get-port-lazy.d.ts.map +1 -1
- package/dist/runtime/get-port-lazy.js +75 -6
- package/dist/runtime/get-world-lazy.d.ts +5 -14
- package/dist/runtime/get-world-lazy.d.ts.map +1 -1
- package/dist/runtime/get-world-lazy.js +19 -29
- package/dist/runtime/helpers.d.ts +341 -29
- package/dist/runtime/helpers.d.ts.map +1 -1
- package/dist/runtime/helpers.js +559 -57
- package/dist/runtime/quickjs-assets.generated.d.ts +14 -0
- package/dist/runtime/quickjs-assets.generated.d.ts.map +1 -0
- package/dist/runtime/quickjs-assets.generated.js +30 -0
- package/dist/runtime/quickjs-entrypoint.d.ts +136 -0
- package/dist/runtime/quickjs-entrypoint.d.ts.map +1 -0
- package/dist/runtime/quickjs-entrypoint.js +1686 -0
- package/dist/runtime/quickjs-runtime.d.ts +244 -0
- package/dist/runtime/quickjs-runtime.d.ts.map +1 -0
- package/dist/runtime/quickjs-runtime.js +2427 -0
- package/dist/runtime/quickjs-serde.d.ts +107 -0
- package/dist/runtime/quickjs-serde.d.ts.map +1 -0
- package/dist/runtime/quickjs-serde.js +2159 -0
- package/dist/runtime/replay-budget.d.ts +97 -0
- package/dist/runtime/replay-budget.d.ts.map +1 -0
- package/dist/runtime/replay-budget.js +139 -0
- package/dist/runtime/replay-recovery-reporter.d.ts +36 -0
- package/dist/runtime/replay-recovery-reporter.d.ts.map +1 -0
- package/dist/runtime/replay-recovery-reporter.js +64 -0
- package/dist/runtime/resume-hook.d.ts +91 -8
- package/dist/runtime/resume-hook.d.ts.map +1 -1
- package/dist/runtime/resume-hook.js +512 -89
- package/dist/runtime/resume-latency.d.ts +150 -0
- package/dist/runtime/resume-latency.d.ts.map +1 -0
- package/dist/runtime/resume-latency.js +155 -0
- package/dist/runtime/run-id-time.d.ts +19 -0
- package/dist/runtime/run-id-time.d.ts.map +1 -0
- package/dist/runtime/run-id-time.js +42 -0
- package/dist/runtime/run.d.ts +72 -7
- package/dist/runtime/run.d.ts.map +1 -1
- package/dist/runtime/run.js +280 -53
- package/dist/runtime/runs.d.ts +54 -3
- package/dist/runtime/runs.d.ts.map +1 -1
- package/dist/runtime/runs.js +126 -16
- package/dist/runtime/start.d.ts +101 -2
- package/dist/runtime/start.d.ts.map +1 -1
- package/dist/runtime/start.js +352 -61
- package/dist/runtime/step-executor.d.ts +205 -5
- package/dist/runtime/step-executor.d.ts.map +1 -1
- package/dist/runtime/step-executor.js +760 -127
- package/dist/runtime/step-latency.d.ts +220 -0
- package/dist/runtime/step-latency.d.ts.map +1 -0
- package/dist/runtime/step-latency.js +215 -0
- package/dist/runtime/step-ownership.d.ts +72 -0
- package/dist/runtime/step-ownership.d.ts.map +1 -0
- package/dist/runtime/step-ownership.js +114 -0
- package/dist/runtime/step-single-flight.d.ts +12 -0
- package/dist/runtime/step-single-flight.d.ts.map +1 -0
- package/dist/runtime/step-single-flight.js +75 -0
- package/dist/runtime/suspension-handler.d.ts +269 -7
- package/dist/runtime/suspension-handler.d.ts.map +1 -1
- package/dist/runtime/suspension-handler.js +1253 -125
- package/dist/runtime/unserializable-step.d.ts +34 -0
- package/dist/runtime/unserializable-step.d.ts.map +1 -0
- package/dist/runtime/unserializable-step.js +52 -0
- package/dist/runtime/vm-mode.d.ts +44 -0
- package/dist/runtime/vm-mode.d.ts.map +1 -0
- package/dist/runtime/vm-mode.js +62 -0
- package/dist/runtime/vm-serde-bundle.generated.d.ts +14 -0
- package/dist/runtime/vm-serde-bundle.generated.d.ts.map +1 -0
- package/dist/runtime/vm-serde-bundle.generated.js +16 -0
- package/dist/runtime/wait-continuation.d.ts +106 -0
- package/dist/runtime/wait-continuation.d.ts.map +1 -0
- package/dist/runtime/wait-continuation.js +136 -0
- package/dist/runtime/wait-until.d.ts +18 -0
- package/dist/runtime/wait-until.d.ts.map +1 -0
- package/dist/runtime/wait-until.js +42 -0
- package/dist/runtime/world-compatibility.d.ts +32 -0
- package/dist/runtime/world-compatibility.d.ts.map +1 -0
- package/dist/runtime/world-compatibility.js +44 -0
- package/dist/runtime/world-init.d.ts +17 -42
- package/dist/runtime/world-init.d.ts.map +1 -1
- package/dist/runtime/world-init.js +18 -43
- package/dist/runtime/world.d.ts +14 -2
- package/dist/runtime/world.d.ts.map +1 -1
- package/dist/runtime/world.js +75 -19
- package/dist/runtime.d.ts +10 -6
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +3741 -490
- package/dist/sealed-box.d.ts +167 -0
- package/dist/sealed-box.d.ts.map +1 -0
- package/dist/sealed-box.js +571 -0
- package/dist/serialization/client.d.ts +3 -3
- package/dist/serialization/client.d.ts.map +1 -1
- package/dist/serialization/client.js +8 -4
- package/dist/serialization/codec-devalue-vm.d.ts +16 -0
- package/dist/serialization/codec-devalue-vm.d.ts.map +1 -0
- package/dist/serialization/codec-devalue-vm.js +148 -0
- package/dist/serialization/codec-devalue.d.ts +1 -1
- package/dist/serialization/codec-devalue.d.ts.map +1 -1
- package/dist/serialization/codec-devalue.js +24 -6
- package/dist/serialization/codec.d.ts +37 -2
- package/dist/serialization/codec.d.ts.map +1 -1
- package/dist/serialization/codec.js +2 -2
- package/dist/serialization/compression.d.ts +105 -0
- package/dist/serialization/compression.d.ts.map +1 -0
- package/dist/serialization/compression.js +261 -0
- package/dist/serialization/encryption.d.ts +119 -17
- package/dist/serialization/encryption.d.ts.map +1 -1
- package/dist/serialization/encryption.js +152 -17
- package/dist/serialization/errors.d.ts +13 -0
- package/dist/serialization/errors.d.ts.map +1 -1
- package/dist/serialization/errors.js +20 -2
- package/dist/serialization/format.d.ts +6 -6
- package/dist/serialization/format.js +7 -7
- package/dist/serialization/hardened.d.ts +156 -0
- package/dist/serialization/hardened.d.ts.map +1 -0
- package/dist/serialization/hardened.js +538 -0
- package/dist/serialization/index.d.ts +10 -8
- package/dist/serialization/index.d.ts.map +1 -1
- package/dist/serialization/index.js +10 -8
- package/dist/serialization/reducers/class-vm.d.ts +20 -0
- package/dist/serialization/reducers/class-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/class-vm.js +77 -0
- package/dist/serialization/reducers/class.d.ts.map +1 -1
- package/dist/serialization/reducers/class.js +11 -6
- package/dist/serialization/reducers/common-vm.d.ts +15 -0
- package/dist/serialization/reducers/common-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/common-vm.js +616 -0
- package/dist/serialization/reducers/common.d.ts +1 -1
- package/dist/serialization/reducers/common.d.ts.map +1 -1
- package/dist/serialization/reducers/common.js +202 -62
- package/dist/serialization/reducers/step-function-vm.d.ts +44 -0
- package/dist/serialization/reducers/step-function-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/step-function-vm.js +97 -0
- package/dist/serialization/reducers/step-function.d.ts.map +1 -1
- package/dist/serialization/reducers/step-function.js +25 -9
- package/dist/serialization/step.d.ts +4 -4
- package/dist/serialization/step.d.ts.map +1 -1
- package/dist/serialization/step.js +9 -5
- package/dist/serialization/types.d.ts +80 -1
- package/dist/serialization/types.d.ts.map +1 -1
- package/dist/serialization/types.js +15 -1
- package/dist/serialization/workflow-vm.d.ts +29 -0
- package/dist/serialization/workflow-vm.d.ts.map +1 -0
- package/dist/serialization/workflow-vm.js +74 -0
- package/dist/serialization-format.d.ts +64 -8
- package/dist/serialization-format.d.ts.map +1 -1
- package/dist/serialization-format.js +225 -73
- package/dist/serialization.d.ts +262 -34
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js +1879 -274
- package/dist/set-attributes.d.ts +13 -0
- package/dist/set-attributes.d.ts.map +1 -0
- package/dist/set-attributes.js +60 -0
- package/dist/source-map.d.ts +25 -0
- package/dist/source-map.d.ts.map +1 -1
- package/dist/source-map.js +154 -10
- package/dist/step/context-storage.d.ts +61 -2
- package/dist/step/context-storage.d.ts.map +1 -1
- package/dist/step/context-storage.js +7 -5
- package/dist/step/writable-stream.d.ts +3 -3
- package/dist/step/writable-stream.d.ts.map +1 -1
- package/dist/step/writable-stream.js +72 -9
- package/dist/step.d.ts.map +1 -1
- package/dist/step.js +148 -27
- package/dist/symbols.d.ts +50 -0
- package/dist/symbols.d.ts.map +1 -1
- package/dist/symbols.js +51 -1
- package/dist/telemetry/semantic-conventions.d.ts +397 -2
- package/dist/telemetry/semantic-conventions.d.ts.map +1 -1
- package/dist/telemetry/semantic-conventions.js +297 -3
- package/dist/telemetry.d.ts +91 -0
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +244 -17
- package/dist/test-support/orchestrator-context.d.ts +25 -0
- package/dist/test-support/orchestrator-context.d.ts.map +1 -0
- package/dist/test-support/orchestrator-context.js +101 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +23 -1
- package/dist/util.d.ts +0 -5
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +1 -15
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +2 -2
- package/dist/vm/index.d.ts.map +1 -1
- package/dist/vm/index.js +84 -14
- package/dist/vm/script-cache.d.ts +26 -0
- package/dist/vm/script-cache.d.ts.map +1 -0
- package/dist/vm/script-cache.js +131 -0
- package/dist/vm/uint8array-base64.js +1 -1
- package/dist/workflow/abort-controller.d.ts +2 -2
- package/dist/workflow/abort-controller.d.ts.map +1 -1
- package/dist/workflow/abort-controller.js +88 -21
- package/dist/workflow/attribute-dispatcher.d.ts +6 -0
- package/dist/workflow/attribute-dispatcher.d.ts.map +1 -0
- package/dist/workflow/attribute-dispatcher.js +45 -0
- package/dist/workflow/create-hook.d.ts.map +1 -1
- package/dist/workflow/create-hook.js +24 -3
- package/dist/workflow/get-workflow-metadata.js +2 -2
- package/dist/workflow/hook.d.ts.map +1 -1
- package/dist/workflow/hook.js +304 -47
- package/dist/workflow/index.d.ts +1 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +2 -1
- package/dist/workflow/set-attributes.d.ts +68 -0
- package/dist/workflow/set-attributes.d.ts.map +1 -0
- package/dist/workflow/set-attributes.js +60 -0
- package/dist/workflow/sleep.d.ts.map +1 -1
- package/dist/workflow/sleep.js +58 -12
- package/dist/workflow/world-init-stub.d.ts +3 -3
- package/dist/workflow/world-init-stub.js +4 -4
- package/dist/workflow.d.ts +95 -3
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +895 -561
- package/docs/api-reference/create-hook.mdx +89 -10
- package/docs/api-reference/create-webhook.mdx +16 -15
- package/docs/api-reference/define-hook.mdx +35 -33
- package/docs/api-reference/fatal-error.mdx +30 -8
- package/docs/api-reference/fetch.mdx +14 -10
- package/docs/api-reference/get-step-metadata.mdx +2 -2
- package/docs/api-reference/get-workflow-metadata.mdx +3 -3
- package/docs/api-reference/get-writable.mdx +7 -7
- package/docs/api-reference/index.mdx +4 -1
- package/docs/api-reference/retryable-error.mdx +1 -1
- package/docs/api-reference/set-attributes.mdx +61 -0
- package/docs/api-reference/sleep.mdx +4 -4
- 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/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/package.json +24 -15
- package/dist/runtime/step-handler.d.ts +0 -2
- package/dist/runtime/step-handler.d.ts.map +0 -1
- package/dist/runtime/step-handler.js +0 -776
|
@@ -8,6 +8,7 @@ prerequisites:
|
|
|
8
8
|
related:
|
|
9
9
|
- /docs/api-reference/workflow/define-hook
|
|
10
10
|
- /docs/api-reference/workflow/create-webhook
|
|
11
|
+
- /docs/foundations/idempotency
|
|
11
12
|
---
|
|
12
13
|
|
|
13
14
|
Creates a low-level hook primitive that can be used to resume a workflow run with arbitrary payloads.
|
|
@@ -25,7 +26,7 @@ export async function hookWorkflow() {
|
|
|
25
26
|
}
|
|
26
27
|
```
|
|
27
28
|
|
|
28
|
-
## API
|
|
29
|
+
## API signature
|
|
29
30
|
|
|
30
31
|
### Parameters
|
|
31
32
|
|
|
@@ -65,9 +66,11 @@ export default Hook;`}
|
|
|
65
66
|
|
|
66
67
|
The returned `Hook` object also implements `AsyncIterable<T>`, which allows you to iterate over incoming payloads using `for await...of` syntax.
|
|
67
68
|
|
|
69
|
+
Use `hook.getConflict()` to check whether the hook token is already claimed by another hook, including one kept reserved after its run ends, without waiting for hook payload data. Calling `createHook()` on its own does not register the hook: registration is only committed when the workflow suspends. Awaiting `hook.getConflict()` suspends the workflow to commit the registration, then resolves with `null` once `hook_created` is recorded, or with the conflicting [`Run`](/docs/api-reference/workflow-api/get-run).
|
|
70
|
+
|
|
68
71
|
## Examples
|
|
69
72
|
|
|
70
|
-
### Basic
|
|
73
|
+
### Basic usage
|
|
71
74
|
|
|
72
75
|
When creating a hook, you can specify a payload type for automatic type safety:
|
|
73
76
|
|
|
@@ -88,7 +91,7 @@ export async function approvalWorkflow() {
|
|
|
88
91
|
}
|
|
89
92
|
```
|
|
90
93
|
|
|
91
|
-
### Customizing
|
|
94
|
+
### Customizing tokens
|
|
92
95
|
|
|
93
96
|
Tokens are used to identify a specific hook. You can customize the token to be more specific to a use case.
|
|
94
97
|
|
|
@@ -112,7 +115,82 @@ export async function slackBotWorkflow(channelId: string) {
|
|
|
112
115
|
}
|
|
113
116
|
```
|
|
114
117
|
|
|
115
|
-
###
|
|
118
|
+
### Detecting token conflicts
|
|
119
|
+
|
|
120
|
+
Use `hook.getConflict()` when the workflow needs to claim a hook token before doing other work, but does not need a payload yet:
|
|
121
|
+
|
|
122
|
+
```typescript lineNumbers
|
|
123
|
+
import { createHook } from "workflow";
|
|
124
|
+
|
|
125
|
+
declare function chargeOrder(orderId: string): Promise<void>; // @setup
|
|
126
|
+
|
|
127
|
+
async function processOrder(orderId: string) {
|
|
128
|
+
"use workflow";
|
|
129
|
+
|
|
130
|
+
using hook = createHook({ // [!code highlight]
|
|
131
|
+
token: `order:${orderId}` // [!code highlight]
|
|
132
|
+
}); // [!code highlight]
|
|
133
|
+
|
|
134
|
+
const conflict = await hook.getConflict(); // [!code highlight]
|
|
135
|
+
if (conflict) { // [!code highlight]
|
|
136
|
+
// Another active workflow run already owns this token.
|
|
137
|
+
return { dedupedTo: conflict.runId };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
await chargeOrder(orderId);
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Because `createHook()` alone does not suspend the workflow, awaiting `hook.getConflict()` is what actually suspends the run and commits the hook registration. It only waits for registration. To receive payload data from a future `resumeHook()` call, await the hook itself or iterate it with `for await...of`.
|
|
145
|
+
|
|
146
|
+
On a conflict, the resolved value is a `Run` handle for the run that owns the token, with durable step-backed accessors. The duplicate run can decide in code how to handle it: return or log `conflict.runId`, inspect `await conflict.status`, wait on `await conflict.returnValue`, or cancel the owner with `await conflict.cancel()` and continue in the current run. See [Run idempotency](/docs/foundations/idempotency#run-idempotency) for these strategies in context.
|
|
147
|
+
|
|
148
|
+
<Callout type="info">
|
|
149
|
+
Custom hook tokens are the recommended way to coordinate active workflow runs. Use a deterministic token from your domain, such as an order ID or conversation ID, create the hook near the beginning of the workflow, and check `await hook.getConflict()` before work that depends on owning the token. See [Run idempotency](/docs/foundations/idempotency#run-idempotency).
|
|
150
|
+
</Callout>
|
|
151
|
+
|
|
152
|
+
### Keep a token unavailable after the run ends
|
|
153
|
+
|
|
154
|
+
By default, another Hook can use the token after its workflow ends. Set `experimental_minRetention` to keep the token unavailable for at least a specific time after `createHook()` runs:
|
|
155
|
+
|
|
156
|
+
```typescript lineNumbers
|
|
157
|
+
import { createHook } from "workflow";
|
|
158
|
+
|
|
159
|
+
declare function processOwnedOrder(orderId: string): Promise<void>; // @setup
|
|
160
|
+
|
|
161
|
+
export async function processOrder(orderId: string) {
|
|
162
|
+
"use workflow";
|
|
163
|
+
|
|
164
|
+
const hook = createHook({ // [!code highlight]
|
|
165
|
+
token: `order:${orderId}`, // [!code highlight]
|
|
166
|
+
experimental_minRetention: "30d", // [!code highlight]
|
|
167
|
+
}); // [!code highlight]
|
|
168
|
+
|
|
169
|
+
const conflict = await hook.getConflict();
|
|
170
|
+
if (conflict) {
|
|
171
|
+
return { status: "duplicate" as const, runId: conflict.runId };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
await processOwnedOrder(orderId);
|
|
175
|
+
return { status: "processed" as const };
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`experimental_minRetention` accepts the same values as [`sleep()`](/docs/api-reference/workflow/sleep): a duration string such as `"30d"`, a number of milliseconds, or an absolute `Date`. Durations start when `createHook()` runs.
|
|
180
|
+
|
|
181
|
+
The Hook remains active until the workflow ends, even if the configured time passes first. Another Hook can use the token only after both the workflow has ended and the configured time has passed. For example, `"30d"` keeps the token unavailable for 29 more days if the workflow ends after 1 day. A workflow that runs for more than 30 days releases the token when it ends.
|
|
182
|
+
|
|
183
|
+
After the workflow ends, [`getHookByToken()`](/docs/api-reference/workflow-api/get-hook-by-token) can still find the Hook until retention ends, but the Hook cannot be resumed.
|
|
184
|
+
|
|
185
|
+
<Callout type="warn">
|
|
186
|
+
`using` auto-disposes the Hook at scope exit, which releases the token immediately and defeats `experimental_minRetention`. Declare retained Hooks with `const` and let the runtime clean them up when the run ends.
|
|
187
|
+
</Callout>
|
|
188
|
+
|
|
189
|
+
<Callout type="warn">
|
|
190
|
+
This option is experimental. Worlds can limit how long tokens are retained; see [World configuration](/docs/configuration/worlds) for each World's limit. If the configured World does not support minimum retention, the workflow fails when registering the Hook. `createWebhook()` does not accept this option.
|
|
191
|
+
</Callout>
|
|
192
|
+
|
|
193
|
+
### Waiting for multiple payloads
|
|
116
194
|
|
|
117
195
|
You can also wait for multiple payloads by using the `for await...of` syntax.
|
|
118
196
|
|
|
@@ -135,7 +213,7 @@ export async function collectHookWorkflow() {
|
|
|
135
213
|
}
|
|
136
214
|
```
|
|
137
215
|
|
|
138
|
-
### Disposing
|
|
216
|
+
### Disposing hooks early
|
|
139
217
|
|
|
140
218
|
You can dispose a hook early to release its token for reuse by another workflow. This is useful for handoff patterns where one workflow needs to transfer a hook token to another workflow while still running.
|
|
141
219
|
|
|
@@ -164,7 +242,7 @@ export async function handoffWorkflow(channelId: string) {
|
|
|
164
242
|
|
|
165
243
|
After calling `dispose()`, the hook will no longer receive events and its token becomes available for other workflows to use.
|
|
166
244
|
|
|
167
|
-
### Automatic
|
|
245
|
+
### Automatic disposal with `using`
|
|
168
246
|
|
|
169
247
|
Hooks implement the [TC39 Explicit Resource Management](https://github.com/tc39/proposal-explicit-resource-management) proposal, allowing automatic disposal with the `using` keyword:
|
|
170
248
|
|
|
@@ -190,8 +268,9 @@ export async function scopedHookWorkflow(channelId: string) {
|
|
|
190
268
|
|
|
191
269
|
This is equivalent to manually calling `dispose()` but ensures the hook is always cleaned up, even if an error occurs.
|
|
192
270
|
|
|
193
|
-
## Related
|
|
271
|
+
## Related functions
|
|
194
272
|
|
|
195
|
-
- [`defineHook()`](/docs/api-reference/workflow/define-hook)
|
|
196
|
-
- [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook)
|
|
197
|
-
- [`createWebhook()`](/docs/api-reference/workflow/create-webhook)
|
|
273
|
+
- [`defineHook()`](/docs/api-reference/workflow/define-hook): Type-safe hook helper
|
|
274
|
+
- [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook): Resume a hook with a payload
|
|
275
|
+
- [`createWebhook()`](/docs/api-reference/workflow/create-webhook): Higher-level HTTP webhook abstraction
|
|
276
|
+
- [Idempotency](/docs/foundations/idempotency): Deduplicate step side effects and workflow starts
|
|
@@ -14,7 +14,7 @@ Creates a webhook that can be used to suspend and resume a workflow run upon rec
|
|
|
14
14
|
Webhooks provide a way for external systems to send HTTP requests directly to your workflow. Unlike hooks which accept arbitrary payloads, webhooks work with standard HTTP `Request` objects and can return HTTP `Response` objects.
|
|
15
15
|
|
|
16
16
|
<Callout type="warn">
|
|
17
|
-
`createWebhook()` creates a public endpoint at `/.well-known/workflow/v1/webhook/:token`, and the token in that URL is the only authorization performed for incoming requests resuming that webhook. This is convenient for prototypes and
|
|
17
|
+
`createWebhook()` creates a public endpoint at `/.well-known/workflow/v1/webhook/:token`, and the token in that URL is the only authorization performed for incoming requests resuming that webhook. This is convenient for prototypes and basic resume links because it avoids creating another route, but if you need stronger security, prefer [`createHook()`](/docs/api-reference/workflow/create-hook) behind your own route and authorize the request before calling [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook) to avoid unauthenticated workflow resumptions.
|
|
18
18
|
</Callout>
|
|
19
19
|
|
|
20
20
|
```ts lineNumbers
|
|
@@ -31,7 +31,7 @@ export async function webhookWorkflow() {
|
|
|
31
31
|
}
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
## API
|
|
34
|
+
## API signature
|
|
35
35
|
|
|
36
36
|
### Parameters
|
|
37
37
|
|
|
@@ -55,6 +55,7 @@ The returned `Webhook` object has:
|
|
|
55
55
|
|
|
56
56
|
- `url`: The HTTP endpoint URL that external systems can call
|
|
57
57
|
- `token`: The unique token identifying this webhook
|
|
58
|
+
- `getConflict()`: A promise that resolves with the conflicting run if another active hook already owns this token, or `null` once the webhook endpoint has been registered
|
|
58
59
|
- Implements `AsyncIterable<T>` for handling multiple requests, where `T` is `Request` (default) or `RequestWithResponse` (manual mode)
|
|
59
60
|
|
|
60
61
|
When using `createWebhook({ respondWith: 'manual' })`, the resolved request type is `RequestWithResponse`, which extends the standard `Request` interface with a `respondWith(response: Response): Promise<void>` method for sending custom responses back to the caller.
|
|
@@ -62,22 +63,22 @@ When using `createWebhook({ respondWith: 'manual' })`, the resolved request type
|
|
|
62
63
|
<Callout type="info">
|
|
63
64
|
Use the simplest option that satisfies the prompt:
|
|
64
65
|
|
|
65
|
-
- `createWebhook()
|
|
66
|
-
- `createWebhook({ respondWith: 'manual' })
|
|
67
|
-
- `createHook()` + `resumeHook()
|
|
66
|
+
- `createWebhook()`: generated callback URL, and the default `202 Accepted` response is fine
|
|
67
|
+
- `createWebhook({ respondWith: 'manual' })`: generated callback URL, but you must send a custom body, status, or headers
|
|
68
|
+
- `createHook()` + `resumeHook()`: the app resumes from server-side code with a deterministic business token instead of a generated callback URL
|
|
68
69
|
</Callout>
|
|
69
70
|
|
|
70
71
|
<details>
|
|
71
72
|
<summary>Common wrong turns</summary>
|
|
72
73
|
|
|
73
|
-
-
|
|
74
|
+
- A callback URL alone does not require `respondWith: 'manual'`.
|
|
74
75
|
- Do not use `RequestWithResponse` unless you chose manual mode.
|
|
75
76
|
- Do not invent a custom callback route when `webhook.url` is the intended callback surface.
|
|
76
77
|
</details>
|
|
77
78
|
|
|
78
79
|
## Examples
|
|
79
80
|
|
|
80
|
-
### Basic
|
|
81
|
+
### Basic usage
|
|
81
82
|
|
|
82
83
|
Create a webhook that receives HTTP requests and logs the request details:
|
|
83
84
|
|
|
@@ -100,11 +101,11 @@ export async function basicWebhookWorkflow() {
|
|
|
100
101
|
}
|
|
101
102
|
```
|
|
102
103
|
|
|
103
|
-
### Responding to
|
|
104
|
+
### Responding to webhook requests (manual mode)
|
|
104
105
|
|
|
105
106
|
Use this section only when the caller requires a non-default HTTP response. If `202 Accepted` is acceptable, use `createWebhook()` without `respondWith: "manual"`.
|
|
106
107
|
|
|
107
|
-
Pass `{ respondWith: "manual" }` to get a `RequestWithResponse` object with a `respondWith()` method.
|
|
108
|
+
Pass `{ respondWith: "manual" }` to get a `RequestWithResponse` object with a `respondWith()` method. Call `respondWith()` from within a step function:
|
|
108
109
|
|
|
109
110
|
```typescript lineNumbers
|
|
110
111
|
import { createWebhook, type RequestWithResponse } from "workflow"
|
|
@@ -142,7 +143,7 @@ async function processData(data: any): Promise<void> {
|
|
|
142
143
|
}
|
|
143
144
|
```
|
|
144
145
|
|
|
145
|
-
### Waiting for
|
|
146
|
+
### Waiting for multiple requests
|
|
146
147
|
|
|
147
148
|
You can also wait for multiple requests by using the `for await...of` syntax.
|
|
148
149
|
|
|
@@ -181,9 +182,9 @@ export async function eventCollectorWorkflow() {
|
|
|
181
182
|
}
|
|
182
183
|
```
|
|
183
184
|
|
|
184
|
-
## Related
|
|
185
|
+
## Related functions
|
|
185
186
|
|
|
186
|
-
- [`createHook()`](/docs/api-reference/workflow/create-hook)
|
|
187
|
-
- [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook)
|
|
188
|
-
- [`defineHook()`](/docs/api-reference/workflow/define-hook)
|
|
189
|
-
- [`resumeWebhook()`](/docs/api-reference/workflow-api/resume-webhook)
|
|
187
|
+
- [`createHook()`](/docs/api-reference/workflow/create-hook): Use when the app resumes from server-side code with a deterministic business token.
|
|
188
|
+
- [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook): Pairs with `createHook()` for deterministic server-side resume.
|
|
189
|
+
- [`defineHook()`](/docs/api-reference/workflow/define-hook): Type-safe hook helper.
|
|
190
|
+
- [`resumeWebhook()`](/docs/api-reference/workflow-api/resume-webhook): Low-level runtime API. Most integrations should call `webhook.url` directly instead of adding a custom callback route.
|
|
@@ -33,7 +33,7 @@ export async function nameWorkflow() {
|
|
|
33
33
|
}
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
## API
|
|
36
|
+
## API signature
|
|
37
37
|
|
|
38
38
|
### Parameters
|
|
39
39
|
|
|
@@ -46,27 +46,24 @@ showSections={['parameters']}
|
|
|
46
46
|
|
|
47
47
|
### Returns
|
|
48
48
|
|
|
49
|
+
`defineHook()` returns a `TypedHook<TInput, TOutput>`:
|
|
50
|
+
|
|
49
51
|
<TSDoc
|
|
50
52
|
definition={`
|
|
51
|
-
interface
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
create: (options?: HookOptions) => Hook<T>;
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
-
* Resumes a hook by sending a payload with the defined type.
|
|
61
|
-
*/
|
|
62
|
-
resume: (token: string, payload: T) => Promise<HookEntity | null>;
|
|
53
|
+
interface TypedHook<TInput, TOutput> {
|
|
54
|
+
/** Creates the hook. Call inside a "use workflow" function. */
|
|
55
|
+
create(options?: HookOptions): Hook<TOutput>;
|
|
56
|
+
/** Resumes the hook from runtime code. Resolves to the resumed hook; throws HookNotFoundError if the token does not match an active hook. */
|
|
57
|
+
resume(token: string, payload: TInput): Promise<HookEntity>;
|
|
63
58
|
}
|
|
64
|
-
export default
|
|
59
|
+
export default TypedHook;`}
|
|
65
60
|
/>
|
|
66
61
|
|
|
62
|
+
`create()` is called inside a `"use workflow"` function to create the hook; `resume()` is called from runtime code (an API route or server action). When a `schema` is provided, `resume()` accepts the raw input type (`TInput`) and the workflow receives the validated and possibly transformed output type (`TOutput`); without a schema, `TOutput` defaults to `TInput`. `resume()` throws [`HookNotFoundError`](/docs/api-reference/workflow-errors/hook-not-found-error) if the token does not match an active hook. It does not return `null`.
|
|
63
|
+
|
|
67
64
|
## Examples
|
|
68
65
|
|
|
69
|
-
### Basic
|
|
66
|
+
### Basic type-safe hook definition
|
|
70
67
|
|
|
71
68
|
By defining the hook once with a specific payload type, you can reuse it in multiple workflows and API routes with automatic type safety.
|
|
72
69
|
|
|
@@ -91,30 +88,35 @@ export async function workflowWithApproval() {
|
|
|
91
88
|
}
|
|
92
89
|
```
|
|
93
90
|
|
|
94
|
-
### Resuming with
|
|
91
|
+
### Resuming with type safety
|
|
95
92
|
|
|
96
|
-
Hooks can be resumed using the same defined hook and a token. By using the same hook, you can ensure that the payload matches the defined type when resuming a hook.
|
|
93
|
+
Hooks can be resumed using the same defined hook and a token. By using the same hook, you can ensure that the payload matches the defined type when resuming a hook. `resume()` resolves to the resumed hook and throws [`HookNotFoundError`](/docs/api-reference/workflow-errors/hook-not-found-error) if the token does not match an active hook.
|
|
97
94
|
|
|
98
95
|
```typescript lineNumbers
|
|
96
|
+
import { HookNotFoundError } from "workflow/errors";
|
|
97
|
+
|
|
99
98
|
// Use the same defined hook to resume
|
|
100
99
|
export async function POST(request: Request) {
|
|
101
100
|
const { token, approved, comment } = await request.json();
|
|
102
101
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return Response.json({
|
|
102
|
+
try {
|
|
103
|
+
// Type-safe resumption - TypeScript ensures the payload matches
|
|
104
|
+
const hook = await approvalHook.resume(token, { // [!code highlight]
|
|
105
|
+
approved, // [!code highlight]
|
|
106
|
+
comment, // [!code highlight]
|
|
107
|
+
}); // [!code highlight]
|
|
108
|
+
|
|
109
|
+
return Response.json({ success: true, runId: hook.runId });
|
|
110
|
+
} catch (error) {
|
|
111
|
+
if (HookNotFoundError.is(error)) { // [!code highlight]
|
|
112
|
+
return Response.json({ error: "Hook not found" }, { status: 404 });
|
|
113
|
+
}
|
|
114
|
+
throw error;
|
|
111
115
|
}
|
|
112
|
-
|
|
113
|
-
return Response.json({ success: true, runId: result.runId });
|
|
114
116
|
}
|
|
115
117
|
```
|
|
116
118
|
|
|
117
|
-
### Validate and
|
|
119
|
+
### Validate and transform with schema
|
|
118
120
|
|
|
119
121
|
You can provide runtime validation and transformation of hook payloads using the `schema` option. This option accepts any validator that conforms to the [Standard Schema v1](https://standardschema.dev) specification.
|
|
120
122
|
|
|
@@ -172,7 +174,7 @@ export async function POST(request: Request) {
|
|
|
172
174
|
}
|
|
173
175
|
```
|
|
174
176
|
|
|
175
|
-
#### Using
|
|
177
|
+
#### Using other Standard Schema libraries
|
|
176
178
|
|
|
177
179
|
The same pattern works with any Standard Schema v1 compliant library. Here's an example with [Valibot](https://valibot.dev):
|
|
178
180
|
|
|
@@ -188,7 +190,7 @@ export const approvalHook = defineHook({
|
|
|
188
190
|
});
|
|
189
191
|
```
|
|
190
192
|
|
|
191
|
-
### Customizing
|
|
193
|
+
### Customizing tokens
|
|
192
194
|
|
|
193
195
|
Tokens are used to identify a specific hook and for resuming a hook. You can customize the token to be more specific to a use case.
|
|
194
196
|
|
|
@@ -209,7 +211,7 @@ export async function slackBotWorkflow(channelId: string) {
|
|
|
209
211
|
}
|
|
210
212
|
```
|
|
211
213
|
|
|
212
|
-
## Related
|
|
214
|
+
## Related functions
|
|
213
215
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
+
- [`createHook()`](/docs/api-reference/workflow/create-hook): Create a hook in a workflow.
|
|
217
|
+
- [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook): Resume a hook with a payload.
|
|
@@ -27,18 +27,40 @@ async function fallibleStep() {
|
|
|
27
27
|
}
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
## API
|
|
30
|
+
## API signature
|
|
31
31
|
|
|
32
|
-
###
|
|
32
|
+
### Constructor
|
|
33
|
+
|
|
34
|
+
`new FatalError(message)`
|
|
35
|
+
|
|
36
|
+
| Parameter | Type | Description |
|
|
37
|
+
| --------- | -------- | ------------------ |
|
|
38
|
+
| `message` | `string` | The error message. |
|
|
39
|
+
|
|
40
|
+
### Properties
|
|
33
41
|
|
|
34
42
|
<TSDoc
|
|
35
43
|
definition={`
|
|
36
|
-
interface
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
* The error message.
|
|
40
|
-
*/
|
|
44
|
+
interface FatalError {
|
|
45
|
+
/** The error message. */
|
|
41
46
|
message: string;
|
|
47
|
+
/** Always true. Marks the error as non-retryable. */
|
|
48
|
+
fatal: boolean;
|
|
42
49
|
}
|
|
43
|
-
export default
|
|
50
|
+
export default FatalError;`}
|
|
44
51
|
/>
|
|
52
|
+
|
|
53
|
+
### Static methods
|
|
54
|
+
|
|
55
|
+
#### `FatalError.is(value)`
|
|
56
|
+
|
|
57
|
+
Type-safe check for `FatalError` instances. Preferred over `instanceof` because it works across module boundaries and VM contexts.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { FatalError } from "workflow"
|
|
61
|
+
declare const error: unknown; // @setup
|
|
62
|
+
|
|
63
|
+
if (FatalError.is(error)) {
|
|
64
|
+
// error is typed as FatalError
|
|
65
|
+
}
|
|
66
|
+
```
|
|
@@ -7,12 +7,17 @@ prerequisites:
|
|
|
7
7
|
- /docs/foundations/workflows-and-steps
|
|
8
8
|
related:
|
|
9
9
|
- /docs/errors/fetch-in-workflow
|
|
10
|
+
- /docs/foundations/idempotency
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
Makes HTTP requests from within a workflow. This is a special step function that wraps the standard `fetch` API, automatically handling serialization and providing retry semantics.
|
|
13
14
|
|
|
14
15
|
This is useful when you need to call external APIs or services from within your workflow.
|
|
15
16
|
|
|
17
|
+
<Callout type="warn">
|
|
18
|
+
Because workflow `fetch()` has retry semantics, use idempotency keys when the request mutates an external system, such as creating a charge, sending an email, or enqueueing work. See [Idempotency](/docs/foundations/idempotency).
|
|
19
|
+
</Callout>
|
|
20
|
+
|
|
16
21
|
<Callout>
|
|
17
22
|
`fetch` is a *special* type of step function provided and should be called directly inside workflow functions.
|
|
18
23
|
</Callout>
|
|
@@ -29,7 +34,7 @@ async function apiWorkflow() {
|
|
|
29
34
|
}
|
|
30
35
|
```
|
|
31
36
|
|
|
32
|
-
## API
|
|
37
|
+
## API signature
|
|
33
38
|
|
|
34
39
|
### Parameters
|
|
35
40
|
|
|
@@ -55,9 +60,9 @@ showSections={['returns']}
|
|
|
55
60
|
|
|
56
61
|
## Examples
|
|
57
62
|
|
|
58
|
-
### Basic
|
|
63
|
+
### Basic usage
|
|
59
64
|
|
|
60
|
-
Here's
|
|
65
|
+
Here's an example of how you can use `fetch` inside your workflow.
|
|
61
66
|
|
|
62
67
|
```typescript lineNumbers
|
|
63
68
|
import { fetch } from "workflow"
|
|
@@ -82,13 +87,13 @@ async function apiWorkflow() {
|
|
|
82
87
|
}
|
|
83
88
|
```
|
|
84
89
|
|
|
85
|
-
We call `fetch()` with a URL and optional request options,
|
|
90
|
+
We call `fetch()` with a URL and optional request options, like the standard fetch API. The workflow runtime automatically handles the response serialization.
|
|
86
91
|
|
|
87
|
-
This API
|
|
92
|
+
This API lets you use `fetch` in a workflow. You can also implement a custom fetch for different error handling and retry logic.
|
|
88
93
|
|
|
89
|
-
### Customizing
|
|
94
|
+
### Customizing fetch behavior
|
|
90
95
|
|
|
91
|
-
|
|
96
|
+
The following custom fetch wrapper provides more advanced error handling with custom retry logic. Call `globalThis.fetch` inside your own `"use step"` function because calling the workflow `fetch` imported from `workflow` would nest a step inside a step:
|
|
92
97
|
|
|
93
98
|
```typescript lineNumbers
|
|
94
99
|
import { FatalError, RetryableError } from "workflow"
|
|
@@ -99,7 +104,7 @@ export async function customFetch(
|
|
|
99
104
|
) {
|
|
100
105
|
"use step"
|
|
101
106
|
|
|
102
|
-
const response = await fetch(url, init)
|
|
107
|
+
const response = await globalThis.fetch(url, init)
|
|
103
108
|
|
|
104
109
|
// Handle client errors (4xx) - don't retry
|
|
105
110
|
if (response.status >= 400 && response.status < 500) {
|
|
@@ -140,7 +145,6 @@ export async function customFetch(
|
|
|
140
145
|
|
|
141
146
|
This example demonstrates:
|
|
142
147
|
|
|
143
|
-
- Setting custom `maxRetries` to 5 retries (6 total attempts including the initial attempt).
|
|
144
148
|
- Throwing [`FatalError`](/docs/api-reference/workflow/fatal-error) for client errors (400-499) to prevent retries.
|
|
145
149
|
- Handling 429 rate limiting by reading the `Retry-After` header and using [`RetryableError`](/docs/api-reference/workflow/retryable-error).
|
|
146
|
-
- Allowing automatic retries for server errors (5xx)
|
|
150
|
+
- Allowing automatic retries for server errors (5xx) by throwing a plain `Error`.
|
|
@@ -34,7 +34,7 @@ async function logStepId() {
|
|
|
34
34
|
}
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
### Example:
|
|
37
|
+
### Example: use `stepId` as an idempotency key
|
|
38
38
|
|
|
39
39
|
```typescript lineNumbers
|
|
40
40
|
import { getStepMetadata } from "workflow";
|
|
@@ -61,7 +61,7 @@ async function chargeUser(userId: string, amount: number) {
|
|
|
61
61
|
<a href="/docs/foundations/idempotency">Idempotency</a> guide.
|
|
62
62
|
</Callout>
|
|
63
63
|
|
|
64
|
-
## API
|
|
64
|
+
## API signature
|
|
65
65
|
|
|
66
66
|
### Parameters
|
|
67
67
|
|
|
@@ -30,7 +30,7 @@ async function testWorkflow() {
|
|
|
30
30
|
}
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
### Detecting
|
|
33
|
+
### Detecting workflow runtime
|
|
34
34
|
|
|
35
35
|
You can use `getWorkflowMetadata` to detect whether your code is running inside a workflow context. This is useful when building shared utilities that need to behave differently inside and outside of workflows.
|
|
36
36
|
|
|
@@ -64,7 +64,7 @@ function log(message: string) {
|
|
|
64
64
|
}
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
### Detecting
|
|
67
|
+
### Detecting encryption
|
|
68
68
|
|
|
69
69
|
The `features` object indicates which capabilities are active for the current run. Library authors can use `features.encryption` to control whether sensitive data is included in step return values, which are serialized to the event log:
|
|
70
70
|
|
|
@@ -90,7 +90,7 @@ async function fetchUserProfile(userId: string) {
|
|
|
90
90
|
}
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
## API
|
|
93
|
+
## API signature
|
|
94
94
|
|
|
95
95
|
### Parameters
|
|
96
96
|
|
|
@@ -47,7 +47,7 @@ async function writeToStream(writable: WritableStream) {
|
|
|
47
47
|
}
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
## API
|
|
50
|
+
## API signature
|
|
51
51
|
|
|
52
52
|
### Parameters
|
|
53
53
|
|
|
@@ -69,7 +69,7 @@ export default getWritable;`}
|
|
|
69
69
|
|
|
70
70
|
Returns a `WritableStream<W>` where `W` is the type of data you plan to write to the stream.
|
|
71
71
|
|
|
72
|
-
## Good to
|
|
72
|
+
## Good to know
|
|
73
73
|
|
|
74
74
|
- **Workflow functions can only obtain the stream** - Call `getWritable()` in a workflow to get the stream reference, but you cannot call methods like `getWriter()`, `write()`, or `close()` directly in the workflow context.
|
|
75
75
|
- **Step functions can interact with streams** - Steps can receive the stream as an argument or call `getWritable()` directly, and they can freely interact with it (write, close, etc.).
|
|
@@ -81,9 +81,9 @@ Returns a `WritableStream<W>` where `W` is the type of data you plan to write to
|
|
|
81
81
|
|
|
82
82
|
## Examples
|
|
83
83
|
|
|
84
|
-
### Basic
|
|
84
|
+
### Basic text streaming
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
This example streams text data:
|
|
87
87
|
|
|
88
88
|
```typescript lineNumbers
|
|
89
89
|
import { sleep, getWritable } from "workflow";
|
|
@@ -118,7 +118,7 @@ async function stepCloseOutputStream(writable: WritableStream) {
|
|
|
118
118
|
}
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
### Calling `getWritable()`
|
|
121
|
+
### Calling `getWritable()` inside steps
|
|
122
122
|
|
|
123
123
|
You can also call `getWritable()` directly inside step functions without passing it as a parameter:
|
|
124
124
|
|
|
@@ -157,7 +157,7 @@ async function stepCloseOutputStreamInside() {
|
|
|
157
157
|
}
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
-
### Using
|
|
160
|
+
### Using namespaced streams in steps
|
|
161
161
|
|
|
162
162
|
You can also use namespaced streams when calling `getWritable()` from steps:
|
|
163
163
|
|
|
@@ -201,7 +201,7 @@ async function closeStreams() {
|
|
|
201
201
|
}
|
|
202
202
|
```
|
|
203
203
|
|
|
204
|
-
### Advanced
|
|
204
|
+
### Advanced chat streaming
|
|
205
205
|
|
|
206
206
|
Here's a more complex example showing how you might stream AI chat responses:
|
|
207
207
|
|
|
@@ -47,9 +47,12 @@ Workflow SDK contains the following functions you can use inside your workflow f
|
|
|
47
47
|
<Card href="/docs/api-reference/workflow/get-writable" title="getWritable()">
|
|
48
48
|
Access the current workflow run's default stream.
|
|
49
49
|
</Card>
|
|
50
|
+
<Card href="/docs/api-reference/workflow/set-attributes" title="setAttributes()">
|
|
51
|
+
Attach string metadata to the current workflow run.
|
|
52
|
+
</Card>
|
|
50
53
|
</Cards>
|
|
51
54
|
|
|
52
|
-
## Error
|
|
55
|
+
## Error classes
|
|
53
56
|
|
|
54
57
|
Workflow SDK includes error classes that can be thrown in a workflow or step to change the error exit strategy of a workflow.
|
|
55
58
|
|
|
@@ -31,7 +31,7 @@ async function retryStep() {
|
|
|
31
31
|
The difference between `Error` and `RetryableError` may not be entirely obvious, since when both are thrown, they both retry. The difference is that `RetryableError` has an additional configurable `retryAfter` parameter.
|
|
32
32
|
</Callout>
|
|
33
33
|
|
|
34
|
-
## API
|
|
34
|
+
## API signature
|
|
35
35
|
|
|
36
36
|
### Parameters
|
|
37
37
|
|