@workflow/core 4.0.1-beta.9 → 4.1.0-beta.51

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.
Files changed (181) hide show
  1. package/dist/builtins.js +1 -1
  2. package/dist/class-serialization.d.ts +26 -0
  3. package/dist/class-serialization.d.ts.map +1 -0
  4. package/dist/class-serialization.js +66 -0
  5. package/dist/create-hook.js +1 -1
  6. package/dist/define-hook.d.ts +40 -25
  7. package/dist/define-hook.d.ts.map +1 -1
  8. package/dist/define-hook.js +22 -27
  9. package/dist/events-consumer.d.ts.map +1 -1
  10. package/dist/events-consumer.js +5 -1
  11. package/dist/flushable-stream.d.ts +82 -0
  12. package/dist/flushable-stream.d.ts.map +1 -0
  13. package/dist/flushable-stream.js +214 -0
  14. package/dist/global.d.ts +4 -1
  15. package/dist/global.d.ts.map +1 -1
  16. package/dist/global.js +21 -9
  17. package/dist/index.d.ts +2 -2
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +2 -2
  20. package/dist/logger.js +1 -1
  21. package/dist/observability.d.ts +60 -0
  22. package/dist/observability.d.ts.map +1 -1
  23. package/dist/observability.js +265 -32
  24. package/dist/private.d.ts +10 -1
  25. package/dist/private.d.ts.map +1 -1
  26. package/dist/private.js +6 -1
  27. package/dist/runtime/helpers.d.ts +52 -0
  28. package/dist/runtime/helpers.d.ts.map +1 -0
  29. package/dist/runtime/helpers.js +264 -0
  30. package/dist/runtime/resume-hook.d.ts +17 -12
  31. package/dist/runtime/resume-hook.d.ts.map +1 -1
  32. package/dist/runtime/resume-hook.js +79 -64
  33. package/dist/runtime/start.d.ts +14 -0
  34. package/dist/runtime/start.d.ts.map +1 -1
  35. package/dist/runtime/start.js +71 -45
  36. package/dist/runtime/step-handler.d.ts +7 -0
  37. package/dist/runtime/step-handler.d.ts.map +1 -0
  38. package/dist/runtime/step-handler.js +337 -0
  39. package/dist/runtime/suspension-handler.d.ts +25 -0
  40. package/dist/runtime/suspension-handler.d.ts.map +1 -0
  41. package/dist/runtime/suspension-handler.js +182 -0
  42. package/dist/runtime/world.d.ts.map +1 -1
  43. package/dist/runtime/world.js +20 -21
  44. package/dist/runtime.d.ts +3 -7
  45. package/dist/runtime.d.ts.map +1 -1
  46. package/dist/runtime.js +103 -411
  47. package/dist/schemas.d.ts +1 -15
  48. package/dist/schemas.d.ts.map +1 -1
  49. package/dist/schemas.js +2 -15
  50. package/dist/serialization.d.ts +112 -21
  51. package/dist/serialization.d.ts.map +1 -1
  52. package/dist/serialization.js +469 -85
  53. package/dist/sleep.d.ts +10 -0
  54. package/dist/sleep.d.ts.map +1 -1
  55. package/dist/sleep.js +1 -1
  56. package/dist/source-map.d.ts +10 -0
  57. package/dist/source-map.d.ts.map +1 -0
  58. package/dist/source-map.js +56 -0
  59. package/dist/step/context-storage.d.ts +2 -1
  60. package/dist/step/context-storage.d.ts.map +1 -1
  61. package/dist/step/context-storage.js +1 -1
  62. package/dist/step/get-closure-vars.d.ts +9 -0
  63. package/dist/step/get-closure-vars.d.ts.map +1 -0
  64. package/dist/step/get-closure-vars.js +16 -0
  65. package/dist/step/get-step-metadata.js +1 -1
  66. package/dist/step/get-workflow-metadata.js +1 -1
  67. package/dist/step/writable-stream.d.ts +10 -2
  68. package/dist/step/writable-stream.d.ts.map +1 -1
  69. package/dist/step/writable-stream.js +6 -5
  70. package/dist/step.d.ts +1 -1
  71. package/dist/step.d.ts.map +1 -1
  72. package/dist/step.js +93 -47
  73. package/dist/symbols.d.ts +6 -0
  74. package/dist/symbols.d.ts.map +1 -1
  75. package/dist/symbols.js +7 -1
  76. package/dist/telemetry/semantic-conventions.d.ts +66 -38
  77. package/dist/telemetry/semantic-conventions.d.ts.map +1 -1
  78. package/dist/telemetry/semantic-conventions.js +16 -3
  79. package/dist/telemetry.d.ts +8 -4
  80. package/dist/telemetry.d.ts.map +1 -1
  81. package/dist/telemetry.js +39 -6
  82. package/dist/types.js +1 -1
  83. package/dist/util.d.ts +5 -24
  84. package/dist/util.d.ts.map +1 -1
  85. package/dist/util.js +19 -38
  86. package/dist/version.d.ts +2 -0
  87. package/dist/version.d.ts.map +1 -0
  88. package/dist/version.js +3 -0
  89. package/dist/vm/index.js +2 -2
  90. package/dist/vm/uuid.js +1 -1
  91. package/dist/workflow/create-hook.js +1 -1
  92. package/dist/workflow/define-hook.d.ts +3 -3
  93. package/dist/workflow/define-hook.d.ts.map +1 -1
  94. package/dist/workflow/define-hook.js +1 -1
  95. package/dist/workflow/get-workflow-metadata.js +1 -1
  96. package/dist/workflow/hook.d.ts.map +1 -1
  97. package/dist/workflow/hook.js +49 -14
  98. package/dist/workflow/index.d.ts +1 -1
  99. package/dist/workflow/index.d.ts.map +1 -1
  100. package/dist/workflow/index.js +2 -2
  101. package/dist/workflow/sleep.d.ts +1 -1
  102. package/dist/workflow/sleep.d.ts.map +1 -1
  103. package/dist/workflow/sleep.js +26 -39
  104. package/dist/workflow/writable-stream.d.ts +1 -1
  105. package/dist/workflow/writable-stream.d.ts.map +1 -1
  106. package/dist/workflow/writable-stream.js +1 -1
  107. package/dist/workflow.d.ts +1 -1
  108. package/dist/workflow.d.ts.map +1 -1
  109. package/dist/workflow.js +72 -9
  110. package/docs/api-reference/create-hook.mdx +133 -0
  111. package/docs/api-reference/create-webhook.mdx +225 -0
  112. package/docs/api-reference/define-hook.mdx +206 -0
  113. package/docs/api-reference/fatal-error.mdx +37 -0
  114. package/docs/api-reference/fetch.mdx +139 -0
  115. package/docs/api-reference/get-step-metadata.mdx +76 -0
  116. package/docs/api-reference/get-workflow-metadata.mdx +44 -0
  117. package/docs/api-reference/get-writable.mdx +292 -0
  118. package/docs/api-reference/index.mdx +55 -0
  119. package/docs/api-reference/meta.json +3 -0
  120. package/docs/api-reference/retryable-error.mdx +106 -0
  121. package/docs/api-reference/sleep.mdx +59 -0
  122. package/docs/foundations/common-patterns.mdx +253 -0
  123. package/docs/foundations/errors-and-retries.mdx +190 -0
  124. package/docs/foundations/hooks.mdx +455 -0
  125. package/docs/foundations/idempotency.mdx +55 -0
  126. package/docs/foundations/index.mdx +32 -0
  127. package/docs/foundations/meta.json +14 -0
  128. package/docs/foundations/serialization.mdx +157 -0
  129. package/docs/foundations/starting-workflows.mdx +211 -0
  130. package/docs/foundations/streaming.mdx +569 -0
  131. package/docs/foundations/workflows-and-steps.mdx +197 -0
  132. package/docs/how-it-works/code-transform.mdx +334 -0
  133. package/docs/how-it-works/event-sourcing.mdx +254 -0
  134. package/docs/how-it-works/framework-integrations.mdx +437 -0
  135. package/docs/how-it-works/meta.json +10 -0
  136. package/docs/how-it-works/understanding-directives.mdx +611 -0
  137. package/package.json +31 -25
  138. package/dist/builtins.js.map +0 -1
  139. package/dist/create-hook.js.map +0 -1
  140. package/dist/define-hook.js.map +0 -1
  141. package/dist/events-consumer.js.map +0 -1
  142. package/dist/global.js.map +0 -1
  143. package/dist/index.js.map +0 -1
  144. package/dist/logger.js.map +0 -1
  145. package/dist/observability.js.map +0 -1
  146. package/dist/parse-name.d.ts +0 -25
  147. package/dist/parse-name.d.ts.map +0 -1
  148. package/dist/parse-name.js +0 -40
  149. package/dist/parse-name.js.map +0 -1
  150. package/dist/private.js.map +0 -1
  151. package/dist/runtime/resume-hook.js.map +0 -1
  152. package/dist/runtime/start.js.map +0 -1
  153. package/dist/runtime/world.js.map +0 -1
  154. package/dist/runtime.js.map +0 -1
  155. package/dist/schemas.js.map +0 -1
  156. package/dist/serialization.js.map +0 -1
  157. package/dist/sleep.js.map +0 -1
  158. package/dist/step/context-storage.js.map +0 -1
  159. package/dist/step/get-step-metadata.js.map +0 -1
  160. package/dist/step/get-workflow-metadata.js.map +0 -1
  161. package/dist/step/writable-stream.js.map +0 -1
  162. package/dist/step.js.map +0 -1
  163. package/dist/symbols.js.map +0 -1
  164. package/dist/telemetry/semantic-conventions.js.map +0 -1
  165. package/dist/telemetry.js.map +0 -1
  166. package/dist/types.js.map +0 -1
  167. package/dist/util.js.map +0 -1
  168. package/dist/vm/index.js.map +0 -1
  169. package/dist/vm/uuid.js.map +0 -1
  170. package/dist/workflow/create-hook.js.map +0 -1
  171. package/dist/workflow/define-hook.js.map +0 -1
  172. package/dist/workflow/get-workflow-metadata.js.map +0 -1
  173. package/dist/workflow/hook.js.map +0 -1
  174. package/dist/workflow/index.js.map +0 -1
  175. package/dist/workflow/sleep.js.map +0 -1
  176. package/dist/workflow/writable-stream.js.map +0 -1
  177. package/dist/workflow.js.map +0 -1
  178. package/dist/writable-stream.d.ts +0 -23
  179. package/dist/writable-stream.d.ts.map +0 -1
  180. package/dist/writable-stream.js +0 -17
  181. package/dist/writable-stream.js.map +0 -1
@@ -0,0 +1,225 @@
1
+ ---
2
+ title: createWebhook
3
+ ---
4
+
5
+ Creates a webhook that can be used to suspend and resume a workflow run upon receiving an HTTP request.
6
+
7
+ 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.
8
+
9
+ ```ts lineNumbers
10
+ import { createWebhook } from "workflow"
11
+
12
+ export async function webhookWorkflow() {
13
+ "use workflow";
14
+ const webhook = createWebhook(); // [!code highlight]
15
+ console.log("Webhook URL:", webhook.url);
16
+
17
+ const request = await webhook; // Suspends until HTTP request received
18
+ console.log("Received request:", request.method, request.url);
19
+ }
20
+ ```
21
+
22
+ ## API Signature
23
+
24
+ ### Parameters
25
+
26
+ <TSDoc
27
+ definition={`
28
+ import { createWebhook } from "workflow";
29
+ export default createWebhook;`}
30
+ showSections={['parameters']}
31
+ />
32
+
33
+ ### Returns
34
+
35
+ <TSDoc
36
+ definition={`
37
+ import { createWebhook } from "workflow";
38
+ export default createWebhook;`}
39
+ showSections={['returns']}
40
+ />
41
+
42
+ The returned `Webhook` object has:
43
+
44
+ - `url`: The HTTP endpoint URL that external systems can call
45
+ - `token`: The unique token identifying this webhook
46
+ - Implements `AsyncIterable<RequestWithResponse>` for handling multiple requests
47
+
48
+ The `RequestWithResponse` type extends the standard `Request` interface with a `respondWith(response: Response)` method for sending custom responses back to the caller.
49
+
50
+ ## Examples
51
+
52
+ ### Basic Usage
53
+
54
+ Create a webhook that receives HTTP requests and logs the request details:
55
+
56
+ ```typescript lineNumbers
57
+ import { createWebhook } from "workflow"
58
+
59
+ export async function basicWebhookWorkflow() {
60
+ "use workflow";
61
+
62
+ const webhook = createWebhook(); // [!code highlight]
63
+ console.log("Send requests to:", webhook.url);
64
+
65
+ const request = await webhook;
66
+
67
+ console.log("Method:", request.method);
68
+ console.log("Headers:", Object.fromEntries(request.headers));
69
+
70
+ const body = await request.text();
71
+ console.log("Body:", body);
72
+ }
73
+ ```
74
+
75
+ ### Responding to Webhook Requests
76
+
77
+ Use the `respondWith()` method to send custom HTTP responses. Note that `respondWith()` must be called from within a step function:
78
+
79
+ ```typescript lineNumbers
80
+ import { createWebhook, type RequestWithResponse } from "workflow"
81
+
82
+ async function sendResponse(request: RequestWithResponse) { // [!code highlight]
83
+ "use step"; // [!code highlight]
84
+ await request.respondWith( // [!code highlight]
85
+ new Response(JSON.stringify({ success: true, message: "Received!" }), { // [!code highlight]
86
+ status: 200, // [!code highlight]
87
+ headers: { "Content-Type": "application/json" } // [!code highlight]
88
+ }) // [!code highlight]
89
+ ); // [!code highlight]
90
+ } // [!code highlight]
91
+
92
+ export async function respondingWebhookWorkflow() {
93
+ "use workflow";
94
+
95
+ const webhook = createWebhook();
96
+ console.log("Webhook URL:", webhook.url);
97
+
98
+ const request = await webhook;
99
+
100
+ // Send a custom response back to the caller
101
+ await sendResponse(request);
102
+
103
+ // Continue workflow processing
104
+ const data = await request.json();
105
+ await processData(data);
106
+ }
107
+
108
+ async function processData(data: any) {
109
+ "use step";
110
+ // Process the webhook data
111
+ console.log("Processing:", data);
112
+ }
113
+ ```
114
+
115
+ ### Customizing Tokens
116
+
117
+ Tokens are used to identify a specific webhook. You can customize the token to be more specific to a use case.
118
+
119
+ ```typescript lineNumbers
120
+ import { type RequestWithResponse } from "workflow"
121
+
122
+ async function sendAck(request: RequestWithResponse) {
123
+ "use step";
124
+ await request.respondWith(
125
+ new Response(JSON.stringify({ received: true }), {
126
+ headers: { "Content-Type": "application/json" }
127
+ })
128
+ );
129
+ }
130
+
131
+ export async function githubWebhookWorkflow(repoName: string) {
132
+ "use workflow";
133
+
134
+ // Use a deterministic token based on the repository
135
+ const webhook = createWebhook({ // [!code highlight]
136
+ token: `github_webhook:${repoName}`, // [!code highlight]
137
+ }); // [!code highlight]
138
+
139
+ console.log("Configure GitHub webhook:", webhook.url);
140
+
141
+ const request = await webhook;
142
+ const event = await request.json();
143
+
144
+ await sendAck(request);
145
+
146
+ await deployCommit(event);
147
+ }
148
+
149
+ async function deployCommit(event: any) {
150
+ "use step";
151
+ // Deploy logic here
152
+ }
153
+ ```
154
+
155
+ ### Waiting for Multiple Requests
156
+
157
+ You can also wait for multiple requests by using the `for await...of` syntax.
158
+
159
+ ```typescript lineNumbers
160
+ import { createWebhook, type RequestWithResponse } from "workflow"
161
+
162
+ async function sendSlackResponse(request: RequestWithResponse, message: string) {
163
+ "use step";
164
+ await request.respondWith(
165
+ new Response(
166
+ JSON.stringify({
167
+ response_type: "in_channel",
168
+ text: message
169
+ }),
170
+ { headers: { "Content-Type": "application/json" } }
171
+ )
172
+ );
173
+ }
174
+
175
+ async function sendStopResponse(request: RequestWithResponse) {
176
+ "use step";
177
+ await request.respondWith(
178
+ new Response("Stopping workflow...")
179
+ );
180
+ }
181
+
182
+ export async function slackCommandWorkflow(channelId: string) {
183
+ "use workflow";
184
+
185
+ const webhook = createWebhook({
186
+ token: `slack_command:${channelId}`,
187
+ });
188
+
189
+ for await (const request of webhook) { // [!code highlight]
190
+ const formData = await request.formData();
191
+ const command = formData.get("command");
192
+ const text = formData.get("text");
193
+
194
+ if (command === "/status") {
195
+ // Respond immediately to Slack
196
+ await sendSlackResponse(request, "Checking status...");
197
+
198
+ // Process the command
199
+ const status = await checkSystemStatus();
200
+ await postToSlack(channelId, `Status: ${status}`);
201
+ }
202
+
203
+ if (text === "stop") {
204
+ await sendStopResponse(request);
205
+ break;
206
+ }
207
+ }
208
+ }
209
+
210
+ async function checkSystemStatus() {
211
+ "use step";
212
+ return "All systems operational";
213
+ }
214
+
215
+ async function postToSlack(channelId: string, message: string) {
216
+ "use step";
217
+ // Post message to Slack
218
+ }
219
+ ```
220
+
221
+ ## Related Functions
222
+
223
+ - [`createHook()`](/docs/api-reference/workflow/create-hook) - Lower-level hook primitive for arbitrary payloads
224
+ - [`defineHook()`](/docs/api-reference/workflow/define-hook) - Type-safe hook helper
225
+ - [`resumeWebhook()`](/docs/api-reference/workflow-api/resume-webhook) - Resume a webhook from an API route
@@ -0,0 +1,206 @@
1
+ ---
2
+ title: defineHook
3
+ ---
4
+
5
+ Creates a type-safe hook helper that ensures the payload type is consistent between hook creation and resumption.
6
+
7
+ This is a lightweight wrapper around [`createHook()`](/docs/api-reference/workflow/create-hook) and [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook) to avoid type mismatches. It also supports optional runtime validation and transformation of payloads using any [Standard Schema v1](https://standardschema.dev) compliant validator like Zod or Valibot.
8
+
9
+ <Callout>
10
+ We recommend using `defineHook()` over `createHook()` in production codebases for better type safety and optional runtime validation.
11
+ </Callout>
12
+
13
+ ```ts lineNumbers
14
+ import { defineHook } from "workflow";
15
+
16
+ const nameHook = defineHook<{
17
+ name: string;
18
+ }>();
19
+
20
+ export async function nameWorkflow() {
21
+ "use workflow";
22
+
23
+ const hook = nameHook.create(); // [!code highlight]
24
+ const result = await hook; // Fully typed as { name: string }
25
+ console.log("Name:", result.name);
26
+ }
27
+ ```
28
+
29
+ ## API Signature
30
+
31
+ ### Parameters
32
+
33
+ <TSDoc
34
+ definition={`
35
+ import { defineHook } from "workflow";
36
+ export default defineHook;`}
37
+ showSections={['parameters']}
38
+ />
39
+
40
+ ### Returns
41
+
42
+ <TSDoc
43
+ definition={`
44
+ interface DefineHook<T> {
45
+ /**
46
+
47
+ * Creates a new hook with the defined payload type.
48
+ */
49
+ create: (options?: HookOptions) => Hook<T>;
50
+
51
+ /**
52
+
53
+ * Resumes a hook by sending a payload with the defined type.
54
+ */
55
+ resume: (token: string, payload: T) => Promise<HookEntity | null>;
56
+ }
57
+ export default DefineHook;`}
58
+ />
59
+
60
+ ## Examples
61
+
62
+ ### Basic Type-Safe Hook Definition
63
+
64
+ By defining the hook once with a specific payload type, you can reuse it in multiple workflows and API routes with automatic type safety.
65
+
66
+ ```typescript lineNumbers
67
+ import { defineHook } from "workflow";
68
+
69
+ // Define once with a specific payload type
70
+ const approvalHook = defineHook<{ // [!code highlight]
71
+ approved: boolean; // [!code highlight]
72
+ comment: string; // [!code highlight]
73
+ }>(); // [!code highlight]
74
+
75
+ // In your workflow
76
+ export async function workflowWithApproval() {
77
+ "use workflow";
78
+
79
+ const hook = approvalHook.create();
80
+ const result = await hook; // Fully typed as { approved: boolean; comment: string }
81
+
82
+ console.log("Approved:", result.approved);
83
+ console.log("Comment:", result.comment);
84
+ }
85
+ ```
86
+
87
+ ### Resuming with Type Safety
88
+
89
+ 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.
90
+
91
+ ```typescript lineNumbers
92
+ // Use the same defined hook to resume
93
+ export async function POST(request: Request) {
94
+ const { token, approved, comment } = await request.json();
95
+
96
+ // Type-safe resumption - TypeScript ensures the payload matches
97
+ const result = await approvalHook.resume(token, { // [!code highlight]
98
+ approved, // [!code highlight]
99
+ comment, // [!code highlight]
100
+ }); // [!code highlight]
101
+
102
+ if (!result) {
103
+ return Response.json({ error: "Hook not found" }, { status: 404 });
104
+ }
105
+
106
+ return Response.json({ success: true, runId: result.runId });
107
+ }
108
+ ```
109
+
110
+ ### Validate and Transform with Schema
111
+
112
+ 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.
113
+
114
+ <Callout type="info">
115
+ Standard Schema is a standardized specification for schema validation libraries. Most popular validation libraries support it, including Zod, Valibot, ArkType, and Effect Schema. You can also write custom validators.
116
+ </Callout>
117
+
118
+ #### Using Zod with defineHook
119
+
120
+ Here's an example using [Zod](https://zod.dev) to validate and transform hook payloads:
121
+
122
+ ```typescript lineNumbers
123
+ import { defineHook } from "workflow";
124
+ import { z } from "zod";
125
+
126
+ export const approvalHook = defineHook({
127
+ schema: z.object({ // [!code highlight]
128
+ approved: z.boolean(), // [!code highlight]
129
+ comment: z.string().min(1).transform((value) => value.trim()), // [!code highlight]
130
+ }), // [!code highlight]
131
+ });
132
+
133
+ export async function approvalWorkflow(approvalId: string) {
134
+ "use workflow";
135
+
136
+ const hook = approvalHook.create({
137
+ token: `approval:${approvalId}`,
138
+ });
139
+
140
+ // Payload is automatically typed based on the schema
141
+ const { approved, comment } = await hook;
142
+ console.log("Approved:", approved);
143
+ console.log("Comment (trimmed):", comment);
144
+ }
145
+ ```
146
+
147
+ When resuming the hook from an API route, the schema validates and transforms the incoming payload before the workflow resumes:
148
+
149
+ ```typescript lineNumbers
150
+ export async function POST(request: Request) {
151
+ // Incoming payload: { token: "...", approved: true, comment: " Ready! " }
152
+ const { token, approved, comment } = await request.json();
153
+
154
+ // The schema validates and transforms the payload:
155
+ // - Checks that `approved` is a boolean
156
+ // - Checks that `comment` is a non-empty string
157
+ // - Trims whitespace from the comment
158
+ // If validation fails, an error is thrown and the hook is not resumed
159
+ await approvalHook.resume(token, { // [!code highlight]
160
+ approved, // [!code highlight]
161
+ comment, // Automatically trimmed to "Ready!" // [!code highlight]
162
+ }); // [!code highlight]
163
+
164
+ return Response.json({ success: true });
165
+ }
166
+ ```
167
+
168
+ #### Using Other Standard Schema Libraries
169
+
170
+ The same pattern works with any Standard Schema v1 compliant library. Here's an example with [Valibot](https://valibot.dev):
171
+
172
+ ```typescript lineNumbers
173
+ import { defineHook } from "workflow";
174
+ import * as v from "valibot";
175
+
176
+ export const approvalHook = defineHook({
177
+ schema: v.object({ // [!code highlight]
178
+ approved: v.boolean(), // [!code highlight]
179
+ comment: v.pipe(v.string(), v.minLength(1), v.trim()), // [!code highlight]
180
+ }), // [!code highlight]
181
+ });
182
+ ```
183
+
184
+ ### Customizing Tokens
185
+
186
+ 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.
187
+
188
+ ```typescript lineNumbers
189
+ const slackHook = defineHook<{ text: string; userId: string }>();
190
+
191
+ export async function slackBotWorkflow(channelId: string) {
192
+ "use workflow";
193
+
194
+ const hook = slackHook.create({
195
+ token: `slack:${channelId}`, // [!code highlight]
196
+ });
197
+
198
+ const message = await hook;
199
+ console.log(`Message from ${message.userId}: ${message.text}`);
200
+ }
201
+ ```
202
+
203
+ ## Related Functions
204
+
205
+ * [`createHook()`](/docs/api-reference/workflow/create-hook) - Create a hook in a workflow.
206
+ * [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook) - Resume a hook with a payload.
@@ -0,0 +1,37 @@
1
+ ---
2
+ title: FatalError
3
+ ---
4
+
5
+ When a `FatalError` is thrown in a step, it indicates that the workflow should not retry a step, marking it as failure.
6
+
7
+ You should use this when you don't want a specific step to retry.
8
+
9
+ ```typescript lineNumbers
10
+ import { FatalError } from "workflow"
11
+
12
+ async function fallibleWorkflow() {
13
+ "use workflow"
14
+ await fallibleStep();
15
+ }
16
+
17
+ async function fallibleStep() {
18
+ "use step"
19
+ throw new FatalError("Fallible!") // [!code highlight]
20
+ }
21
+ ```
22
+
23
+ ## API Signature
24
+
25
+ ### Parameters
26
+
27
+ <TSDoc
28
+ definition={`
29
+ interface Error {
30
+ /**
31
+
32
+ * The error message.
33
+ */
34
+ message: string;
35
+ }
36
+ export default Error;`}
37
+ />
@@ -0,0 +1,139 @@
1
+ ---
2
+ title: fetch
3
+ ---
4
+
5
+ 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.
6
+
7
+ This is useful when you need to call external APIs or services from within your workflow.
8
+
9
+ <Callout>
10
+ `fetch` is a *special* type of step function provided and should be called directly inside workflow functions.
11
+ </Callout>
12
+
13
+ ```typescript lineNumbers
14
+ import { fetch } from "workflow"
15
+
16
+ async function apiWorkflow() {
17
+ "use workflow"
18
+
19
+ // Fetch data from an API
20
+ const response = await fetch("https://api.example.com/data") // [!code highlight]
21
+ return await response.json()
22
+ }
23
+ ```
24
+
25
+ ## API Signature
26
+
27
+ ### Parameters
28
+
29
+ Accepts the same arguments as web [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch)
30
+
31
+ <TSDoc
32
+ definition={`
33
+ import { fetch } from "workflow";
34
+ export default fetch;`}
35
+ showSections={['parameters']}
36
+ />
37
+
38
+ ### Returns
39
+
40
+ Returns the same response as web [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch)
41
+
42
+ <TSDoc
43
+ definition={`
44
+ import { fetch } from "workflow";
45
+ export default fetch;`}
46
+ showSections={['returns']}
47
+ />
48
+
49
+ ## Examples
50
+
51
+ ### Basic Usage
52
+
53
+ Here's a simple example of how you can use `fetch` inside your workflow.
54
+
55
+ ```typescript lineNumbers
56
+ import { fetch } from "workflow"
57
+
58
+ async function apiWorkflow() {
59
+ "use workflow"
60
+
61
+ // Fetch data from an API
62
+ const response = await fetch("https://api.example.com/data") // [!code highlight]
63
+ const data = await response.json()
64
+
65
+ // Make a POST request
66
+ const postResponse = await fetch("https://api.example.com/create", { // [!code highlight]
67
+ method: "POST",
68
+ headers: {
69
+ "Content-Type": "application/json"
70
+ },
71
+ body: JSON.stringify({ name: "test" })
72
+ })
73
+
74
+ return data
75
+ }
76
+ ```
77
+
78
+ We call `fetch()` with a URL and optional request options, just like the standard fetch API. The workflow runtime automatically handles the response serialization.
79
+
80
+ This API is provided as a convenience to easily use `fetch` in workflow, but often, you might want to extend and implement your own fetch for more powerful error handing and retry logic.
81
+
82
+ ### Customizing Fetch Behavior
83
+
84
+ Here's an example of a custom fetch wrapper that provides more sophisticated error handling with custom retry logic:
85
+
86
+ ```typescript lineNumbers
87
+ import { FatalError, RetryableError } from "workflow"
88
+
89
+ export async function customFetch(
90
+ url: string,
91
+ init?: RequestInit
92
+ ) {
93
+ "use step"
94
+
95
+ const response = await fetch(url, init)
96
+
97
+ // Handle client errors (4xx) - don't retry
98
+ if (response.status >= 400 && response.status < 500) {
99
+ if (response.status === 429) {
100
+ // Rate limited - retry with backoff from Retry-After header
101
+ const retryAfter = response.headers.get("Retry-After")
102
+
103
+ if (retryAfter) {
104
+ // The Retry-After header is either a number (seconds) or an RFC 7231 date string
105
+ const retryAfterValue = /^\d+$/.test(retryAfter)
106
+ ? parseInt(retryAfter) * 1000 // Convert seconds to milliseconds
107
+ : new Date(retryAfter); // Parse RFC 7231 date format
108
+
109
+ // Use `RetryableError` to customize the retry
110
+ throw new RetryableError( // [!code highlight]
111
+ `Rate limited by ${url}`, // [!code highlight]
112
+ { retryAfter: retryAfterValue } // [!code highlight]
113
+ ) // [!code highlight]
114
+ }
115
+ }
116
+
117
+ // Other client errors are fatal (400, 401, 403, 404, etc.)
118
+ throw new FatalError( // [!code highlight]
119
+ `Client error ${response.status}: ${response.statusText}` // [!code highlight]
120
+ ) // [!code highlight]
121
+ }
122
+
123
+ // Handle server errors (5xx) - will retry automatically
124
+ if (!response.ok) {
125
+ throw new Error(
126
+ `Server error ${response.status}: ${response.statusText}`
127
+ )
128
+ }
129
+
130
+ return response
131
+ }
132
+ ```
133
+
134
+ This example demonstrates:
135
+
136
+ - Setting custom `maxRetries` to 5 retries (6 total attempts including the initial attempt).
137
+ - Throwing [`FatalError`](/docs/api-reference/workflow/fatal-error) for client errors (400-499) to prevent retries.
138
+ - Handling 429 rate limiting by reading the `Retry-After` header and using [`RetryableError`](/docs/api-reference/workflow/retryable-error).
139
+ - Allowing automatic retries for server errors (5xx).
@@ -0,0 +1,76 @@
1
+ ---
2
+ title: getStepMetadata
3
+ ---
4
+
5
+ Returns metadata available in the current step function.
6
+
7
+ You may want to use this function when you need to:
8
+
9
+ - Track retry attempts in error handling
10
+ - Access timing information of a step and execution metadata
11
+ - Generate idempotency keys for external APIs
12
+
13
+ <Callout type="warn">
14
+ This function can only be called inside a step function.
15
+ </Callout>
16
+
17
+ ```typescript lineNumbers
18
+ import { getStepMetadata } from "workflow";
19
+
20
+ async function testWorkflow() {
21
+ "use workflow";
22
+ await logStepId();
23
+ }
24
+
25
+ async function logStepId() {
26
+ "use step";
27
+ const ctx = getStepMetadata(); // [!code highlight]
28
+ console.log(ctx.stepId); // Grab the current step ID
29
+ }
30
+ ```
31
+
32
+ ### Example: Use `stepId` as an idempotency key
33
+
34
+ ```typescript lineNumbers
35
+ import { getStepMetadata } from "workflow";
36
+
37
+ async function chargeUser(userId: string, amount: number) {
38
+ "use step";
39
+ const { stepId } = getStepMetadata();
40
+
41
+ await stripe.charges.create(
42
+ {
43
+ amount,
44
+ currency: "usd",
45
+ customer: userId,
46
+ },
47
+ {
48
+ idempotencyKey: `charge:${stepId}`, // [!code highlight]
49
+ }
50
+ );
51
+ }
52
+ ```
53
+
54
+ <Callout type="info">
55
+ Learn more about patterns and caveats in the{" "}
56
+ <a href="/docs/foundations/idempotency">Idempotency</a> guide.
57
+ </Callout>
58
+
59
+ ## API Signature
60
+
61
+ ### Parameters
62
+
63
+ <TSDoc
64
+ definition={`
65
+ import { getStepMetadata } from "workflow";
66
+ export default getStepMetadata;`}
67
+ showSections={["parameters"]}
68
+ />
69
+
70
+ ### Returns
71
+
72
+ <TSDoc
73
+ definition={`
74
+ import type { StepMetadata } from "workflow";
75
+ export default StepMetadata;`}
76
+ />